quality_patch/models/mrp_production.py
2026-02-11 10:28:28 +07:00

71 lines
3.6 KiB
Python

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)