[FIX] stock_account/sale_stock: correctly valuate the product in below use case
Below is the case of product with automated valuation: - You bought 8 units @ 10$. - Then you deliver 10 units of the product. - You assumed the missing 2 should go out at a value of 10$ but you are not sure yet as it hasn't been bought in Odoo yet. - Afterwards, you buy missing 2 units of the same product at 12$. In this case the product has been undervalued when delivered without stock, the vacuum entry is the following one (this entry already takes place): Account | Debit | Credit =================================================== Stock Valuation | 0.00 | 4.00 Stock Interim (Delivered) | 4.00 | 0.00 So, on delivering product with different price, We should create additional journal items like: Account | Debit | Credit =================================================== Stock Interim (Delivered) | 0.00 | 4.00 Expenses Revaluation | 4.00 | 0.00 closes odoo/odoo#52396 Task: 2224025 Closes: #52396 Signed-off-by: Simon Lejeune (sle) <sle@openerp.com>
This commit is contained in:
committed by
Ravi Gohil
parent
c997700181
commit
12b4a24b97
@@ -1009,3 +1009,65 @@ class TestAngloSaxonValuation(SavepointCase):
|
||||
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.expense_account)
|
||||
self.assertEqual(cogs_aml.debit, 56)
|
||||
self.assertEqual(cogs_aml.credit, 0)
|
||||
|
||||
def test_fifo_delivered_invoice_post_delivery_4(self):
|
||||
"""Receive 8@10. Sale order 10@12. Deliver and also invoice it without receiving the 2 missing.
|
||||
Now, receive 2@12. Make sure price difference is correctly reflected in expense account."""
|
||||
self.product.categ_id.property_cost_method = 'fifo'
|
||||
self.product.invoice_policy = 'delivery'
|
||||
self.product.standard_price = 10
|
||||
|
||||
in_move_1 = self.env['stock.move'].create({
|
||||
'name': 'a',
|
||||
'product_id': self.product.id,
|
||||
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||||
'location_dest_id': self.stock_location.id,
|
||||
'product_uom': self.product.uom_id.id,
|
||||
'product_uom_qty': 8,
|
||||
'price_unit': 10,
|
||||
})
|
||||
in_move_1._action_confirm()
|
||||
in_move_1.quantity_done = 8
|
||||
in_move_1._action_done()
|
||||
|
||||
# Create and confirm a sale order for 10@12
|
||||
sale_order = self.env['sale.order'].create({
|
||||
'partner_id': self.customer.id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'name': self.product.name,
|
||||
'product_id': self.product.id,
|
||||
'product_uom_qty': 10.0,
|
||||
'product_uom': self.product.uom_id.id,
|
||||
'price_unit': 12,
|
||||
'tax_id': False, # no love taxes amls
|
||||
})],
|
||||
})
|
||||
sale_order.action_confirm()
|
||||
|
||||
# Deliver 10
|
||||
sale_order.picking_ids.move_lines.quantity_done = 10
|
||||
sale_order.picking_ids.button_validate()
|
||||
|
||||
# Invoice the sale order.
|
||||
invoice = sale_order.with_context(default_journal_id=self.journal_sale.id)._create_invoices()
|
||||
invoice.post()
|
||||
|
||||
# Make the second receipt
|
||||
in_move_2 = self.env['stock.move'].create({
|
||||
'name': 'a',
|
||||
'product_id': self.product.id,
|
||||
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||||
'location_dest_id': self.stock_location.id,
|
||||
'product_uom': self.product.uom_id.id,
|
||||
'product_uom_qty': 2,
|
||||
'price_unit': 12,
|
||||
})
|
||||
in_move_2._action_confirm()
|
||||
in_move_2.quantity_done = 2
|
||||
in_move_2._action_done()
|
||||
|
||||
# check the last anglo saxon move line
|
||||
revalued_anglo_expense_amls = sale_order.picking_ids.mapped('move_lines.stock_valuation_layer_ids')[-1].stock_move_id.account_move_ids[-1].mapped('line_ids')
|
||||
revalued_cogs_aml = revalued_anglo_expense_amls.filtered(lambda aml: aml.account_id == self.expense_account)
|
||||
self.assertEqual(revalued_cogs_aml.debit, 4, 'Price difference should have correctly reflected in expense account.')
|
||||
|
||||
@@ -437,6 +437,62 @@ class ProductProduct(models.Model):
|
||||
vacuum_svl.stock_move_id._account_entry_move(
|
||||
vacuum_svl.quantity, vacuum_svl.description, vacuum_svl.id, vacuum_svl.value
|
||||
)
|
||||
# Create the related expense entry
|
||||
self._create_fifo_vacuum_anglo_saxon_expense_entry(vacuum_svl, svl_to_vacuum)
|
||||
|
||||
def _create_fifo_vacuum_anglo_saxon_expense_entry(self, vacuum_svl, svl_to_vacuum):
|
||||
""" When product is delivered and invoiced while you don't have units in stock anymore, there are chances of that
|
||||
product getting undervalued/overvalued. So, we should nevertheless take into account the fact that the product has
|
||||
already been delivered and invoiced to the customer by posting the value difference in the expense account also.
|
||||
Consider the below case where product is getting undervalued:
|
||||
|
||||
You bought 8 units @ 10$ -> You have a stock valuation of 8 units, unit cost 10.
|
||||
Then you deliver 10 units of the product.
|
||||
You assumed the missing 2 should go out at a value of 10$ but you are not sure yet as it hasn't been bought in Odoo yet.
|
||||
Afterwards, you buy missing 2 units of the same product at 12$ instead of expected 10$.
|
||||
In case the product has been undervalued when delivered without stock, the vacuum entry is the following one (this entry already takes place):
|
||||
|
||||
Account | Debit | Credit
|
||||
===================================================
|
||||
Stock Valuation | 0.00 | 4.00
|
||||
Stock Interim (Delivered) | 4.00 | 0.00
|
||||
|
||||
So, on delivering product with different price, We should create additional journal items like:
|
||||
Account | Debit | Credit
|
||||
===================================================
|
||||
Stock Interim (Delivered) | 0.00 | 4.00
|
||||
Expenses Revaluation | 4.00 | 0.00
|
||||
"""
|
||||
if not vacuum_svl.company_id.anglo_saxon_accounting or not svl_to_vacuum.stock_move_id._is_out():
|
||||
return False
|
||||
AccountMove = self.env['account.move'].sudo()
|
||||
account_move_lines = svl_to_vacuum.account_move_id.line_ids
|
||||
# Find related customer invoice where product is delivered while you don't have units in stock anymore
|
||||
reconciled_line_ids = list(set(account_move_lines._reconciled_lines()) - set(account_move_lines.ids))
|
||||
account_move = AccountMove.search([('line_ids','in', reconciled_line_ids)], limit=1)
|
||||
# If delivered quantity is not invoiced then no need to create this entry
|
||||
if not account_move:
|
||||
return False
|
||||
accounts = svl_to_vacuum.product_id.product_tmpl_id.get_product_accounts(fiscal_pos=account_move.fiscal_position_id)
|
||||
if not accounts.get('stock_output') or not accounts.get('expense'):
|
||||
return False
|
||||
description = "Expenses %s" % (vacuum_svl.description)
|
||||
move_lines = vacuum_svl.stock_move_id._prepare_account_move_line(
|
||||
vacuum_svl.quantity, vacuum_svl.value * -1,
|
||||
accounts['stock_output'].id, accounts['expense'].id,
|
||||
description)
|
||||
new_account_move = AccountMove.sudo().create({
|
||||
'journal_id': accounts['stock_journal'].id,
|
||||
'line_ids': move_lines,
|
||||
'date': self._context.get('force_period_date', fields.Date.context_today(self)),
|
||||
'ref': description,
|
||||
'stock_move_id': vacuum_svl.stock_move_id.id,
|
||||
'move_type': 'entry',
|
||||
})
|
||||
new_account_move.post()
|
||||
to_reconcile_account_move_lines = vacuum_svl.account_move_id.line_ids.filtered(lambda l: not l.reconciled and l.account_id == accounts['stock_output'] and l.account_id.reconcile)
|
||||
to_reconcile_account_move_lines += new_account_move.line_ids.filtered(lambda l: not l.reconciled and l.account_id == accounts['stock_output'] and l.account_id.reconcile)
|
||||
return to_reconcile_account_move_lines.reconcile()
|
||||
|
||||
@api.model
|
||||
def _svl_empty_stock(self, description, product_category=None, product_template=None):
|
||||
|
||||
Reference in New Issue
Block a user