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
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-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
+
+
+
+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.
+
+
-
+
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.
-
+
-
+
This module is maintained by the OCA.
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 @@
-
-
-
-
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 @@
+
+
+
+
+
+
+ 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 @@
}"
/>
+
+
+
+