From 0accbd6e69937011ea9bac141dd186c3757b1a8a Mon Sep 17 00:00:00 2001 From: "Tiffany Chang (tic)" Date: Fri, 17 Apr 2020 11:36:19 +0000 Subject: [PATCH 1/6] [IMP] stock, delivery: aggregate delivery slip product lines This commit makes it so move_lines with the same product+description+uom are grouped together ONLY WHEN serial numbers/lots are not to be printed. This is the case when the setting "Display Lots & Serial Numbers on Delivery Slips" is not active or when there are no lots/serial numbers assigned in the picking. This is only applicable for stock.pickings that are 'state=Done'. In order to implement this, some clean up of lots/serial numbers printing was also done. This completes subsection 2 of overall Improve Delivery Slip task. Task: 2008609 --- addons/delivery/models/stock_move.py | 14 +++++ addons/delivery/views/report_deliveryslip.xml | 5 +- addons/stock/models/stock_move_line.py | 29 ++++++++++ addons/stock/report/report_deliveryslip.xml | 57 ++++++++++++------- 4 files changed, 83 insertions(+), 22 deletions(-) diff --git a/addons/delivery/models/stock_move.py b/addons/delivery/models/stock_move.py index 49ace8ec7ef..f679f2aa1f0 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): + """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(StockMoveLine, self)._get_aggregated_product_quantities() + 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/views/report_deliveryslip.xml b/addons/delivery/views/report_deliveryslip.xml index 43031e5803e..0da77776e76 100644 --- a/addons/delivery/views/report_deliveryslip.xml +++ b/addons/delivery/views/report_deliveryslip.xml @@ -29,9 +29,12 @@ HS Code - + + + + diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 079aad39305..c78f8e7aed5 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): + """ 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..598360e51a9 100644 --- a/addons/stock/report/report_deliveryslip.xml +++ b/addons/stock/report/report_deliveryslip.xml @@ -56,12 +56,13 @@ - + + - - @@ -69,26 +70,40 @@ - - - - + - + + - - - + + + + + + + + + +
Product + + Lot/Serial Number
- -

- -

-
- + + +
+ +

+ +

- + + + + - - -
+ +

+ +

+
+ + +
From d20467bb6a2e1e6b07a2db86ee4be199eeeed0e2 Mon Sep 17 00:00:00 2001 From: "Tiffany Chang (tic)" Date: Tue, 21 Apr 2020 08:35:30 +0000 Subject: [PATCH 2/6] [IMP] delivery, stock: group delivery slip packages This commit makes it so if a delivery slip contains 1 or more packages then move lines will be split by package (and non-package) groups with a "section line" between them. This only applies to when a stock.picking is 'State=Done'. In order to accomodate complexity of splitting by package + grouping by product unless printing serial numbers/lots + template inheritance, reoccurring parts of the template are split into their own templates and called. Relevant inheritance has been updated to match. Additionally, picking.shipping_weight calculation has been updated so if a pack.weight = 0 then calculation will default to the calculated product weight. This prevents inconsistency between the "Total Weight" at the top of the Delivery Slip and the package sections' displayed weights. To distinguish which value is being used, package sections that use the total product weight rather than the pack.weight have "(estimated)" after it. This completes subsection 3 of overall Improve delivery slip task. Task: 2039720 --- addons/delivery/models/stock_picking.py | 8 +- addons/delivery/views/report_deliveryslip.xml | 46 +++++- addons/stock/report/report_deliveryslip.xml | 131 +++++++++++++----- 3 files changed, 142 insertions(+), 43 deletions(-) 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 0da77776e76..4bf0057dd3b 100644 --- a/addons/delivery/views/report_deliveryslip.xml +++ b/addons/delivery/views/report_deliveryslip.xml @@ -6,7 +6,7 @@

