Files
odoo_source/addons/sale_mrp/models/account_move.py
T
Adrien Widart 87dc2bc614 [FIX] stock_account, sale_stock: reverse AML using the returned SM value
When reversing an invoice linked to a sale order, the value of the new
anglo saxon AML should be based on the value of the returned product

To reproduce the issue:
(Need account_accountant,purchase)
1. Create a product category PC:
    - Costing Method: FIFO
    - Inventory Valuation: Automated
2. Create a product P:
    - Type: Storable
    - Product Category: PC
3. For C in [10, 20, 30]:
    - Create a purchase order with 1 x P at cost C
    - Receive P
4. Create a SO with 3 x P
5. Deliver the products one by one
6. Create and Post the invoice INV linked to SO
7. Return the second delivery
8. Add a credit note to INV01:
    - Credit Method: Partial Refund
9. Set the quantity to 1
10. Post the new invoice INV02

Error: The anglo saxon lines are listed in the journal items, which is
correct, but their value is $30 while it should be $20 (i.e., the value
of the returned product)

When delivering the last product, its standard price is updated with the
new value ($30):
https://github.com/odoo/odoo/blob/6b96ed418cb626678f4fa5baec25a903d7a74eda/addons/stock_account/models/product.py#L305-L307
Later on, when posting the credit note, the module gets the anglo saxon
unit price (`_stock_account_get_anglo_saxon_price_unit`). However, it
does not consider that the current account move line is reversing
another one. In the reversing process, the computation of the invoiced
quantity should be based on the invoices lines that are reversing too.
Moreover, when computing the average unit price of the returned product,
the module should use the stock moves that are returning the product.
In the use case, because of the two issued noted above,
`_compute_average_price` does not find any stock valuation layer to
compute the average unit price and uses the fallback, i.e. the standard
price of the product:
https://github.com/odoo/odoo/blob/6b96ed418cb626678f4fa5baec25a903d7a74eda/addons/stock_account/models/product.py#L652-L656
That's the reason why, in the above case, the value is $30 instead of
$20.

Note: a similar use case can be reproduced with a kit

OPW-2646926
OPW-2628215

closes odoo/odoo#82978

X-original-commit: 524d0d5e8817e1c7bcc99204c4cbb90fcfb6074c
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
2022-01-18 15:25:51 +00:00

36 lines
2.1 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
def _stock_account_get_anglo_saxon_price_unit(self):
price_unit = super(AccountMoveLine, self)._stock_account_get_anglo_saxon_price_unit()
so_line = self.sale_line_ids and self.sale_line_ids[-1] or False
if so_line:
bom = (so_line.product_id.variant_bom_ids or so_line.product_id.product_tmpl_id.bom_ids).filtered(
lambda b: not b.company_id or b.company_id == so_line.company_id
)[:1]
if bom and bom.type == 'phantom':
is_line_reversing = bool(self.move_id.reversed_entry_id)
qty_to_invoice = self.product_uom_id._compute_quantity(self.quantity, self.product_id.uom_id)
posted_invoice_lines = so_line.invoice_lines.filtered(lambda l: l.move_id.state == 'posted' and bool(l.move_id.reversed_entry_id) == is_line_reversing)
qty_invoiced = sum([x.product_uom_id._compute_quantity(x.quantity, x.product_id.uom_id) for x in posted_invoice_lines])
moves = so_line.move_ids
average_price_unit = 0
components_qty = so_line._get_bom_component_qty(bom)
storable_components = self.env['product.product'].search([('id', 'in', list(components_qty.keys())), ('type', '=', 'product')])
for product in storable_components:
factor = components_qty[product.id]['qty']
prod_moves = moves.filtered(lambda m: m.product_id == product)
prod_qty_invoiced = factor * qty_invoiced
prod_qty_to_invoice = factor * qty_to_invoice
product = product.with_company(self.company_id)
average_price_unit += factor * product._compute_average_price(prod_qty_invoiced, prod_qty_to_invoice, prod_moves, is_returned=is_line_reversing)
price_unit = average_price_unit / bom.product_qty or price_unit
price_unit = self.product_id.uom_id._compute_price(price_unit, self.product_uom_id)
return price_unit