diff --git a/delivery_hibou/__init__.py b/delivery_hibou/__init__.py
new file mode 100644
index 00000000..0650744f
--- /dev/null
+++ b/delivery_hibou/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/delivery_hibou/__manifest__.py b/delivery_hibou/__manifest__.py
new file mode 100644
index 00000000..627c0f3e
--- /dev/null
+++ b/delivery_hibou/__manifest__.py
@@ -0,0 +1,24 @@
+{
+ 'name': 'Delivery Hibou',
+ 'summary': 'Adds underlying pinnings for things like "RMA Return Labels"',
+ 'version': '15.0.1.0.0',
+ 'author': "Hibou Corp.",
+ 'category': 'Stock',
+ 'license': 'AGPL-3',
+ 'images': [],
+ 'website': "https://hibou.io",
+ 'description': """
+This is a collection of "typical" carrier needs, and a bridge into Hibou modules like `delivery_partner` and `sale_planner`.
+""",
+ 'depends': [
+ 'delivery',
+ 'delivery_partner',
+ ],
+ 'demo': [],
+ 'data': [
+ 'views/delivery_views.xml',
+ 'views/stock_views.xml',
+ ],
+ 'auto_install': False,
+ 'installable': True,
+}
diff --git a/delivery_hibou/models/__init__.py b/delivery_hibou/models/__init__.py
new file mode 100644
index 00000000..29ce1386
--- /dev/null
+++ b/delivery_hibou/models/__init__.py
@@ -0,0 +1,2 @@
+from . import delivery
+from . import stock
diff --git a/delivery_hibou/models/delivery.py b/delivery_hibou/models/delivery.py
new file mode 100644
index 00000000..d281fb5f
--- /dev/null
+++ b/delivery_hibou/models/delivery.py
@@ -0,0 +1,245 @@
+from odoo import fields, models
+from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
+from odoo.exceptions import UserError
+
+
+class DeliveryCarrier(models.Model):
+ _inherit = 'delivery.carrier'
+
+ automatic_insurance_value = fields.Float(string='Automatic Insurance Value',
+ help='Will be used during shipping to determine if the '
+ 'picking\'s value warrants insurance being added.')
+ procurement_priority = fields.Selection(PROCUREMENT_PRIORITIES,
+ string='Procurement Priority',
+ help='Priority for this carrier. Will affect pickings '
+ 'and procurements related to this carrier.')
+
+ # Utility
+
+ def get_insurance_value(self, order=None, picking=None):
+ value = 0.0
+ if order:
+ if order.order_line:
+ value = sum(order.order_line.filtered(lambda l: l.type != 'service').mapped('price_subtotal'))
+ else:
+ return value
+ if picking:
+ value = picking.declared_value()
+ if picking.require_insurance == 'no':
+ value = 0.0
+ elif picking.require_insurance == 'auto' and self.automatic_insurance_value and self.automatic_insurance_value > value:
+ value = 0.0
+ return value
+
+ def get_third_party_account(self, order=None, picking=None):
+ if order and order.shipping_account_id:
+ return order.shipping_account_id
+ if picking and picking.shipping_account_id:
+ return picking.shipping_account_id
+ return None
+
+ def get_order_name(self, order=None, picking=None):
+ if order:
+ return order.name
+ if picking:
+ if picking.sale_id:
+ return picking.sale_id.name # + ' - ' + picking.name
+ return picking.name
+ return ''
+
+ def get_attn(self, order=None, picking=None):
+ if order:
+ return order.client_order_ref
+ if picking and picking.sale_id:
+ return picking.sale_id.client_order_ref
+ # If Picking has a reference, decide what it is.
+ return False
+
+ def _classify_picking(self, picking):
+ if picking.picking_type_id.code == 'incoming' and picking.location_id.usage == 'supplier' and picking.location_dest_id.usage == 'customer':
+ return 'dropship'
+ elif picking.picking_type_id.code == 'incoming' and picking.location_id.usage == 'customer' and picking.location_dest_id.usage == 'supplier':
+ return 'dropship_in'
+ elif picking.picking_type_id.code == 'incoming':
+ return 'in'
+ return 'out'
+
+ def is_amazon(self, order=None, picking=None):
+ """
+ Amazon MWS orders potentially need to be flagged for
+ clean up on the carrier's side.
+
+ Override to return based on criteria in your company.
+ :return:
+ """
+ return False
+
+ # Shipper Company
+
+ def get_shipper_company(self, order=None, picking=None):
+ """
+ Shipper Company: The `res.partner` that provides the name of where the shipment is coming from.
+ """
+ if order:
+ return order.company_id.partner_id
+ if picking:
+ return getattr(self, ('_get_shipper_company_%s' % (self._classify_picking(picking),)),
+ self._get_shipper_company_out)(picking)
+ return None
+
+ def _get_shipper_company_dropship(self, picking):
+ return picking.company_id.partner_id
+
+ def _get_shipper_company_dropship_in(self, picking):
+ return picking.company_id.partner_id
+
+ def _get_shipper_company_in(self, picking):
+ return picking.company_id.partner_id
+
+ def _get_shipper_company_out(self, picking):
+ return picking.company_id.partner_id
+
+ # Shipper Warehouse
+
+ def get_shipper_warehouse(self, order=None, picking=None):
+ """
+ Shipper Warehouse: The `res.partner` that is basically the physical address a shipment is coming from.
+ """
+ if order:
+ return order.warehouse_id.partner_id
+ if picking:
+ return getattr(self, ('_get_shipper_warehouse_%s' % (self._classify_picking(picking),)),
+ self._get_shipper_warehouse_out)(picking)
+ return None
+
+ def _get_shipper_warehouse_dropship(self, picking):
+ return picking.partner_id
+
+ def _get_shipper_warehouse_dropship_in(self, picking):
+ if picking.sale_id:
+ picking.sale_id.partner_shipping_id
+ return self._get_shipper_warehouse_dropship_in_no_sale(picking)
+
+ def _get_shipper_warehouse_dropship_in_no_sale(self, picking):
+ return picking.company_id.partner_id
+
+ def _get_shipper_warehouse_in(self, picking):
+ return picking.partner_id
+
+ def _get_shipper_warehouse_out(self, picking):
+ return picking.picking_type_id.warehouse_id.partner_id
+
+ # Recipient
+
+ def get_recipient(self, order=None, picking=None):
+ """
+ Recipient: The `res.partner` receiving the shipment.
+ """
+ if order:
+ return order.partner_shipping_id
+ if picking:
+ return getattr(self, ('_get_recipient_%s' % (self._classify_picking(picking),)),
+ self._get_recipient_out)(picking)
+ return None
+
+ def _get_recipient_dropship(self, picking):
+ if picking.sale_id:
+ return picking.sale_id.partner_shipping_id
+ return picking.partner_id
+
+ def _get_recipient_dropship_no_sale(self, picking):
+ return picking.company_id.partner_id
+
+ def _get_recipient_dropship_in(self, picking):
+ return picking.picking_type_id.warehouse_id.partner_id
+
+ def _get_recipient_in(self, picking):
+ return picking.picking_type_id.warehouse_id.partner_id
+
+ def _get_recipient_out(self, picking):
+ return picking.partner_id
+
+ # -------------------------- #
+ # API for external providers #
+ # -------------------------- #
+ def rate_shipment_multi(self, order=None, picking=None, packages=None):
+ ''' Compute the price of the order shipment
+
+ :param order: record of sale.order or None
+ :param picking: record of stock.picking or None
+ :param packages: recordset of stock.quant.package or None (requires picking also set)
+ :return list: dict: {
+ 'carrier': delivery.carrier(),
+ 'success': boolean,
+ 'price': a float,
+ 'error_message': a string containing an error message,
+ 'warning_message': a string containing a warning message,
+ 'date_planned': a datetime for when the shipment is supposed to leave,
+ 'date_delivered': a datetime for when the shipment is supposed to arrive,
+ 'transit_days': a Float for how many days it takes in transit,
+ 'service_code': a string that represents the service level/agreement,
+ 'package': stock.quant.package(),
+ }
+
+ e.g. self == delivery.carrier(5, 6)
+ then return might be:
+ [
+ {'carrier': delivery.carrier(5), 'price': 10.50, 'service_code': 'GROUND_HOME_DELIVERY', ...},
+ {'carrier': delivery.carrier(7), 'price': 12.99, 'service_code': 'FEDEX_EXPRESS_SAVER', ...}, # NEW!
+ {'carrier': delivery.carrier(6), 'price': 8.0, 'service_code': 'USPS_PRI', ...},
+ ]
+ '''
+ self.ensure_one()
+
+ if picking:
+ self = self.with_context(date_planned=fields.Datetime.now())
+ if not packages:
+ packages = picking.package_ids
+ else:
+ if packages:
+ raise UserError('Cannot rate package without picking.')
+ self = self.with_context(date_planned=(order.date_planned or fields.Datetime.now()))
+
+ res = []
+ for carrier in self:
+ carrier_packages = packages.filtered(lambda p: not p.carrier_tracking_ref and
+ (not p.carrier_id or p.carrier_id == carrier) and
+ p.packaging_id.package_carrier_type in (False, '', 'none', carrier.delivery_type))
+ if packages and not carrier_packages:
+ continue
+ if hasattr(carrier, '%s_rate_shipment_multi' % self.delivery_type):
+ try:
+ res += getattr(carrier, '%s_rate_shipment_multi' % carrier.delivery_type)(order=order,
+ picking=picking,
+ packages=carrier_packages)
+ except TypeError:
+ # TODO remove catch if after Odoo 14
+ # This is intended to find ones that don't support packages= kwarg
+ res += getattr(carrier, '%s_rate_shipment_multi' % carrier.delivery_type)(order=order,
+ picking=picking)
+
+ return res
+
+ def cancel_shipment(self, pickings, packages=None):
+ ''' Cancel a shipment
+
+ :param pickings: A recordset of pickings
+ :param packages: Optional recordset of packages (should be for this carrier)
+ '''
+ self.ensure_one()
+ if hasattr(self, '%s_cancel_shipment' % self.delivery_type):
+ # No good way to tell if this method takes the kwarg for packages
+ if packages:
+ try:
+ return getattr(self, '%s_cancel_shipment' % self.delivery_type)(pickings, packages=packages)
+ except TypeError:
+ # we won't be able to cancel the packages properly
+ # here we will TRY to make a good call here where we put the package references into the picking
+ # and let the original mechanisms try to work here
+ tracking_ref = ','.join(packages.mapped('carrier_tracking_ref'))
+ pickings.write({
+ 'carrier_id': self.id,
+ 'carrier_tracking_ref': tracking_ref,
+ })
+
+ return getattr(self, '%s_cancel_shipment' % self.delivery_type)(pickings)
diff --git a/delivery_hibou/models/stock.py b/delivery_hibou/models/stock.py
new file mode 100644
index 00000000..592ac96c
--- /dev/null
+++ b/delivery_hibou/models/stock.py
@@ -0,0 +1,161 @@
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
+
+
+class StockQuantPackage(models.Model):
+ _inherit = 'stock.quant.package'
+
+ carrier_id = fields.Many2one('delivery.carrier', string='Carrier')
+ carrier_tracking_ref = fields.Char(string='Tracking Reference')
+
+ def _get_active_picking(self):
+ picking_id = self._context.get('active_id')
+ picking_model = self._context.get('active_model')
+ if not picking_id or picking_model != 'stock.picking':
+ raise UserError('Cannot cancel package other than through shipment/picking.')
+ return self.env['stock.picking'].browse(picking_id)
+
+ def send_to_shipper(self):
+ picking = self._get_active_picking()
+ picking.with_context(packages=self).send_to_shipper()
+
+ def cancel_shipment(self):
+ picking = self._get_active_picking()
+ picking.with_context(packages=self).cancel_shipment()
+
+
+class StockPicking(models.Model):
+ _inherit = 'stock.picking'
+
+ shipping_account_id = fields.Many2one('partner.shipping.account', string='Shipping Account')
+ require_insurance = fields.Selection([
+ ('auto', 'Automatic'),
+ ('yes', 'Yes'),
+ ('no', 'No'),
+ ], string='Require Insurance', default='auto',
+ help='If your carrier supports it, auto should be calculated off of the "Automatic Insurance Value" field.')
+ package_carrier_tracking_ref = fields.Char(string='Package Tracking Numbers', compute='_compute_package_carrier_tracking_ref')
+
+ @api.depends('package_ids.carrier_tracking_ref')
+ def _compute_package_carrier_tracking_ref(self):
+ for picking in self:
+ package_refs = picking.package_ids.filtered('carrier_tracking_ref').mapped('carrier_tracking_ref')
+ if package_refs:
+ picking.package_carrier_tracking_ref = ','.join(package_refs)
+ else:
+ picking.package_carrier_tracking_ref = False
+
+ @api.onchange('carrier_id')
+ def _onchange_carrier_id_for_priority(self):
+ for picking in self:
+ if picking.carrier_id and picking.carrier_id.procurement_priority:
+ picking.priority = picking.carrier_id.procurement_priority
+
+ @api.model
+ def create(self, values):
+ origin = values.get('origin')
+ if origin and not values.get('shipping_account_id'):
+ so = self.env['sale.order'].search([('name', '=', str(origin))], limit=1)
+ if so and so.shipping_account_id:
+ values['shipping_account_id'] = so.shipping_account_id.id
+ carrier_id = values.get('carrier_id')
+ if carrier_id:
+ carrier = self.env['delivery.carrier'].browse(carrier_id)
+ if carrier.procurement_priority:
+ values['priority'] = carrier.procurement_priority
+
+ res = super(StockPicking, self).create(values)
+ return res
+
+ def declared_value(self):
+ self.ensure_one()
+ cost = sum([(l.product_id.standard_price * l.qty_done) for l in self.move_line_ids] or [0.0])
+ if not cost:
+ # Assume Full Value
+ cost = sum([(l.product_id.standard_price * l.product_uom_qty) for l in self.move_lines] or [0.0])
+ return cost
+
+ def clear_carrier_tracking_ref(self):
+ self.write({'carrier_tracking_ref': False})
+
+ def reset_carrier_tracking_ref(self):
+ for picking in self:
+ picking.carrier_tracking_ref = picking.package_carrier_tracking_ref
+
+ # Override to send to specific packaging carriers
+ def send_to_shipper(self):
+ packages = self._context.get('packages')
+ self.ensure_one()
+ if not packages:
+ packages = self.package_ids
+ package_carriers = packages.mapped('carrier_id')
+ if not package_carriers:
+ # Original behavior
+ return super().send_to_shipper()
+
+ tracking_numbers = []
+ carrier_prices = []
+ order_currency = self.sale_id.currency_id or self.company_id.currency_id
+ for carrier in package_carriers:
+ self.carrier_id = carrier
+ carrier_packages = packages.filtered(lambda p: p.carrier_id == carrier)
+ res = carrier.send_shipping(self)
+ if res:
+ res = res[0]
+ if carrier.free_over and self.sale_id and self.sale_id._compute_amount_total_without_delivery() >= carrier.amount:
+ res['exact_price'] = 0.0
+ carrier_price = res['exact_price'] * (1.0 + (self.carrier_id.margin / 100.0))
+ carrier_prices.append(carrier_price)
+ tracking_number = ''
+ if res['tracking_number']:
+ tracking_number = res['tracking_number']
+ tracking_numbers.append(tracking_number)
+ # Try to add tracking to the individual packages.
+ potential_tracking_numbers = tracking_number.split(',')
+ if len(potential_tracking_numbers) >= len(carrier_packages):
+ for t, p in zip(potential_tracking_numbers, carrier_packages):
+ p.carrier_tracking_ref = t
+ else:
+ carrier_packages.write({'carrier_tracking_ref': tracking_number})
+ msg = _(
+ "Shipment sent to carrier %(carrier_name)s for shipping with tracking number %(ref)s
Cost: %(price).2f %(currency)s",
+ carrier_name=carrier.name,
+ ref=tracking_number,
+ price=carrier_price,
+ currency=order_currency.name
+ )
+ self.message_post(body=msg)
+
+ self.carrier_price = sum(carrier_prices or [0.0])
+ self.carrier_tracking_ref = ','.join(tracking_numbers or [''])
+ self._add_delivery_cost_to_so()
+
+ # Override to provide per-package versions...
+ def cancel_shipment(self):
+ packages = self._context.get('packages')
+ pickings_with_package_tracking = self.filtered(lambda p: p.package_carrier_tracking_ref)
+ for picking in pickings_with_package_tracking:
+ if packages:
+ current_packages = packages
+ else:
+ current_packages = picking.package_ids
+ # Packages without a carrier can just be cleared
+ packages_without_carrier = current_packages.filtered(lambda p: not p.carrier_id and p.carrier_tracking_ref)
+ packages_without_carrier.write({
+ 'carrier_tracking_ref': False,
+ })
+ # Packages with carrier can use the carrier method
+ packages_with_carrier = current_packages.filtered(lambda p: p.carrier_id and p.carrier_tracking_ref)
+ carriers = packages_with_carrier.mapped('carrier_id')
+ for carrier in carriers:
+ carrier_packages = packages_with_carrier.filtered(lambda p: p.carrier_id == carrier)
+ carrier.cancel_shipment(self, packages=carrier_packages)
+ package_refs = ','.join(carrier_packages.mapped('carrier_tracking_ref'))
+ msg = "Shipment %s cancelled" % package_refs
+ picking.message_post(body=msg)
+ carrier_packages.write({'carrier_tracking_ref': False})
+
+ pickings_without_package_tracking = self - pickings_with_package_tracking
+ if pickings_without_package_tracking:
+ # use original on these
+ super(StockPicking, pickings_without_package_tracking).cancel_shipment()
diff --git a/delivery_hibou/tests/__init__.py b/delivery_hibou/tests/__init__.py
new file mode 100644
index 00000000..d4e6373e
--- /dev/null
+++ b/delivery_hibou/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_delivery_hibou
diff --git a/delivery_hibou/tests/test_delivery_hibou.py b/delivery_hibou/tests/test_delivery_hibou.py
new file mode 100644
index 00000000..1dd9aa99
--- /dev/null
+++ b/delivery_hibou/tests/test_delivery_hibou.py
@@ -0,0 +1,150 @@
+from odoo.tests import common
+
+
+class TestDeliveryHibou(common.TransactionCase):
+
+ def setUp(self):
+ super(TestDeliveryHibou, self).setUp()
+ self.partner = self.env.ref('base.res_partner_address_13')
+ self.product = self.env.ref('product.product_product_7')
+ # Create Shipping Account
+ self.shipping_account = self.env['partner.shipping.account'].create({
+ 'name': '123123',
+ 'delivery_type': 'other',
+ })
+ # Create Carrier
+ self.delivery_product = self.env['product.product'].create({
+ 'name': 'Test Carrier1 Delivery',
+ 'type': 'service',
+ })
+ self.carrier = self.env['delivery.carrier'].create({
+ 'name': 'Test Carrier1',
+ 'product_id': self.delivery_product.id,
+ })
+
+ def test_delivery_hibou(self):
+ # Assign a new shipping account
+ self.partner.shipping_account_ids = self.shipping_account
+
+ # Assign values to new Carrier
+ test_insurance_value = 600
+ test_procurement_priority = '1'
+ self.carrier.automatic_insurance_value = test_insurance_value
+ self.carrier.procurement_priority = test_procurement_priority
+
+
+ sale_order = self.env['sale.order'].create({
+ 'partner_id': self.partner.id,
+ 'partner_shipping_id': self.partner.id,
+ 'partner_invoice_id': self.partner.id,
+ 'shipping_account_id': self.shipping_account.id,
+ 'order_line': [(0, 0, {
+ 'product_id': self.product.id,
+ })]
+ })
+ self.assertFalse(sale_order.carrier_id)
+ action = sale_order.action_open_delivery_wizard()
+ form = common.Form(self.env[action['res_model']].with_context(**action.get('context', {})))
+ form.carrier_id = self.carrier
+ wizard = form.save()
+ wizard.button_confirm()
+
+ #sale_order.set_delivery_line()
+ self.assertEqual(sale_order.carrier_id, self.carrier)
+ sale_order.action_confirm()
+ # Make sure 3rd party Shipping Account is set.
+ self.assertEqual(sale_order.shipping_account_id, self.shipping_account)
+
+ self.assertTrue(sale_order.picking_ids)
+ # Priority coming from Carrier procurement_priority
+ self.assertEqual(sale_order.picking_ids.priority, test_procurement_priority)
+ # 3rd party Shipping Account copied from Sale Order
+ self.assertEqual(sale_order.picking_ids.shipping_account_id, self.shipping_account)
+ self.assertEqual(sale_order.carrier_id.get_third_party_account(order=sale_order), self.shipping_account)
+
+ # Test attn
+ test_ref = 'TEST100'
+ self.assertEqual(sale_order.carrier_id.get_attn(order=sale_order), False)
+ sale_order.client_order_ref = test_ref
+ self.assertEqual(sale_order.carrier_id.get_attn(order=sale_order), test_ref)
+ # The picking should get this ref as well
+ self.assertEqual(sale_order.picking_ids.carrier_id.get_attn(picking=sale_order.picking_ids), test_ref)
+
+ # Test order_name
+ self.assertEqual(sale_order.carrier_id.get_order_name(order=sale_order), sale_order.name)
+ # The picking should get the same 'order_name'
+ self.assertEqual(sale_order.picking_ids.carrier_id.get_order_name(picking=sale_order.picking_ids), sale_order.name)
+
+ def test_carrier_hibou_out(self):
+ test_insurance_value = 1000
+ self.carrier.automatic_insurance_value = test_insurance_value
+
+ picking_out = self.env.ref('stock.outgoing_shipment_main_warehouse')
+ picking_out.action_assign()
+ self.assertEqual(picking_out.state, 'assigned')
+
+ picking_out.carrier_id = self.carrier
+
+ # This relies heavily on the 'stock' demo data.
+ # Should only have a single move_line_ids and it should not be done at all.
+ self.assertEqual(picking_out.move_line_ids.mapped('qty_done'), [0.0])
+ self.assertEqual(picking_out.move_line_ids.mapped('product_uom_qty'), [15.0])
+ self.assertEqual(picking_out.move_line_ids.mapped('product_id.standard_price'), [100.0])
+ test_whole_value = 15.0 * 100.0
+ test_one_value = 100.0
+
+ # The 'value' is assumed to be all of the product value from the initial demand.
+ self.assertEqual(picking_out.declared_value(), test_whole_value)
+ self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), picking_out.declared_value())
+
+ # Workflow where user explicitly opts out of insurance on the picking level.
+ picking_out.require_insurance = 'no'
+ self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 0.0)
+ picking_out.require_insurance = 'auto'
+
+ # Lets choose to only delivery one piece at the moment.
+ # This does not meet the minimum on the carrier to have insurance value.
+ picking_out.move_line_ids.qty_done = 1.0
+ self.assertEqual(picking_out.declared_value(), 100.0)
+ self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 0.0)
+ # Workflow where user opts in to insurance.
+ picking_out.require_insurance = 'yes'
+ self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), test_one_value)
+ picking_out.require_insurance = 'auto'
+
+ # Test with picking having 3rd party account.
+ self.assertEqual(picking_out.carrier_id.get_third_party_account(picking=picking_out), None)
+ picking_out.shipping_account_id = self.shipping_account
+ self.assertEqual(picking_out.carrier_id.get_third_party_account(picking=picking_out), self.shipping_account)
+
+ # Shipment Time Methods!
+ self.assertEqual(picking_out.carrier_id._classify_picking(picking=picking_out), 'out')
+ self.assertEqual(picking_out.carrier_id.get_shipper_company(picking=picking_out),
+ picking_out.company_id.partner_id)
+ self.assertEqual(picking_out.carrier_id.get_shipper_warehouse(picking=picking_out),
+ picking_out.picking_type_id.warehouse_id.partner_id)
+ self.assertEqual(picking_out.carrier_id.get_recipient(picking=picking_out),
+ picking_out.partner_id)
+ # This picking has no `sale_id`
+ # Right now ATTN requires a sale_id, which this picking doesn't have (none of the stock ones do)
+ self.assertEqual(picking_out.carrier_id.get_attn(picking=picking_out), False)
+ self.assertEqual(picking_out.carrier_id.get_order_name(picking=picking_out), picking_out.name)
+
+ def test_carrier_hibou_in(self):
+ picking_in = self.env.ref('stock.incomming_shipment1')
+ self.assertEqual(picking_in.state, 'assigned')
+
+ picking_in.carrier_id = self.carrier
+ # This relies heavily on the 'stock' demo data.
+ # Should only have a single move_line_ids and it should not be done at all.
+ self.assertEqual(picking_in.move_line_ids.mapped('qty_done'), [0.0])
+ self.assertEqual(picking_in.move_line_ids.mapped('product_uom_qty'), [35.0])
+ self.assertEqual(picking_in.move_line_ids.mapped('product_id.standard_price'), [55.0])
+
+ self.assertEqual(picking_in.carrier_id._classify_picking(picking=picking_in), 'in')
+ self.assertEqual(picking_in.carrier_id.get_shipper_company(picking=picking_in),
+ picking_in.company_id.partner_id)
+ self.assertEqual(picking_in.carrier_id.get_shipper_warehouse(picking=picking_in),
+ picking_in.partner_id)
+ self.assertEqual(picking_in.carrier_id.get_recipient(picking=picking_in),
+ picking_in.picking_type_id.warehouse_id.partner_id)
diff --git a/delivery_hibou/views/delivery_views.xml b/delivery_hibou/views/delivery_views.xml
new file mode 100644
index 00000000..681578aa
--- /dev/null
+++ b/delivery_hibou/views/delivery_views.xml
@@ -0,0 +1,26 @@
+
+
+
+ hibou.delivery.carrier.form
+ delivery.carrier
+
+
+
+
+
+
+
+
+
+
+ hibou.choose.delivery.package.form
+ choose.delivery.package
+
+
+
+ []
+
+
+
+
+
\ No newline at end of file
diff --git a/delivery_hibou/views/stock_views.xml b/delivery_hibou/views/stock_views.xml
new file mode 100644
index 00000000..6bb47f34
--- /dev/null
+++ b/delivery_hibou/views/stock_views.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ hibou.stock.quant.package.form
+ stock.quant.package
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hibou.delivery.stock.picking_withcarrier.form.view
+ stock.picking
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+