diff --git a/addons/delivery/models/stock_move.py b/addons/delivery/models/stock_move.py index 49ace8ec7ef..3b51fe7763e 100644 --- a/addons/delivery/models/stock_move.py +++ b/addons/delivery/models/stock_move.py @@ -41,3 +41,17 @@ class StockMoveLine(models.Model): unit_price = move_line.product_id.list_price qty = move_line.product_uom_id._compute_quantity(move_line.qty_done, move_line.product_id.uom_id) move_line.sale_price = unit_price * qty + + def _get_aggregated_product_quantities(self, **kwargs): + """Returns dictionary of products and corresponding values of interest + hs_code + + Unfortunately because we are working with aggregated data, we have to loop through the + aggregation to add more values to each datum. This extension adds on the hs_code value. + + returns: dictionary {same_key_as_super: {same_values_as_super, hs_code}, ...} + """ + aggregated_move_lines = super()._get_aggregated_product_quantities(**kwargs) + for aggregated_move_line in aggregated_move_lines: + hs_code = aggregated_move_lines[aggregated_move_line]['product'].product_tmpl_id.hs_code + aggregated_move_lines[aggregated_move_line]['hs_code'] = hs_code + return aggregated_move_lines diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py index d4c90d77d55..20ad15160f2 100644 --- a/addons/delivery/models/stock_picking.py +++ b/addons/delivery/models/stock_picking.py @@ -66,7 +66,8 @@ class StockPicking(models.Model): @api.depends('move_line_ids.result_package_id', 'move_line_ids.result_package_id.shipping_weight', 'weight_bulk') def _compute_shipping_weight(self): for picking in self: - picking.shipping_weight = picking.weight_bulk + sum([pack.shipping_weight for pack in picking.package_ids]) + # if shipping weight is not assigned => default to calculated product weight + picking.shipping_weight = picking.weight_bulk + sum([pack.shipping_weight or pack.weight for pack in picking.package_ids]) def _get_default_weight_uom(self): return self.env['product.template']._get_weight_uom_name_from_ir_config_parameter() @@ -83,8 +84,9 @@ class StockPicking(models.Model): carrier_tracking_url = fields.Char(string='Tracking URL', compute='_compute_carrier_tracking_url') weight_uom_name = fields.Char(string='Weight unit of measure label', compute='_compute_weight_uom_name', readonly=True, default=_get_default_weight_uom) package_ids = fields.Many2many('stock.quant.package', compute='_compute_packages', string='Packages') - weight_bulk = fields.Float('Bulk Weight', compute='_compute_bulk_weight') - shipping_weight = fields.Float("Weight for Shipping", compute='_compute_shipping_weight', help="Total weight of the packages and products which are not in a package. That's the weight used to compute the cost of the shipping.") + weight_bulk = fields.Float('Bulk Weight', compute='_compute_bulk_weight', help="Total weight of products which are not in a package.") + shipping_weight = fields.Float("Weight for Shipping", compute='_compute_shipping_weight', + help="Total weight of packages and products not in a package. Packages with no shipping weight specified will default to their products' total weight. This is the weight used to compute the cost of the shipping.") is_return_picking = fields.Boolean(compute='_compute_return_picking') return_label_ids = fields.One2many('ir.attachment', compute='_compute_return_label') diff --git a/addons/delivery/views/report_deliveryslip.xml b/addons/delivery/views/report_deliveryslip.xml index 43031e5803e..4bf0057dd3b 100644 --- a/addons/delivery/views/report_deliveryslip.xml +++ b/addons/delivery/views/report_deliveryslip.xml @@ -6,7 +6,7 @@

