diff --git a/stock_move_packaging_qty/README.rst b/stock_move_packaging_qty/README.rst index d3de8f875..067dc3d2b 100644 --- a/stock_move_packaging_qty/README.rst +++ b/stock_move_packaging_qty/README.rst @@ -7,7 +7,7 @@ Stock Packaging Qty !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:ac3969cedf86da72ca20a93812053d6d9054a9f5d8e0c896eab57fe2b835eefc + !! source digest: sha256:5b4005788a852d7f71c44042e6c8839be5963fd5d6ac95822b93c48c660a2730 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -28,13 +28,22 @@ Stock Packaging Qty |badge1| |badge2| |badge3| |badge4| |badge5| -Add packaging fields in the stock moves and their reports. +Add packaging fields in the stock moves, their lines and their reports. **Table of contents** .. contents:: :local: +Known issues / Roadmap +====================== + +* Maybe we should track also reserved packaging quantities? + +* Since we store done product packaging quantities in the stock move lines, we + should be able to use this information in quants to provide real + packaging-based stock data. + Bug Tracker =========== diff --git a/stock_move_packaging_qty/__manifest__.py b/stock_move_packaging_qty/__manifest__.py index 51125885d..abd1072f3 100644 --- a/stock_move_packaging_qty/__manifest__.py +++ b/stock_move_packaging_qty/__manifest__.py @@ -10,7 +10,8 @@ "depends": ["stock"], "data": [ "views/report_stock_picking.xml", - "views/stock_move_tree_view.xml", + "views/stock_move_line_view.xml", + "views/stock_move_view.xml", "views/stock_picking_form_view.xml", ], "license": "LGPL-3", diff --git a/stock_move_packaging_qty/models/__init__.py b/stock_move_packaging_qty/models/__init__.py index 6bda2d242..f800274cd 100644 --- a/stock_move_packaging_qty/models/__init__.py +++ b/stock_move_packaging_qty/models/__init__.py @@ -1 +1,2 @@ from . import stock_move +from . import stock_move_line diff --git a/stock_move_packaging_qty/models/stock_move.py b/stock_move_packaging_qty/models/stock_move.py index f42c70eb2..b6746f971 100644 --- a/stock_move_packaging_qty/models/stock_move.py +++ b/stock_move_packaging_qty/models/stock_move.py @@ -1,17 +1,24 @@ # Copyright 2020 Camptocamp SA # Copyright 2021 ForgeFlow, S.L. -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -from odoo import api, fields, models +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). +from odoo import _, api, exceptions, fields, models +from odoo.tools import float_compare -class StockPicking(models.Model): +class StockMove(models.Model): _inherit = "stock.move" product_packaging_qty = fields.Float( - string="Pkg. Qty.", + string="Pkgs. Demand", compute="_compute_product_packaging_qty", inverse="_inverse_product_packaging_qty", - help="Amount of packages demanded.", + help="Amount of product packagings demanded.", + ) + product_packaging_qty_done = fields.Float( + string="Pkgs. Done", + compute="_compute_product_packaging_qty_done", + inverse="_inverse_product_packaging_qty_done", + help="Amount of product packagings done.", ) @api.depends( @@ -31,6 +38,18 @@ class StockPicking(models.Model): move.product_uom_qty / move._get_single_package_uom_qty() ) + @api.depends( + "move_line_ids.product_packaging_qty_done", + "move_line_nosuggest_ids.product_packaging_qty_done", + ) + def _compute_product_packaging_qty_done(self): + """Get the sum of done packaging qtys from move lines.""" + for move in self: + lines = move._get_move_lines() + move.product_packaging_qty_done = sum( + lines.mapped("product_packaging_qty_done") + ) + @api.onchange("product_packaging_qty") def _inverse_product_packaging_qty(self): """Store the quantity in the product's UoM. @@ -43,6 +62,23 @@ class StockPicking(models.Model): uom_factor = move._get_single_package_uom_qty() move.product_uom_qty = move.product_packaging_qty * uom_factor + def _inverse_product_packaging_qty_done(self): + """Store the done packaging dqty in the move line if there's just one.""" + for move in self: + lines = move._get_move_lines() + # Setting 0 done pkgs with no lines? Nothing to do + if not lines and not move.product_packaging_qty_done: + continue + if len(lines) != 1: + raise exceptions.UserError( + _( + "There are %d move lines involved. " + "Please set their product packaging done qty directly.", + len(lines), + ) + ) + lines.product_packaging_qty_done = move.product_packaging_qty_done + @api.onchange("product_packaging_id") def _onchange_product_packaging(self): """Add a default qty if the packaging has an invalid value.""" @@ -62,3 +98,32 @@ class StockPicking(models.Model): return self.product_packaging_id.product_uom_id._compute_quantity( self.product_packaging_id.qty, self.product_uom ) + + def _set_quantities_to_reservation(self): + """Add packaging qtys when clicking on "Set Quantities".""" + result = super()._set_quantities_to_reservation() + digits = self.env["stock.move.line"].fields_get(["qty_done"], ["digits"])[ + "qty_done" + ]["digits"][1] + for line in self.move_line_ids: + if float_compare(line.qty_done, line.reserved_uom_qty, digits): + continue + if not line.product_packaging_id: + line.product_packaging_qty_done = 0 + continue + line.product_packaging_qty_done = ( + line.product_packaging_id._check_qty( + line.qty_done, line.product_uom_id, "DOWN" + ) + / line.product_packaging_id.qty + ) + return result + + def _clear_quantities_to_zero(self): + """Clear packaging qtys when clicking on "Clear Quantities".""" + result = super()._clear_quantities_to_zero() + for line in self.move_line_ids: + if line.qty_done: + continue + line.product_packaging_qty_done = 0 + return result diff --git a/stock_move_packaging_qty/models/stock_move_line.py b/stock_move_packaging_qty/models/stock_move_line.py new file mode 100644 index 000000000..b953ead6a --- /dev/null +++ b/stock_move_packaging_qty/models/stock_move_line.py @@ -0,0 +1,49 @@ +# Copyright 2024 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) +from odoo import fields, models + + +class StockMoveLine(models.Model): + _inherit = "stock.move.line" + + product_packaging_id = fields.Many2one( + related="move_id.product_packaging_id", readonly=True + ) + product_packaging_qty_done = fields.Float( + string="Done Pkg. Qty.", + help="Product packaging quantity done.", + ) + + def _get_aggregated_properties(self, move_line=False, move=False): + """Aggregate by product packaging too.""" + result = super()._get_aggregated_properties(move_line, move) + pkg = result["move"].product_packaging_id + result["product_packaging"] = pkg + result["line_key"] += f"_{pkg.id}" + return result + + def _get_aggregated_product_quantities(self, **kwargs): + """Aggregate by product packaging too.""" + result = super()._get_aggregated_product_quantities(**kwargs) + # Know all involved move lines, following upstream criteria + all_lines = self.browse() + processed_moves = all_lines.move_id + if kwargs.get("except_package"): + all_lines |= self - self.filtered("result_package_id") + if not kwargs.get("strict"): + moves = (self.picking_id | self.picking_id.backorder_ids).move_ids + all_lines |= moves.move_line_ids | moves.move_line_nosuggest_ids + # Aggregate product packaging quantities + for move_line in all_lines: + props = self._get_aggregated_properties(move_line) + try: + agg = result[props["line_key"]] + except KeyError: + continue # Missing aggregation; nothing to do + agg.setdefault("product_packaging_qty", 0.0) + agg.setdefault("product_packaging_qty_done", 0.0) + agg["product_packaging_qty_done"] += move_line.product_packaging_qty_done + if move_line.move_id not in processed_moves: + agg["product_packaging_qty"] += move_line.move_id.product_packaging_qty + processed_moves |= move_line.move_id + return result diff --git a/stock_move_packaging_qty/readme/DESCRIPTION.rst b/stock_move_packaging_qty/readme/DESCRIPTION.rst index 0b1041118..bdbb7d88f 100644 --- a/stock_move_packaging_qty/readme/DESCRIPTION.rst +++ b/stock_move_packaging_qty/readme/DESCRIPTION.rst @@ -1 +1 @@ -Add packaging fields in the stock moves and their reports. +Add packaging fields in the stock moves, their lines and their reports. diff --git a/stock_move_packaging_qty/readme/ROADMAP.rst b/stock_move_packaging_qty/readme/ROADMAP.rst new file mode 100644 index 000000000..a3737366b --- /dev/null +++ b/stock_move_packaging_qty/readme/ROADMAP.rst @@ -0,0 +1,5 @@ +* Maybe we should track also reserved packaging quantities? + +* Since we store done product packaging quantities in the stock move lines, we + should be able to use this information in quants to provide real + packaging-based stock data. diff --git a/stock_move_packaging_qty/static/description/index.html b/stock_move_packaging_qty/static/description/index.html index ad679f59a..831109373 100644 --- a/stock_move_packaging_qty/static/description/index.html +++ b/stock_move_packaging_qty/static/description/index.html @@ -1,3 +1,4 @@ + @@ -366,24 +367,34 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:ac3969cedf86da72ca20a93812053d6d9054a9f5d8e0c896eab57fe2b835eefc +!! source digest: sha256:5b4005788a852d7f71c44042e6c8839be5963fd5d6ac95822b93c48c660a2730 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: LGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runboat

