[IMP] purchase_stock, stock_account: handle invoice before receipt

In some buisness case when the invoice is done before the receipt, the
`stock.valuation.layer` price unit will be taken on the invoice rather
than the `purchase.order.line`

Part-of: odoo/odoo#99411
This commit is contained in:
Arnold Moyaux
2022-09-12 13:49:17 +02:00
parent ccc1fa5d37
commit 9f046d538f
2 changed files with 37 additions and 13 deletions
+35 -13
View File
@@ -29,10 +29,33 @@ class StockMove(models.Model):
def _get_price_unit(self):
""" Returns the unit price for the move"""
self.ensure_one()
if self.purchase_line_id and self.product_id.id:
price_unit_prec = self.env['decimal.precision'].precision_get('Product Price')
line = self.purchase_line_id
order = line.order_id
if not self.purchase_line_id or not self.product_id.id:
return super(StockMove, self)._get_price_unit()
price_unit_prec = self.env['decimal.precision'].precision_get('Product Price')
line = self.purchase_line_id
order = line.order_id
received_qty = line.qty_received
if self.state == 'done':
received_qty -= self.product_uom._compute_quantity(self.quantity_done, line.product_uom)
if line.qty_invoiced > received_qty:
move_layer = line.move_ids.stock_valuation_layer_ids
invoiced_layer = line.invoice_lines.stock_valuation_layer_ids
receipt_value = sum(move_layer.mapped('value')) + sum(invoiced_layer.mapped('value'))
invoiced_value = 0
invoiced_qty = 0
for invoice_line in line.invoice_lines:
if invoice_line.tax_ids:
invoiced_value += invoice_line.tax_ids.with_context(round=False).compute_all(
invoice_line.price_unit, currency=invoice_line.account_id.currency_id, quantity=invoice_line.quantity)['total_void']
else:
invoiced_value += invoice_line.price_unit * invoice_line.quantity
invoiced_qty += invoice_line.product_uom_id._compute_quantity(invoice_line.quantity, line.product_id.uom_id)
# TODO currency check
remaining_value = invoiced_value - receipt_value
# TODO qty_received in product uom
remaining_qty = invoiced_qty - line.product_uom._compute_quantity(received_qty, line.product_id.uom_id)
price_unit = float_round(remaining_value / remaining_qty, precision_digits=price_unit_prec)
else:
price_unit = line.price_unit
if line.taxes_id:
qty = line.product_qty or 1
@@ -40,15 +63,14 @@ class StockMove(models.Model):
price_unit = float_round(price_unit / qty, precision_digits=price_unit_prec)
if line.product_uom.id != line.product_id.uom_id.id:
price_unit *= line.product_uom.factor / line.product_id.uom_id.factor
if order.currency_id != order.company_id.currency_id:
# The date must be today, and not the date of the move since the move move is still
# in assigned state. However, the move date is the scheduled date until move is
# done, then date of actual move processing. See:
# https://github.com/odoo/odoo/blob/2f789b6863407e63f90b3a2d4cc3be09815f7002/addons/stock/models/stock_move.py#L36
price_unit = order.currency_id._convert(
price_unit, order.company_id.currency_id, order.company_id, fields.Date.context_today(self), round=False)
return price_unit
return super(StockMove, self)._get_price_unit()
if order.currency_id != order.company_id.currency_id:
# The date must be today, and not the date of the move since the move move is still
# in assigned state. However, the move date is the scheduled date until move is
# done, then date of actual move processing. See:
# https://github.com/odoo/odoo/blob/2f789b6863407e63f90b3a2d4cc3be09815f7002/addons/stock/models/stock_move.py#L36
price_unit = order.currency_id._convert(
price_unit, order.company_id.currency_id, order.company_id, fields.Date.context_today(self), round=False)
return price_unit
def _generate_valuation_lines_data(self, partner_id, qty, debit_value, credit_value, debit_account_id, credit_account_id, svl_id, description):
""" Overridden from stock_account to support amount_currency on valuation lines generated from po
@@ -247,6 +247,8 @@ class AccountMove(models.Model):
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
stock_valuation_layer_ids = fields.One2many('stock.valuation.layer', 'account_move_line_id', string='Stock Valuation Layer')
def _compute_account_id(self):
super()._compute_account_id()
input_lines = self.filtered(lambda line: (