- Weight: + Total Weight:
@@ -22,16 +22,51 @@ HS Code - - - - HS Code - + + + + + + + + diff --git a/addons/mrp/__manifest__.py b/addons/mrp/__manifest__.py index ce6d450810d..a5fdb6e3ebc 100644 --- a/addons/mrp/__manifest__.py +++ b/addons/mrp/__manifest__.py @@ -36,6 +36,7 @@ 'views/res_config_settings_views.xml', 'views/mrp_templates.xml', 'views/stock_scrap_views.xml', + 'report/report_deliveryslip.xml', 'report/mrp_report_views_main.xml', 'report/mrp_report_bom_structure.xml', 'report/mrp_production_templates.xml', diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 3ad3ba69067..f006fdb22d9 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -60,6 +60,24 @@ class StockMoveLine(models.Model): move_line._log_message(production, move_line, 'mrp.track_production_move_template', vals) return super(StockMoveLine, self).write(vals) + def _get_aggregated_product_quantities(self, **kwargs): + """Returns dictionary of products and corresponding values of interest grouped by optional kit_name + + Removes descriptions where description == kit_name. kit_name is expected to be passed as a + kwargs value because this is not directly stored in move_line_ids. Unfortunately because we + are working with aggregated data, we have to loop through the aggregation to do this removal. + + arguments: kit_name (optional): string value of a kit name passed as a kwarg + returns: dictionary {same_key_as_super: {same_values_as_super, ...} + """ + aggregated_move_lines = super()._get_aggregated_product_quantities(**kwargs) + kit_name = kwargs.get('kit_name') + if kit_name: + for aggregated_move_line in aggregated_move_lines: + if aggregated_move_lines[aggregated_move_line]['description'] == kit_name: + aggregated_move_lines[aggregated_move_line]['description'] = "" + return aggregated_move_lines + class StockMove(models.Model): _inherit = 'stock.move' diff --git a/addons/mrp/report/report_deliveryslip.xml b/addons/mrp/report/report_deliveryslip.xml new file mode 100644 index 00000000000..88370ca6e78 --- /dev/null +++ b/addons/mrp/report/report_deliveryslip.xml @@ -0,0 +1,81 @@ + + + + + + + + + diff --git a/addons/product_expiry/__manifest__.py b/addons/product_expiry/__manifest__.py index 2021381a583..0a620081fa8 100644 --- a/addons/product_expiry/__manifest__.py +++ b/addons/product_expiry/__manifest__.py @@ -18,11 +18,14 @@ Following dates can be tracked: Also implements the removal strategy First Expiry First Out (FEFO) widely used, for example, in food industries. """, 'data': ['security/ir.model.access.csv', + 'security/stock_security.xml', 'views/production_lot_views.xml', 'views/product_template_views.xml', + 'views/res_config_settings_views.xml', 'views/stock_move_views.xml', 'views/stock_quant_views.xml', 'wizard/confirm_expiry_view.xml', + 'report/report_deliveryslip.xml', 'report/report_lot_barcode.xml', 'data/product_expiry_data.xml'], } diff --git a/addons/product_expiry/models/__init__.py b/addons/product_expiry/models/__init__.py index ce07f86f046..55fcdd6ea27 100644 --- a/addons/product_expiry/models/__init__.py +++ b/addons/product_expiry/models/__init__.py @@ -3,6 +3,7 @@ from . import production_lot from . import product_product +from . import res_config_settings from . import stock_move_line from . import stock_move from . import stock_picking diff --git a/addons/product_expiry/models/res_config_settings.py b/addons/product_expiry/models/res_config_settings.py new file mode 100644 index 00000000000..bb565c56346 --- /dev/null +++ b/addons/product_expiry/models/res_config_settings.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + group_expiry_date_on_delivery_slip = fields.Boolean("Display Expiration Dates on Delivery Slips", + implied_group='product_expiry.group_expiry_date_on_delivery_slip') + + @api.onchange('group_lot_on_delivery_slip') + def _onchange_group_lot_on_delivery_slip(self): + if not self.group_lot_on_delivery_slip: + self.group_expiry_date_on_delivery_slip = False + + @api.onchange('module_product_expiry') + def _onchange_module_product_expiry(self): + if not self.module_product_expiry: + self.group_expiry_date_on_delivery_slip = False diff --git a/addons/product_expiry/report/report_deliveryslip.xml b/addons/product_expiry/report/report_deliveryslip.xml new file mode 100644 index 00000000000..1d390ee181c --- /dev/null +++ b/addons/product_expiry/report/report_deliveryslip.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/addons/product_expiry/security/stock_security.xml b/addons/product_expiry/security/stock_security.xml new file mode 100644 index 00000000000..0757b8ac205 --- /dev/null +++ b/addons/product_expiry/security/stock_security.xml @@ -0,0 +1,9 @@ + + + + + Include expiration dates on delivery slip + + + + diff --git a/addons/product_expiry/views/product_template_views.xml b/addons/product_expiry/views/product_template_views.xml index c6af30107e6..a6deea0d936 100644 --- a/addons/product_expiry/views/product_template_views.xml +++ b/addons/product_expiry/views/product_template_views.xml @@ -8,7 +8,7 @@ - + + + + {'invisible': [('effective_date', '!=', False), ('commitment_date', '!=', False)]} + +
diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 079aad39305..6204341b12e 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -605,3 +605,32 @@ class StockMoveLine(models.Model): def _should_bypass_reservation(self, location): self.ensure_one() return location.should_bypass_reservation() or self.product_id.type != 'product' + + def _get_aggregated_product_quantities(self, **kwargs): + """ Returns a dictionary of products (key = id+name+description+uom) and corresponding values of interest. + + Allows aggregation of data across separate move lines for the same product. This is expected to be useful + in things such as delivery reports. Dict key is made as a combination of values we expect to want to group + the products by (i.e. so data is not lost). This function purposely ignores lots/SNs because these are + expected to already be properly grouped by line. + + returns: dictionary {product_id+name+description+uom: {product, name, description, qty_done, product_uom}, ...} + """ + aggregated_move_lines = {} + for move_line in self: + name = move_line.product_id.display_name + description = move_line.move_id.description_picking + if description == name or description == move_line.product_id.name: + description = False + uom = move_line.product_uom_id + line_key = str(move_line.product_id.id) + "_" + name + (description or "") + "uom " + str(uom.id) + + if line_key not in aggregated_move_lines: + aggregated_move_lines[line_key] = {'name': name, + 'description': description, + 'qty_done': move_line.qty_done, + 'product_uom': uom.name, + 'product': move_line.product_id} + else: + aggregated_move_lines[line_key]['qty_done'] += move_line.qty_done + return aggregated_move_lines diff --git a/addons/stock/report/report_deliveryslip.xml b/addons/stock/report/report_deliveryslip.xml index f6f56b10e86..24bd5e2366b 100644 --- a/addons/stock/report/report_deliveryslip.xml +++ b/addons/stock/report/report_deliveryslip.xml @@ -1,6 +1,5 @@ - + + + + + + + + + -