From bdac1407a742a840da29523ef02dd5107e22730c Mon Sep 17 00:00:00 2001 From: fja-odoo Date: Tue, 31 Aug 2021 15:05:50 +0000 Subject: [PATCH] [FIX] sale_stock, purchase_stock: fix conversion qty received Rounding in purchase order is 'UP' which is causing issue when doing receipt in a different uom than the purchase. Rounding should be 'HALF-UP' Added test in purchase and sales to ensure this issue is detected in the future. closes odoo/odoo#76006 X-original-commit: 9d3d1949d20834c92449c989299ac8e67f26d05d Signed-off-by: William Henrotin --- addons/purchase_stock/models/purchase.py | 6 ++-- .../tests/test_purchase_order.py | 30 +++++++++++++++++++ addons/sale_stock/tests/test_sale_stock.py | 29 ++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py index e33613a5f69..e842866951c 100644 --- a/addons/purchase_stock/models/purchase.py +++ b/addons/purchase_stock/models/purchase.py @@ -308,7 +308,7 @@ class PurchaseOrderLine(models.Model): if move.state == 'done': if move.location_dest_id.usage == "supplier": if move.to_refund: - total -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom) + total -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP') elif move.origin_returned_move_id and move.origin_returned_move_id._is_dropshipped() and not move._is_dropshipped_returned(): # Edge case: the dropship is returned to the stock, no to the supplier. # In this case, the received quantity on the PO is set although we didn't @@ -323,9 +323,9 @@ class PurchaseOrderLine(models.Model): [("id", "child_of", move.warehouse_id.view_location_id.id)] ) ): - total -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom) + total -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP') else: - total += move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom) + total += move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP') line._track_qty_received(total) line.qty_received = total diff --git a/addons/purchase_stock/tests/test_purchase_order.py b/addons/purchase_stock/tests/test_purchase_order.py index 9d96dca4b37..56e621575c0 100644 --- a/addons/purchase_stock/tests/test_purchase_order.py +++ b/addons/purchase_stock/tests/test_purchase_order.py @@ -336,3 +336,33 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon): po_on_time_rate = po.on_time_rate # 4. Check both are equals. self.assertEqual(partner_on_time_rate, po_on_time_rate) + + def test_04_multi_uom(self): + yards_uom = self.env['uom.uom'].create({ + 'category_id': self.env.ref('uom.uom_categ_length').id, + 'name': 'Yards', + 'factor_inv': 0.9144, + 'uom_type': 'bigger', + }) + self.product_id_2.write({ + 'uom_id': self.env.ref('uom.product_uom_meter').id, + 'uom_po_id': yards_uom.id, + }) + po = self.env['purchase.order'].create({ + 'partner_id': self.partner_a.id, + 'order_line': [ + (0, 0, { + 'name': self.product_id_2.name, + 'product_id': self.product_id_2.id, + 'product_qty': 4.0, + 'product_uom': self.product_id_2.uom_po_id.id, + 'price_unit': 1.0, + 'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT), + }) + ], + }) + po.button_confirm() + picking = po.picking_ids[0] + picking.move_line_ids.write({'qty_done': 3.66}) + picking.button_validate() + self.assertEqual(po.order_line.mapped('qty_received'), [4.0], 'Purchase: no conversion error on receipt in different uom"') diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py index 21d46941e3f..2dce01c4087 100644 --- a/addons/sale_stock/tests/test_sale_stock.py +++ b/addons/sale_stock/tests/test_sale_stock.py @@ -1090,3 +1090,32 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon): so.picking_ids.action_cancel() self.assertEqual(so.invoice_status, 'no') + + def test_16_multi_uom(self): + yards_uom = self.env['uom.uom'].create({ + 'category_id': self.env.ref('uom.uom_categ_length').id, + 'name': 'Yards', + 'factor_inv': 0.9144, + 'uom_type': 'bigger', + }) + product = self.env.ref('product.product_product_11').copy({ + 'uom_id': self.env.ref('uom.product_uom_meter').id, + 'uom_po_id': yards_uom.id, + }) + so = self.env['sale.order'].create({ + 'partner_id': self.partner_a.id, + 'order_line': [ + (0, 0, { + 'name': product.name, + 'product_id': product.id, + 'product_uom_qty': 4.0, + 'product_uom': yards_uom.id, + 'price_unit': 1.0, + }) + ], + }) + so.action_confirm() + picking = so.picking_ids[0] + picking.move_lines.write({'quantity_done': 3.66}) + picking.button_validate() + self.assertEqual(so.order_line.mapped('qty_delivered'), [4.0], 'Sale: no conversion error on delivery in different uom"')