From 9955064415aecdfd567baa38906501fb92002ab9 Mon Sep 17 00:00:00 2001 From: yhu-odoo Date: Wed, 31 May 2023 12:20:36 +0000 Subject: [PATCH] [FIX] purchase_stock: valuation layer wrong when different currency To reproduce: 1. Create a storable product with ordered quantities policy for purchase and with a category set as FIFO automated 2. Create a purchase order in a different currency and set quantities for partial deliveries 3. Create a partial delivery 4. Fully invoice the purchase order 5. Create the delivery of the complementary units 6. Check the stock valuation layer The valuation of backorder is wrong. The value on valuation layer is in company's currency, while the value on invoice line is in the PO's currency. When create valuation line for backorder, we didn't convert them to same currency. opw-3300266 closes odoo/odoo#125206 X-original-commit: c2de679fcba7f153201dde5b3aa989eb3f329910 Signed-off-by: Arnold Moyaux (arm) Signed-off-by: Yuchen Huang (yhu) --- addons/purchase_stock/models/stock_move.py | 11 ++++- .../tests/test_stockvaluation.py | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/addons/purchase_stock/models/stock_move.py b/addons/purchase_stock/models/stock_move.py index 9f9f02cf0ba..e5a6710feaa 100644 --- a/addons/purchase_stock/models/stock_move.py +++ b/addons/purchase_stock/models/stock_move.py @@ -45,13 +45,20 @@ class StockMove(models.Model): if float_compare(line.qty_invoiced, received_qty, precision_rounding=line.product_uom.rounding) > 0: move_layer = line.move_ids.stock_valuation_layer_ids invoiced_layer = line.invoice_lines.stock_valuation_layer_ids - receipt_value = sum(move_layer.mapped('value')) + sum(invoiced_layer.mapped('value')) + # value on valuation layer is in company's currency, while value on invoice line is in order's currency + receipt_value = 0 + if move_layer: + receipt_value += sum(move_layer.mapped(lambda l: l.currency_id._convert( + l.value, order.currency_id, order.company_id, l.account_move_id.date, round=False))) + if invoiced_layer: + receipt_value += sum(invoiced_layer.mapped(lambda l: l.currency_id._convert( + l.value, order.currency_id, order.company_id, l.account_move_id.date, round=False))) invoiced_value = 0 invoiced_qty = 0 for invoice_line in line.invoice_lines: if invoice_line.tax_ids: invoiced_value += invoice_line.tax_ids.with_context(round=False).compute_all( - invoice_line.price_unit, currency=invoice_line.account_id.currency_id, quantity=invoice_line.quantity)['total_void'] + invoice_line.price_unit, currency=invoice_line.currency_id, quantity=invoice_line.quantity)['total_void'] else: invoiced_value += invoice_line.price_unit * invoice_line.quantity invoiced_qty += invoice_line.product_uom_id._compute_quantity(invoice_line.quantity, line.product_id.uom_id) diff --git a/addons/purchase_stock/tests/test_stockvaluation.py b/addons/purchase_stock/tests/test_stockvaluation.py index 8a141046c73..c89f79e7902 100644 --- a/addons/purchase_stock/tests/test_stockvaluation.py +++ b/addons/purchase_stock/tests/test_stockvaluation.py @@ -1887,3 +1887,48 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon): full_reconcile = input_amls[0].full_reconcile_id self.assertTrue(full_reconcile) self.assertTrue(all(aml.full_reconcile_id == full_reconcile for aml in input_amls)) + + def test_invoice_on_ordered_qty_with_backorder_and_different_currency(self): + """Create a PO with currency different from the company currency. Set the + product to be invoiced on ordered quantities. Receive partially the products + and create a backorder. Create an invoice for the ordered quantity. Then + receive the backorder. Check if the valuation layer is correctly created. + """ + usd_currency = self.env.ref('base.USD') + self.env.company.currency_id = usd_currency.id + self.product1.categ_id.property_cost_method = 'fifo' + self.product1.purchase_method = 'purchase' + + price_unit_EUR = 100 + price_unit_USD = self.env.ref('base.EUR')._convert(price_unit_EUR, usd_currency, self.env.company, fields.Date.today(), round=False) + po = self.env['purchase.order'].create({ + 'partner_id': self.partner_id.id, + 'currency_id': self.env.ref('base.EUR').id, + 'order_line': [ + (0, 0, { + 'name': self.product1.name, + 'product_id': self.product1.id, + 'product_qty': 12.0, + 'product_uom': self.product1.uom_po_id.id, + 'price_unit': 100.0, + 'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT), + }), + ], + }) + po.button_confirm() + picking = po.picking_ids[0] + move = picking.move_ids[0] + move.quantity_done = 10 + res_dict = picking.button_validate() + self.assertEqual(res_dict['res_model'], 'stock.backorder.confirmation') + wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')).with_context(res_dict['context']) + wizard.process() + self.assertAlmostEqual(move.stock_valuation_layer_ids.unit_cost, price_unit_USD) + + po.action_create_invoice() + + picking2 = po.picking_ids.filtered(lambda p: p.backorder_id) + move2 = picking2.move_ids[0] + move2.quantity_done = 2 + picking2.button_validate() + self.assertAlmostEqual(move2.stock_valuation_layer_ids.unit_cost, price_unit_USD)