[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:
committed by
William Henrotin
parent
5a8c7a17b2
commit
a660e8ab3e
@@ -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
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user