diff --git a/addons/purchase_stock/models/account_invoice.py b/addons/purchase_stock/models/account_invoice.py index 2eb8bc135fa..9a5adae6690 100644 --- a/addons/purchase_stock/models/account_invoice.py +++ b/addons/purchase_stock/models/account_invoice.py @@ -45,18 +45,14 @@ class AccountMove(models.Model): debit_expense_account = line._get_price_diff_account() if not debit_expense_account: continue - if line.product_id.cost_method != 'standard' and not line.purchase_line_id: - continue - if line.product_id.cost_method != 'standard': - - # Retrieve stock valuation moves. - valuation_stock_moves = self.env['stock.move'].search([ - ('purchase_line_id', '=', line.purchase_line_id.id), - ('state', '=', 'done'), - ('product_qty', '!=', 0.0), - ('product_id', '=', line.product_id.id), # kits must be handled manually - ]) + # Retrieve stock valuation moves. + valuation_stock_moves = self.env['stock.move'].search([ + ('purchase_line_id', '=', line.purchase_line_id.id), + ('state', '=', 'done'), + ('product_qty', '!=', 0.0), + ]) if line.purchase_line_id else self.env['stock.move'] + if line.product_id.cost_method != 'standard' and line.purchase_line_id: if move.move_type == 'in_refund': valuation_stock_moves = valuation_stock_moves.filtered(lambda stock_move: stock_move._is_out()) else: @@ -68,13 +64,14 @@ class AccountMove(models.Model): valuation_price_unit_total, valuation_total_qty = valuation_stock_moves._get_valuation_price_and_qty(line, move.currency_id) valuation_price_unit = valuation_price_unit_total / valuation_total_qty valuation_price_unit = line.product_id.uom_id._compute_price(valuation_price_unit, line.product_uom_id) - else: + # Valuation_price unit is always expressed in invoice currency, so that it can always be computed with the good rate price_unit = line.product_id.uom_id._compute_price(line.product_id.standard_price, line.product_uom_id) price_unit = -price_unit if line.move_id.move_type == 'in_refund' else price_unit + valuation_date = valuation_stock_moves and max(valuation_stock_moves.mapped('date')) or move.date valuation_price_unit = line.company_currency_id._convert( price_unit, move.currency_id, - move.company_id, fields.Date.today(), round=False + move.company_id, valuation_date, round=False ) diff --git a/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py b/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py index beb29be6d88..fbc78b466c1 100644 --- a/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py +++ b/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py @@ -5,6 +5,7 @@ from freezegun import freeze_time from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon from odoo.tests.common import Form, tagged +from odoo import fields @@ -210,6 +211,75 @@ class TestValuationReconciliation(ValuationReconciliationTestCommon): picking = self.env['stock.picking'].search([('purchase_id', '=', purchase_order.id)]) self.check_reconciliation(invoice, picking) + @freeze_time('2021-01-03') + def test_price_difference_exchange_difference_accounting_date(self): + self.stock_account_product_categ.property_account_creditor_price_difference_categ = self.company_data['default_account_stock_price_diff'] + test_product = self.test_product_delivery + test_product.categ_id.write({"property_cost_method": "standard"}) + test_product.write({'standard_price': 100.0}) + date_po_receipt = '2021-01-02' + rate_po_receipt = 25.0 + date_bill = '2021-01-01' + rate_bill = 30.0 + date_accounting = '2021-01-03' + rate_accounting = 26.0 + + foreign_currency = self.currency_data['currency'] + company_currency = self.env.company.currency_id + self.env['res.currency.rate'].create([ + { + 'name': date_po_receipt, + 'rate': rate_po_receipt, + 'currency_id': foreign_currency.id, + 'company_id': self.env.company.id, + }, { + 'name': date_bill, + 'rate': rate_bill, + 'currency_id': foreign_currency.id, + 'company_id': self.env.company.id, + }, { + 'name': date_accounting, + 'rate': rate_accounting, + 'currency_id': foreign_currency.id, + 'company_id': self.env.company.id, + }, { + 'name': date_po_receipt, + 'rate': 1.0, + 'currency_id': company_currency.id, + 'company_id': self.env.company.id, + }, { + 'name': date_accounting, + 'rate': 1.0, + 'currency_id': company_currency.id, + 'company_id': self.env.company.id, + }, { + 'name': date_bill, + 'rate': 1.0, + 'currency_id': company_currency.id, + 'company_id': self.env.company.id, + }]) + + #purchase order created in foreign currency + purchase_order = self._create_purchase(test_product, date_po_receipt, quantity=10, price_unit=3000) + with freeze_time(date_po_receipt): + self._process_pickings(purchase_order.picking_ids) + invoice = self._create_invoice_for_po(purchase_order, date_bill) + with Form(invoice) as move_form: + move_form.invoice_date = fields.Date.from_string(date_bill) + move_form.date = fields.Date.from_string(date_accounting) + invoice.action_post() + + price_diff_line = invoice.line_ids.filtered(lambda l: l.account_id == self.stock_account_product_categ.property_account_creditor_price_difference_categ) + self.assertTrue(len(price_diff_line) == 1, "A price difference line should be created") + self.assertAlmostEqual(price_diff_line.balance, 192.31) + self.assertAlmostEqual(price_diff_line.price_subtotal, 5000.0) + + picking = self.env['stock.picking'].search([('purchase_id', '=', purchase_order.id)]) + interim_account_id = self.company_data['default_account_stock_in'].id + + valuation_line = picking.move_ids.mapped('account_move_ids.line_ids').filtered(lambda x: x.account_id.id == interim_account_id) + self.assertTrue(valuation_line.full_reconcile_id, "The reconciliation should be total at that point.") + def test_reconcile_cash_basis_bill(self): ''' Test the generation of the CABA move after bill payment '''