# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from ast import literal_eval from collections import defaultdict from odoo import fields, models, _, api from odoo.tools import float_round from odoo.exceptions import ValidationError class MrpProduction(models.Model): _inherit = 'mrp.production' def _get_moves_raw_values(self): moves = [] for production in self: if not production.bom_id: continue factor = production.product_uom_id._compute_quantity(production.product_qty, production.bom_id.product_uom_id) / production.bom_id.product_qty _boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id, never_attribute_values=production.never_product_template_attribute_value_ids) for bom_line, line_data in lines: # if bom_line.child_bom_id and bom_line.child_bom_id.type == 'phantom' or\ # bom_line.product_id.type != 'consu': if bom_line.child_bom_id and bom_line.child_bom_id.type == 'phantom': continue operation = bom_line.operation_id.id or line_data['parent_line'] and line_data['parent_line'].operation_id.id moves.append(production._get_move_raw_values( bom_line.product_id, line_data['qty'], bom_line.product_uom_id, operation, bom_line )) return moves def action_confirm(self): res = super(MrpProduction,self).action_confirm() for rec in self: rec.product_id.button_bom_cost() return res class MrpBomLine(models.Model): _inherit = 'mrp.bom.line' unit_cost = fields.Float(compute='_compute_product_price', digits=(16, 2), string="Unit Cost") total_price = fields.Float(compute='_compute_total_price', string="Total Cost") is_service_product = fields.Float(compute='_compute_is_service_product', default=False) @api.depends('product_id') def _compute_product_price(self): for rec in self: if rec.product_id: # Get product cost in the company context standard_price = rec.product_id.with_company(rec.env.company).standard_price # Convert cost from product's UoM to BoM line UoM unit_cost = rec.product_id.uom_id._compute_price( standard_price, rec.product_uom_id ) rec.unit_cost = unit_cost else: rec.unit_cost = 0.0 @api.depends('unit_cost', 'product_qty') def _compute_total_price(self): for rec in self: if rec.product_id: # Get product cost in the company context standard_price = rec.product_id.with_company(rec.env.company).standard_price # Convert cost from product's UoM to BoM line UoM unit_cost = rec.product_id.uom_id._compute_price( standard_price, rec.product_uom_id ) else: unit_cost = 0.0 rec.total_price = unit_cost * rec.product_qty @api.depends('product_id') def _compute_is_service_product(self): for rec in self: rec.is_service_product = True if rec.product_id.type == 'service' else False @api.constrains('product_id', 'bom_id') @api.onchange('product_id', 'bom_id') def _check_unique_product_in_bom(self): for line in self: # Find all lines in the same BoM with the same product duplicate_lines = line.bom_id.bom_line_ids.filtered( lambda l: l.product_id == line.product_id ) if len(duplicate_lines) > 1: raise ValidationError( f"The product '{line.product_id.display_name}' is already used in this BoM." ) class StockMove(models.Model): _inherit = 'stock.move' is_service_product = fields.Float(compute='_compute_is_service_product', default=False) @api.depends('product_id') def _compute_is_service_product(self): for rec in self: rec.is_service_product = True if rec.product_id.type == 'service' else False