-

Add packaging fields in the stock moves and their reports.

+

Add packaging fields in the stock moves, their lines and their reports.

Table of contents

+
+

Known issues / Roadmap

+ +
-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -391,15 +402,15 @@ If you spotted it first, help us to smash it by providing a detailed and welcome

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose diff --git a/stock_move_packaging_qty/tests/test_stock_move_packaging_qty.py b/stock_move_packaging_qty/tests/test_stock_move_packaging_qty.py index ca6ccff1e..cc62c83c2 100644 --- a/stock_move_packaging_qty/tests/test_stock_move_packaging_qty.py +++ b/stock_move_packaging_qty/tests/test_stock_move_packaging_qty.py @@ -12,7 +12,14 @@ class TestStockMovePackagingQty(TransactionCase): cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) cls.env.user.groups_id |= cls.env.ref("product.group_stock_packaging") cls.partner = cls.env.ref("base.res_partner_12") - cls.product = cls.env.ref("product.product_product_9") + cls.product = cls.env["product.product"].create( + { + "name": "Test product", + "type": "consu", + "uom_id": cls.env.ref("uom.product_uom_unit").id, + "uom_po_id": cls.env.ref("uom.product_uom_unit").id, + } + ) cls.packaging = cls.env["product.packaging"].create( {"name": "Test packaging", "product_id": cls.product.id, "qty": 5.0} ) @@ -59,3 +66,20 @@ class TestStockMovePackagingQty(TransactionCase): move_f.product_packaging_id = self.packaging self.assertEqual(move_f.product_uom_qty, 5) self.assertEqual(move_f.product_packaging_qty, 1) + picking = picking_f.save() + self.assertEqual(picking.state, "draft") + picking.action_assign() + picking.action_set_quantities_to_reservation() + self.assertRecordValues( + picking.move_ids_without_package, + [ + { + "product_id": self.product.id, + "product_packaging_id": self.packaging.id, + "product_packaging_qty_done": 1, + "product_packaging_qty": 1, + "product_uom_qty": 5, + } + ], + ) + picking.button_validate() diff --git a/stock_move_packaging_qty/views/report_stock_picking.xml b/stock_move_packaging_qty/views/report_stock_picking.xml index f9183a7e2..149299516 100644 --- a/stock_move_packaging_qty/views/report_stock_picking.xml +++ b/stock_move_packaging_qty/views/report_stock_picking.xml @@ -40,5 +40,72 @@

