from odoo import api, fields, models class MrpProduction(models.Model): _inherit = 'mrp.production' lot_producing_id = fields.Many2one( 'stock.lot', compute='_compute_lot_producing_id', string='Producing Lot (v18 Compatible)', help="Compatibility field for Odoo 18 modules using lot_producing_id" ) @api.depends('lot_producing_ids') def _compute_lot_producing_id(self): for production in self: production.lot_producing_id = production.lot_producing_ids[:1] def _cal_price(self, consumed_moves): """ Patch to handle multiple finished moves for the same product. Odoo's mrp_account._cal_price uses finished_move.ensure_one(), which crashes if quality checks split the finished move into multiple lines (e.g. Pass/Fail). """ # We only apply this logic if there are multiple finished moves for the MO product finished_moves = self.move_finished_ids.filtered( lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel') and x.quantity > 0) if len(finished_moves) > 1: # If we have multiple moves, we need to bypass the strict ensure_one() in the parent # while still ensuring the cost is calculated correctly. # We call super for EACH move separately by temporarily filtering move_finished_ids # but that's complex since _cal_price is usually called on the whole MO. # Alternative: Since we can't easily change how the super() code reaches finished_move, # we handle the multi-move case here and return True to bypass the crash in super(). # (Note: Odoo's mrp_account._cal_price starts with super()._cal_price(consumed_moves)) # The super() call in mrp_account._cal_price is: # res = super()._cal_price(consumed_moves) # which usually just calculates the price on the move if it's NOT fifo/avco. # Let's try to mimic the logic but for multiple moves. work_center_cost = 0 for work_order in self.workorder_ids: work_center_cost += work_order._cal_cost() total_quantity = sum(m.product_uom._compute_quantity(m.quantity, m.product_id.uom_id) for m in finished_moves) if total_quantity == 0: return super()._cal_price(consumed_moves) total_cost = sum(move.value for move in consumed_moves) + work_center_cost + (self.extra_cost * total_quantity) byproduct_moves = self.move_byproduct_ids.filtered(lambda m: m.state not in ('done', 'cancel') and m.quantity > 0) byproduct_cost_share = sum(byproduct_moves.mapped('cost_share')) # Update price_unit for each finished move shared_price_unit = (total_cost * (1 - byproduct_cost_share / 100)) / total_quantity for move in finished_moves: move.price_unit = shared_price_unit # Also handle byproducts if any for byproduct in byproduct_moves: if byproduct.cost_share > 0 and byproduct.product_id.cost_method in ('fifo', 'average'): byproduct_qty = byproduct.product_uom._compute_quantity(byproduct.quantity, byproduct.product_id.uom_id) if byproduct_qty > 0: byproduct.price_unit = (total_cost * byproduct.cost_share / 100) / byproduct_qty return True return super()._cal_price(consumed_moves)