[FIX] purchase_stock: price difference currency conversion date

Have a product P configured with:
* Product Type: Storable Product
* Product category:
    * costing method: standard
    * Inventory Valuation Automated
    * Price Difference Account: "500000 Cost of Goods Sold"
* Cost $100

Activate Multicurrency:
* USD main currency, CZK foreign currency
* On CZK set the rates
    * date1, Unit per USD: 30
    * date2, Unit per USD: 25 (date2 > date1)
    * date3, Unit per USD: 26 (date3 > date2)

Create a purchase order:
* Date: date2
* Currency: CZK
* Order line:
    * Product P
    * Quantity 10
    * Unit price 3000
Confirm, receive product on date2 (svl needs to be in date2)
Create the vendor bill with:
* Bill date: date 1
* Accounting date: date 3
Confirm the bill

Issue

Check the bill journal items. Price difference account is debited with 4000 czk.
This amount is not correct.
The valuation layer price unit is 100$
At the receipt date the price unit should be 2500czk
The price unit difference 3000-2500 = 500czk
Price difference 500*10 = 5000czk
The issue occur because the price difference unit is converted at bill
accounting date, resulting in 4000czk

opw-3063809

closes odoo/odoo#111199

X-original-commit: bbc0b003f6ff08a50196e07b66083ee7e8896150
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
This commit is contained in:
Andrea Grazioso (agr-odoo)
2023-01-27 14:03:32 +01:00
committed by William Henrotin
parent 5a8c7a17b2
commit a660e8ab3e
2 changed files with 80 additions and 13 deletions
+10 -13
View File
@@ -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
)
@@ -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
'''