diff --git a/addons/sale_mrp/sale_mrp.py b/addons/sale_mrp/sale_mrp.py index 35bb2c539a8..8dc89814a3c 100644 --- a/addons/sale_mrp/sale_mrp.py +++ b/addons/sale_mrp/sale_mrp.py @@ -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 diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index f91e1fc40cd..5c5e789f63a 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -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'