[FIX] sale_mrp, sale_stock: _get_anglo_saxon_price_unit with a kit

When a kit is set with a valuation = "real_time" and invoice_policy = "delivery",
the price unit of the kit computed for the Expense Account in the the journal of
entries must be the sum of the average price of each component of this kit.

Used case:

Create a kit001 with:
*General information:
   -Product Type=Stockable Product
   -Invoicing Policy=Delivered Quantities
   -Sale Price=100$
   -Cost Price=80$
*Inventory:
   -Route=Manufacture
   -Internal Category=All/Sealable
*its product category set with
   -Expense Account=210000 Cost of Goods Sold
   -Costing Method=Standard Price
   -Inventory Valuation=Perpetual(automated)
*a bom of two components child001 and child002

Create child001 with:
*General information:
   -Product Type=Stockable Product
   -Invoicing Policy=Delivered Quantities
   -Sale Price=100$
   -Cost Price=70$
*Inventory:
   -Route=Buy
   -Internal Category=All
*its product category set with
   -Expense Account=210000 Cost of Goods Sold
   -Costing Method=Real Price
   -Inventory Valuation=Perpetual(automated)

Create child002 with:
*General information:
   -Product Type=Stockable Product
   -Invoicing Policy=Delivered Quantities
   -Sale Price=100$
   -Cost Price=10$
*Inventory:
   -Route=Buy
   -Internal Category=All
*its product category set with
   -Expense Account=210000 Cost of Goods Sold
   -Costing Method=Real Price
   -Inventory Valuation=Perpetual(automated)

Create a SO with:
   -one line with the kit001

Confirm the SO

Validate the Delivery

Create and view the invoices
   -per invoicable lines(deduct down payments)

Validate the invoice

Result:

Before the fix: in the entry created for this invoice, the debit for the kit001 in Cost of Goods Sold
was equal to 70 instead of 80.

After the fix: the debit for the kit001 in Cost of Goods Sold
was equal to 80(the sum of the average price of each component of the kit001).

opw:674608
This commit is contained in:
Goffin Simon
2016-05-02 11:37:00 +02:00
parent 02371e8831
commit 3bcfac7d99
2 changed files with 83 additions and 17 deletions
+62
View File
@@ -60,6 +60,31 @@ class SaleOrderLine(models.Model):
return 0.0
return super(SaleOrderLine, self)._get_delivered_qty()
@api.multi
def _get_bom_component_qty(self, bom):
product_uom_qty_bom = self.env['product.uom']._compute_qty_obj(self.product_uom, self.product_uom_qty, bom.product_uom)
bom_exploded = self.env['mrp.bom']._bom_explode(bom, self.product_id, product_uom_qty_bom)[0]
components = {}
for bom_line in bom_exploded:
product = bom_line['product_id']
uom = bom_line['product_uom']
qty = bom_line['product_qty']
if components.get(product, False):
if uom != components[product]['uom']:
from_uom_id = uom
to_uom_id = components[product]['uom']
qty = self.env['product.uom']._compute_qty(from_uom_id, qty, to_uom_id=to_uom_id)
components[product]['qty'] += qty
else:
# To be in the uom reference of the product
to_uom = self.env['product.product'].browse([product])[0].uom_id.id
if uom != to_uom:
qty = self.env['product.uom']._compute_qty(uom, qty, to_uom_id=to_uom)
components[product] = {'qty': qty, 'uom': to_uom}
return components
@api.multi
def _prepare_order_line_procurement(self, group_id=False):
vals = super(SaleOrderLine, self)._prepare_order_line_procurement(group_id=group_id)
@@ -85,3 +110,40 @@ class StockMove(models.Model):
"""
property_ids = move.procurement_id.sale_line_id.property_ids.ids
return super(StockMove, self.with_context(property_ids=property_ids))._action_explode(move)
class AccountInvoiceLine(models.Model):
_inherit = "account.invoice.line"
def _get_anglo_saxon_price_unit(self):
price_unit = super(AccountInvoiceLine, self)._get_anglo_saxon_price_unit()
# in case of anglo saxon with a product configured as invoiced based on delivery, with perpetual
# valuation and real price costing method, we must find the real price for the cost of good sold
uom_obj = self.env['product.uom']
if self.product_id.invoice_policy == "delivery":
for s_line in self.sale_line_ids:
# qtys already invoiced
qty_done = sum([uom_obj._compute_qty_obj(x.uom_id, x.quantity, x.product_id.uom_id) for x in s_line.invoice_lines if x.invoice_id.state in ('open', 'paid')])
quantity = uom_obj._compute_qty_obj(self.uom_id, self.quantity, self.product_id.uom_id)
# Put moves in fixed order by date executed
moves = self.env['stock.move']
for procurement in s_line.procurement_ids:
moves |= procurement.move_ids
moves.sorted(lambda x: x.date)
# Go through all the moves and do nothing until you get to qty_done
# Beyond qty_done we need to calculate the average of the price_unit
# on the moves we encounter.
bom = s_line.product_id.product_tmpl_id.bom_ids and s_line.product_id.product_tmpl_id.bom_ids[0]
if bom.type == 'phantom':
average_price_unit = 0
components = s_line._get_bom_component_qty(bom)
for product_id in components.keys():
factor = components[product_id]['qty']
prod_moves = [m for m in moves if m.product_id.id == product_id]
prod_qty_done = factor * qty_done
prod_quantity = factor * quantity
average_price_unit += factor * self._compute_average_price(prod_qty_done, prod_quantity, prod_moves)
price_unit = average_price_unit or price_unit
price_unit = uom_obj._compute_qty_obj(self.uom_id, price_unit, self.product_id.uom_id, round=False)
return price_unit
else:
return price_unit
+21 -17
View File
@@ -327,27 +327,31 @@ class AccountInvoiceLine(models.Model):
# Go through all the moves and do nothing until you get to qty_done
# Beyond qty_done we need to calculate the average of the price_unit
# on the moves we encounter.
average_price_unit = 0
qty_delivered = 0
invoiced_qty = 0
for move in moves:
if move.state != 'done':
continue
invoiced_qty += move.product_qty
if invoiced_qty <= qty_done:
continue
qty_to_consider = move.product_qty
if invoiced_qty - move.product_qty < qty_done:
qty_to_consider = invoiced_qty - qty_done
qty_to_consider = min(qty_to_consider, quantity - qty_delivered)
qty_delivered += qty_to_consider
average_price_unit = (average_price_unit * (qty_delivered - qty_to_consider) + move.price_unit * qty_to_consider) / qty_delivered
if qty_delivered == quantity:
break
average_price_unit = self._compute_average_price(qty_done, quantity, moves)
price_unit = average_price_unit or price_unit
price_unit = uom_obj._compute_qty_obj(self.uom_id, price_unit, self.product_id.uom_id, round=False)
return price_unit
def _compute_average_price(self, qty_done, quantity, moves):
average_price_unit = 0
qty_delivered = 0
invoiced_qty = 0
for move in moves:
if move.state != 'done':
continue
invoiced_qty += move.product_qty
if invoiced_qty <= qty_done:
continue
qty_to_consider = move.product_qty
if invoiced_qty - move.product_qty < qty_done:
qty_to_consider = invoiced_qty - qty_done
qty_to_consider = min(qty_to_consider, quantity - qty_delivered)
qty_delivered += qty_to_consider
average_price_unit = (average_price_unit * (qty_delivered - qty_to_consider) + move.price_unit * qty_to_consider) / qty_delivered
if qty_delivered == quantity:
break
return average_price_unit
class ProductProduct(models.Model):
_inherit = 'product.product'