+ + +
+ : + +
+
+ + + + + diff --git a/stock_move_packaging_qty/views/stock_move_line_view.xml b/stock_move_packaging_qty/views/stock_move_line_view.xml new file mode 100644 index 000000000..feb698d5b --- /dev/null +++ b/stock_move_packaging_qty/views/stock_move_line_view.xml @@ -0,0 +1,64 @@ + + + + + Add product packaging qty done info + stock.move.line + + + + + + + + + + + Add product packaging qty done info + stock.move.line + + + + + + + + + Add product packaging qty done info + stock.move.line + + + + + + + + + diff --git a/stock_move_packaging_qty/views/stock_move_tree_view.xml b/stock_move_packaging_qty/views/stock_move_tree_view.xml deleted file mode 100644 index fdee6314e..000000000 --- a/stock_move_packaging_qty/views/stock_move_tree_view.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - view.move.extra.tree - stock.move - - - - - - - - - diff --git a/stock_move_packaging_qty/views/stock_move_view.xml b/stock_move_packaging_qty/views/stock_move_view.xml new file mode 100644 index 000000000..9f285c3aa --- /dev/null +++ b/stock_move_packaging_qty/views/stock_move_view.xml @@ -0,0 +1,74 @@ + + + + + view.move.extra.tree + stock.move + + + + + + + + + + + Add product packaging qty info + stock.move + + + + + + + + + diff --git a/stock_move_packaging_qty/views/stock_picking_form_view.xml b/stock_move_packaging_qty/views/stock_picking_form_view.xml index d6fd0b4c3..c979c8747 100644 --- a/stock_move_packaging_qty/views/stock_picking_form_view.xml +++ b/stock_move_packaging_qty/views/stock_picking_form_view.xml @@ -13,9 +13,6 @@ name="product_packaging_qty" groups="product.group_stock_packaging" attrs="{ - 'column_invisible': [ - ('parent.immediate_transfer', '=', True), - ], 'readonly': [ '|', ('product_packaging_id', '=', False), @@ -24,6 +21,17 @@ }" /> + + + +