- Weight: + Total Weight:
@@ -22,19 +22,51 @@ HS Code - - - - HS Code - + + + + + + + + diff --git a/addons/stock/report/report_deliveryslip.xml b/addons/stock/report/report_deliveryslip.xml index 598360e51a9..09d0e66674c 100644 --- a/addons/stock/report/report_deliveryslip.xml +++ b/addons/stock/report/report_deliveryslip.xml @@ -1,6 +1,5 @@ - + + + + + + + + + - From 5ebf1f5dbedb52786289095b7f2dc2733394b419 Mon Sep 17 00:00:00 2001 From: "Tiffany Chang (tic)" Date: Fri, 17 Apr 2020 11:46:55 +0000 Subject: [PATCH 3/6] [IMP] product_expiry: Add delivery slip expiration dates This commit adds the option to have lot/serial number expiration dates be included in a delivery report (via settings). This is only applicable for stock.picking where 'State=Done', when 1 or more lot/serial numbers are used, and 1 or more lot/serial number expiration dates exist. This completes subsection 4 of overall Improve delivery slip task. Task: 2039720 --- addons/product_expiry/__manifest__.py | 3 +++ addons/product_expiry/models/__init__.py | 1 + .../models/res_config_settings.py | 21 +++++++++++++++++ .../report/report_deliveryslip.xml | 22 ++++++++++++++++++ .../security/stock_security.xml | 9 ++++++++ .../views/res_config_settings_views.xml | 23 +++++++++++++++++++ 6 files changed, 79 insertions(+) create mode 100644 addons/product_expiry/models/res_config_settings.py create mode 100644 addons/product_expiry/report/report_deliveryslip.xml create mode 100644 addons/product_expiry/security/stock_security.xml create mode 100644 addons/product_expiry/views/res_config_settings_views.xml 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/res_config_settings_views.xml b/addons/product_expiry/views/res_config_settings_views.xml new file mode 100644 index 00000000000..1c636bed186 --- /dev/null +++ b/addons/product_expiry/views/res_config_settings_views.xml @@ -0,0 +1,23 @@ + + + + res.config.settings.view.form.inherit.product.expiry.stock + res.config.settings + + + +
+
+ +
+
+
+
+
+
+
+
From a60527bde7f2bc4c614ac35fff3b28e11b0a6211 Mon Sep 17 00:00:00 2001 From: "Tiffany Chang (tic)" Date: Fri, 24 Apr 2020 15:18:36 +0000 Subject: [PATCH 4/6] [IMP] delivery, mrp, stock: group delivery slips by kit This commit extends the delivery slip so that products added as a kit component to a stock.picking are grouped together by kit (or non-kit) when at least 1 kit exists. Kit grouping occurs for products from a kit without a package assigned. Non-kit section appears only when no packages are assigned. Implementation notes: - Sub-kits (i.e. kits within kits) of kits are purposely not included - Duplicate product names with different BOMs will be merged into one since there is no straightforward way to distinguish them within current db structure - There are some cases where this doesn't work as expected and may lead to components being grouped together under the same kit even if they are from different kits (e.g. kit1 has bolts and so does kit2 => on slip bolts will only show under kit1 section). Two cases where this happens is when kits are added before 'Operation Type' is specified and 'Operation Type = Manufacturing' This completes subsection 1 of overall Improve delivery slip task. Task: 2039720 --- addons/delivery/models/stock_move.py | 4 +- addons/mrp/__manifest__.py | 1 + addons/mrp/models/stock_move.py | 18 +++++ addons/mrp/report/report_deliveryslip.xml | 81 +++++++++++++++++++++ addons/stock/models/stock_move_line.py | 2 +- addons/stock/report/report_deliveryslip.xml | 24 +++--- 6 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 addons/mrp/report/report_deliveryslip.xml diff --git a/addons/delivery/models/stock_move.py b/addons/delivery/models/stock_move.py index f679f2aa1f0..3b51fe7763e 100644 --- a/addons/delivery/models/stock_move.py +++ b/addons/delivery/models/stock_move.py @@ -42,7 +42,7 @@ class StockMoveLine(models.Model): 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): + 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 @@ -50,7 +50,7 @@ class StockMoveLine(models.Model): returns: dictionary {same_key_as_super: {same_values_as_super, hs_code}, ...} """ - aggregated_move_lines = super(StockMoveLine, self)._get_aggregated_product_quantities() + 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 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/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index c78f8e7aed5..6204341b12e 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -606,7 +606,7 @@ class StockMoveLine(models.Model): self.ensure_one() return location.should_bypass_reservation() or self.product_id.type != 'product' - def _get_aggregated_product_quantities(self): + 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 diff --git a/addons/stock/report/report_deliveryslip.xml b/addons/stock/report/report_deliveryslip.xml index 09d0e66674c..24bd5e2366b 100644 --- a/addons/stock/report/report_deliveryslip.xml +++ b/addons/stock/report/report_deliveryslip.xml @@ -69,12 +69,12 @@ - - + @@ -92,16 +92,16 @@ - - - + + + - + - + @@ -177,8 +177,14 @@