From 7e950abc6c819934101899678fe3038be8c362b8 Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Fri, 21 Jan 2022 16:23:32 +0000 Subject: [PATCH] [FIX] mrp: unbuild a decimal quantity It is sometimes impossible to unbuild a product because of decimal values. To reproduce the issue: (Enable debug mode) 1. In Settings, enable "Units of Measure" 2. Set the decimal accuracy of "Product Unit of Measure" to 4 3. Set the rounding precision of "Units" to 0.001 4. Create two products P1, P2 5. Create a BoM: - Product: P1 - Quantity: 3 - Type: Manufacture - Components: 5 x P2 6. Process a MO with 3 x P1 7. Create an unbuild order UO: - Manufacturing Order: MO - Quantity: 1 8. Unbuild Error: An error message is displayed "The quantity done for the product "P2" doesn't respect the rounding precision[...]" At some point, for each SM associated to the UO, the modules uses the value of `product_uom_qty` to write the done quantity of the SM. The rounding of `product_uom_qty` is based on "Product Unit of Measure" so the component's SM has its quantity equal to `1.6667`. Moreover, writing the done quantity trigger the creation of the associated SML. As a result, the SML has its done quantity equal to `1.6667` while the rounding of the associated UoM is `0.001`. When calling `_action_done` on such a SML, it will raise an error because the value doesn't respect the rounding. Side note: the error message is incorrectly written, the indents in the file are displayed on the front-end. OPW-2710038 closes odoo/odoo#83369 X-original-commit: c11d772f39179c5dec5eb431b6f4bc02c680643a Signed-off-by: Adrien Widart Signed-off-by: Arnold Moyaux --- addons/mrp/models/mrp_unbuild.py | 4 ++-- addons/mrp/tests/test_unbuild.py | 33 ++++++++++++++++++++++++++ addons/stock/i18n/stock.pot | 6 ++--- addons/stock/models/stock_move_line.py | 6 ++--- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/addons/mrp/models/mrp_unbuild.py b/addons/mrp/models/mrp_unbuild.py index c3ca662652b..6dd4f0a55c5 100644 --- a/addons/mrp/models/mrp_unbuild.py +++ b/addons/mrp/models/mrp_unbuild.py @@ -3,7 +3,7 @@ from odoo import api, fields, models, _ from odoo.exceptions import AccessError, UserError -from odoo.tools import float_compare +from odoo.tools import float_compare, float_round from odoo.osv import expression @@ -182,7 +182,7 @@ class MrpUnbuild(models.Model): }) needed_quantity -= taken_quantity else: - move.quantity_done = move.product_uom_qty + move.quantity_done = float_round(move.product_uom_qty, precision_rounding=move.product_uom.rounding) finished_moves._action_done() consume_moves._action_done() diff --git a/addons/mrp/tests/test_unbuild.py b/addons/mrp/tests/test_unbuild.py index 778225c3bc8..dee22c6a46e 100644 --- a/addons/mrp/tests/test_unbuild.py +++ b/addons/mrp/tests/test_unbuild.py @@ -598,3 +598,36 @@ class TestUnbuild(TestMrpCommon): self.assertEqual(StockQuant._get_available_quantity(finshed_product, self.stock_location), 0, 'Table should not be available in stock') self.assertEqual(StockQuant._get_available_quantity(component1, self.stock_location), 1, 'Table head should be available in stock as the picking is transferred') self.assertEqual(StockQuant._get_available_quantity(component2, self.stock_location), 1, 'Table stand should be available in stock as the picking is transferred') + + def test_unbuild_decimal_qty(self): + """ + Use case: + - decimal accuracy of Product UoM > decimal accuracy of Units + - unbuild a product with a decimal quantity of component + """ + self.env['decimal.precision'].search([('name', '=', 'Product Unit of Measure')]).digits = 4 + self.uom_unit.rounding = 0.001 + + self.bom_1.product_qty = 3 + self.bom_1.bom_line_ids.product_qty = 5 + self.env['stock.quant']._update_available_quantity(self.product_2, self.stock_location, 3) + + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = self.bom_1.product_id + mo_form.bom_id = self.bom_1 + mo = mo_form.save() + mo.action_confirm() + mo.action_assign() + + mo_form = Form(mo) + mo_form.qty_producing = 3 + mo_form.save() + mo.button_mark_done() + + uo_form = Form(self.env['mrp.unbuild']) + uo_form.mo_id = mo + # Unbuilding one product means a decimal quantity equal to 1 / 3 * 5 for each component + uo_form.product_qty = 1 + uo = uo_form.save() + uo.action_unbuild() + self.assertEqual(uo.state, 'done') diff --git a/addons/stock/i18n/stock.pot b/addons/stock/i18n/stock.pot index e449a30cf0e..3e985401f66 100644 --- a/addons/stock/i18n/stock.pot +++ b/addons/stock/i18n/stock.pot @@ -7298,10 +7298,8 @@ msgstr "" #, python-format msgid "" "The quantity done for the product \"%s\" doesn't respect the rounding " -"precision defined on the unit of measure " -"\"%s\". Please change the quantity done or the" -" rounding precision of your unit of " -"measure." +"precision defined on the unit of measure \"%s\". Please change the quantity " +"done or the rounding precision of your unit of measure." msgstr "" #. module: stock diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 3bf15f053aa..034af4ee6e5 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -506,9 +506,9 @@ class StockMoveLine(models.Model): precision_digits = self.env['decimal.precision'].precision_get('Product Unit of Measure') qty_done = float_round(ml.qty_done, precision_digits=precision_digits, rounding_method='HALF-UP') if float_compare(uom_qty, qty_done, precision_digits=precision_digits) != 0: - raise UserError(_('The quantity done for the product "%s" doesn\'t respect the rounding precision \ - defined on the unit of measure "%s". Please change the quantity done or the \ - rounding precision of your unit of measure.') % (ml.product_id.display_name, ml.product_uom_id.name)) + raise UserError(_('The quantity done for the product "%s" doesn\'t respect the rounding precision ' + 'defined on the unit of measure "%s". Please change the quantity done or the ' + 'rounding precision of your unit of measure.') % (ml.product_id.display_name, ml.product_uom_id.name)) qty_done_float_compared = float_compare(ml.qty_done, 0, precision_rounding=ml.product_uom_id.rounding) if qty_done_float_compared > 0: