# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from collections import defaultdict from odoo import api, fields, models, _ from odoo.tools import OrderedSet class PurchaseOrder(models.Model): _inherit = 'purchase.order' mrp_production_count = fields.Integer( "Count of MO Source", compute='_compute_mrp_production_count', groups='mrp.group_mrp_user') @api.depends('reference_ids', 'reference_ids.production_ids') def _compute_mrp_production_count(self): for purchase in self: purchase.mrp_production_count = len(purchase._get_mrp_productions()) def _get_mrp_productions(self, **kwargs): return self.reference_ids.production_ids def action_view_mrp_productions(self): self.ensure_one() mrp_production_ids = self._get_mrp_productions().ids action = { 'res_model': 'mrp.production', 'type': 'ir.actions.act_window', } if len(mrp_production_ids) == 1: action.update({ 'view_mode': 'form', 'res_id': mrp_production_ids[0], }) else: action.update({ 'name': _("Manufacturing Source of %s", self.name), 'domain': [('id', 'in', mrp_production_ids)], 'view_mode': 'list,form', }) return action class PurchaseOrderLine(models.Model): _inherit = 'purchase.order.line' def _compute_kit_quantities_from_moves(self, moves, kit_bom): self.ensure_one() moves_to_consider = moves.filtered(lambda m: m.state == 'done' and m.location_dest_usage != 'inventory') order_qty = self.product_uom_id._compute_quantity(self.product_uom_qty, kit_bom.product_uom_id) filters = { 'incoming_moves': lambda m: m._is_incoming() and (not m.origin_returned_move_id or (m.origin_returned_move_id and m.to_refund)), 'outgoing_moves': lambda m: m._is_outgoing() and m.to_refund, } return moves_to_consider._compute_kit_quantities(self.product_id, order_qty, kit_bom, filters) def _prepare_qty_received(self): kit_invoiced_qties = defaultdict(float) kit_lines = self.env['purchase.order.line'] lines_stock = self.filtered(lambda l: l.qty_received_method == 'stock_moves' and l.move_ids and l.state != 'cancel') product_by_company = defaultdict(OrderedSet) for line in lines_stock: product_by_company[line.company_id].add(line.product_id.id) kits_by_company = { company: self.env['mrp.bom']._bom_find(self.env['product.product'].browse(product_ids), company_id=company.id, bom_type='phantom') for company, product_ids in product_by_company.items() } for line in lines_stock: kit_bom = kits_by_company[line.company_id].get(line.product_id) if kit_bom: kit_invoiced_qties[line] = line._compute_kit_quantities_from_moves(line.move_ids, kit_bom) kit_lines += line invoiced_qties = super(PurchaseOrderLine, self - kit_lines)._prepare_qty_received() invoiced_qties.update(kit_invoiced_qties) return invoiced_qties def _prepare_stock_moves(self, picking): res = super()._prepare_stock_moves(picking) if len(self.order_id.reference_ids.move_ids.production_group_id) == 1: for re in res: re['production_group_id'] = self.order_id.reference_ids.move_ids.production_group_id.id sale_line_product = self._get_sale_order_line_product() if sale_line_product: bom = self.env['mrp.bom']._bom_find(self.env['product.product'].browse(sale_line_product.id), company_id=picking.company_id.id, bom_type='phantom') # Was a kit sold? bom_kit = bom.get(sale_line_product) if bom_kit: _dummy, bom_sub_lines = bom_kit.explode(sale_line_product, self.sale_line_id.product_uom_qty) bom_kit_component = {line['product_id'].id: line.id for line, _ in bom_sub_lines} # Find the sml for the kit component for vals in res: if vals['product_id'] in bom_kit_component: vals['bom_line_id'] = bom_kit_component[vals['product_id']] return res def _get_upstream_documents_and_responsibles(self, visited): return [(self.order_id, self.order_id.user_id, visited)] def _get_qty_procurement(self): self.ensure_one() # Specific case when we change the qty on a PO for a kit product. # We don't try to be too smart and keep a simple approach: we compare the quantity before # and after update, and return the difference. We don't take into account what was already # sent, or any other exceptional case. bom = self.env['mrp.bom'].sudo()._bom_find(self.product_id, bom_type='phantom')[self.product_id] if bom and 'previous_product_qty' in self.env.context: return self.env.context['previous_product_qty'].get(self.id, 0.0) return super()._get_qty_procurement() def _get_move_dests_initial_demand(self, move_dests): kit_bom = self.env['mrp.bom']._bom_find(self.product_id, bom_type='phantom')[self.product_id] if kit_bom: filters = {'incoming_moves': lambda m: True, 'outgoing_moves': lambda m: False} return move_dests._compute_kit_quantities(self.product_id, self.product_qty, kit_bom, filters) return super()._get_move_dests_initial_demand(move_dests) def _get_sale_order_line_product(self): return False