71 lines
3.6 KiB
Python
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)
|