diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py index f4ff63f9607..e77d9a87b1c 100644 --- a/addons/delivery/models/delivery_carrier.py +++ b/addons/delivery/models/delivery_carrier.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import logging import psycopg2 from odoo import api, fields, models, registry, SUPERUSER_ID, _ +from odoo.tools.float_utils import float_round -_logger = logging.getLogger(__name__) +from .delivery_request_objects import DeliveryCommodity, DeliveryPackage class DeliveryCarrier(models.Model): @@ -277,3 +277,84 @@ class DeliveryCarrier(models.Model): def fixed_cancel_shipment(self, pickings): raise NotImplementedError() + + # -------------------------------- # + # get default packages/commodities # + # -------------------------------- # + + def _get_packages_from_order(self, order, default_package_type): + packages = [] + + total_cost = 0 + for line in order.order_line.filtered(lambda line: not line.is_delivery and not line.display_type): + total_cost += self._product_price_to_company_currency(line.product_qty, line.product_id, order.company_id) + + total_weight = order._get_estimated_weight() + # If max weight == 0 => division by 0. If this happens, we want to have + # more in the max weight than in the total weight, so that it only + # creates ONE package with everything. + max_weight = default_package_type.max_weight or total_weight + 1 + total_full_packages = int(total_weight / max_weight) + last_package_weight = total_weight % max_weight + + package_weights = [max_weight] * total_full_packages + [last_package_weight] if last_package_weight else [] + partial_cost = total_cost / len(package_weights) # separate the cost uniformly + for weight in package_weights: + packages.append(DeliveryPackage(None, weight, default_package_type, total_cost=partial_cost, currency=order.company_id.currency_id)) + return packages + + def _get_packages_from_picking(self, picking, default_package_type): + packages = [] + + if picking.is_return_picking: + commodities = self._get_commodities_from_stock_move_lines(picking.move_line_ids) + weight = picking._get_estimated_weight() + packages.append(DeliveryPackage(commodities, weight, default_package_type, currency=picking.company_id.currency_id)) + return packages + + # Create all packages. + for package in picking.package_ids: + move_lines = picking.move_line_ids.filtered(lambda ml: ml.result_package_id == package) + commodities = self._get_commodities_from_stock_move_lines(move_lines) + package_total_cost = 0.0 + for quant in package.quant_ids: + package_total_cost += self._product_price_to_company_currency(quant.quantity, quant.product_id, picking.company_id) + packages.append(DeliveryPackage(commodities, package.shipping_weight or package.weight, package.package_type_id, name=package.name, total_cost=package_total_cost, currency=picking.company_id.currency_id)) + + # Create one package: either everything is in pack or nothing is. + if picking.weight_bulk: + commodities = self._get_commodities_from_stock_move_lines(picking.move_line_ids) + package_total_cost = 0.0 + for move_line in picking.move_line_ids: + package_total_cost += self._product_price_to_company_currency(move_line.qty_done, move_line.product_id, picking.company_id) + packages.append(DeliveryPackage(commodities, picking.weight_bulk, default_package_type, name='Bulk Content', total_cost=package_total_cost, currency=picking.company_id.currency_id)) + + return packages + + def _get_commodities_from_order(self, order): + commodities = [] + + for line in order.order_line.filtered(lambda line: not line.is_delivery and not line.display_type): + unit_quantity = line.product_uom._compute_quantity(line.product_uom_qty, line.product_id.uom_id) + rounded_qty = max(1, float_round(unit_quantity, precision_digits=0)) + country_of_origin = line.product_id.country_of_origin or order.warehouse_id.partner_id.country_id.code + commodities.append(DeliveryCommodity(line.product_id, amount=rounded_qty, monetary_value=line.price_reduce_taxinc, country_of_origin=country_of_origin)) + + return commodities + + def _get_commodities_from_stock_move_lines(self, move_lines): + commodities = [] + + for line in move_lines.filtered(lambda line: line.product_id.type in ['product', 'consu']): + if line.state == 'done': + unit_quantity = line.product_uom_id._compute_quantity(line.qty_done, line.product_id.uom_id) + else: + unit_quantity = line.product_uom_id._compute_quantity(line.product_uom_qty, line.product_id.uom_id) + rounded_qty = max(1, float_round(unit_quantity, precision_digits=0)) + country_of_origin = line.product_id.country_of_origin or line.picking_id.picking_type_id.warehouse_id.partner_id.country_id.code + commodities.append(DeliveryCommodity(line.product_id, amount=rounded_qty, monetary_value=line.sale_price, country_of_origin=country_of_origin)) + + return commodities + + def _product_price_to_company_currency(self, quantity, product, company): + return company.currency_id._convert(quantity * product.standard_price, product.currency_id, company, fields.Date.today()) diff --git a/addons/delivery/models/delivery_request_objects.py b/addons/delivery/models/delivery_request_objects.py new file mode 100644 index 00000000000..4828e06a3a9 --- /dev/null +++ b/addons/delivery/models/delivery_request_objects.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +class DeliveryPackage: + """ Each provider need similar information about its packages. """ + def __init__(self, commodities, weight, package_type, name=None, total_cost=0, currency=None): + """ The UOMs are based on the config parameters, which is very convenient: + we do not need to keep those stored.""" + self.commodities = commodities or [] # list of DeliveryCommodity objects + self.weight = weight + self.dimension = { + 'length': package_type.packaging_length, + 'width': package_type.width, + 'height': package_type.height + } + self.packaging_type = package_type.shipper_package_code or False + self.name = name + self.total_cost = total_cost + self.currency_id = currency + + +class DeliveryCommodity: + """ Commodities information are needed for Commercial invoices with each provider. """ + def __init__(self, product, amount, monetary_value, country_of_origin): + self.product_id = product + self.qty = amount + self.monetary_value = monetary_value # based on company currency + self.country_of_origin = country_of_origin