From 58ac6db1fe75766e3cac325c8d7d13ca8180451d Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Mon, 21 Feb 2022 11:01:02 +0000 Subject: [PATCH] [MOV] sale_mrp: split sale.py (2) Done in 2 commits to keep code ownership and git blames Part-of: odoo/odoo#85001 --- addons/sale_mrp/models/__init__.py | 3 +- addons/sale_mrp/models/sale_order.py | 107 ---------------------- addons/sale_mrp/models/sale_order_line.py | 51 +---------- 3 files changed, 3 insertions(+), 158 deletions(-) diff --git a/addons/sale_mrp/models/__init__.py b/addons/sale_mrp/models/__init__.py index db9a5396202..27bbff39492 100644 --- a/addons/sale_mrp/models/__init__.py +++ b/addons/sale_mrp/models/__init__.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import sale from . import account_move from . import mrp_production +from . import sale_order +from . import sale_order_line from . import stock_move diff --git a/addons/sale_mrp/models/sale_order.py b/addons/sale_mrp/models/sale_order.py index 45146b92046..c74e7237aaf 100644 --- a/addons/sale_mrp/models/sale_order.py +++ b/addons/sale_mrp/models/sale_order.py @@ -52,110 +52,3 @@ class SaleOrder(models.Model): rendering_context_values = super()._get_additional_order_page_values() rendering_context_values['mrp_production_ids'] = self.mrp_production_ids return rendering_context_values - -class SaleOrderLine(models.Model): - _inherit = 'sale.order.line' - - @api.depends('product_uom_qty', 'qty_delivered', 'product_id', 'state') - def _compute_qty_to_deliver(self): - """The inventory widget should now be visible in more cases if the product is consumable.""" - super(SaleOrderLine, self)._compute_qty_to_deliver() - for line in self: - # Hide the widget for kits since forecast doesn't support them. - boms = self.env['mrp.bom'] - if line.state == 'sale': - boms = line.move_ids.mapped('bom_line_id.bom_id') - elif line.state in ['draft', 'sent'] and line.product_id: - boms = boms._bom_find(line.product_id, company_id=line.company_id.id, bom_type='phantom')[line.product_id] - relevant_bom = boms.filtered(lambda b: b.type == 'phantom' and - (b.product_id == line.product_id or - (b.product_tmpl_id == line.product_id.product_tmpl_id and not b.product_id))) - if relevant_bom: - line.display_qty_widget = False - continue - if line.state == 'draft' and line.product_type == 'consu': - components = line.product_id.get_components() - if components and components != [line.product_id.id]: - line.display_qty_widget = True - - def _compute_qty_delivered(self): - super(SaleOrderLine, self)._compute_qty_delivered() - for order_line in self: - if order_line.qty_delivered_method == 'stock_move': - boms = order_line.move_ids.filtered(lambda m: m.state != 'cancel').mapped('bom_line_id.bom_id') - dropship = False - if not boms and any(m._is_dropshipped() for m in order_line.move_ids): - boms = boms._bom_find(order_line.product_id, company_id=order_line.company_id.id, bom_type='phantom')[order_line.product_id] - dropship = True - # We fetch the BoMs of type kits linked to the order_line, - # the we keep only the one related to the finished produst. - # This bom should be the only one since bom_line_id was written on the moves - relevant_bom = boms.filtered(lambda b: b.type == 'phantom' and - (b.product_id == order_line.product_id or - (b.product_tmpl_id == order_line.product_id.product_tmpl_id and not b.product_id))) - if relevant_bom: - # In case of dropship, we use a 'all or nothing' policy since 'bom_line_id' was - # not written on a move coming from a PO. - # FIXME: if the components of a kit have different suppliers, multiple PO - # are generated. If one PO is confirmed and all the others are in draft, receiving - # the products for this PO will set the qty_delivered. We might need to check the - # state of all PO as well... but sale_mrp doesn't depend on purchase. - if dropship: - moves = order_line.move_ids.filtered(lambda m: m.state != 'cancel') - if moves and all(m.state == 'done' for m in moves): - order_line.qty_delivered = order_line.product_uom_qty - else: - order_line.qty_delivered = 0.0 - continue - moves = order_line.move_ids.filtered(lambda m: m.state == 'done' and not m.scrapped) - filters = { - 'incoming_moves': lambda m: m.location_dest_id.usage == 'customer' and (not m.origin_returned_move_id or (m.origin_returned_move_id and m.to_refund)), - 'outgoing_moves': lambda m: m.location_dest_id.usage != 'customer' and m.to_refund - } - order_qty = order_line.product_uom._compute_quantity(order_line.product_uom_qty, relevant_bom.product_uom_id) - qty_delivered = moves._compute_kit_quantities(order_line.product_id, order_qty, relevant_bom, filters) - order_line.qty_delivered = relevant_bom.product_uom_id._compute_quantity(qty_delivered, order_line.product_uom) - - # If no relevant BOM is found, fall back on the all-or-nothing policy. This happens - # when the product sold is made only of kits. In this case, the BOM of the stock moves - # do not correspond to the product sold => no relevant BOM. - elif boms: - # if the move is ingoing, the product **sold** has delivered qty 0 - if all(m.state == 'done' and m.location_dest_id.usage == 'customer' for m in order_line.move_ids): - order_line.qty_delivered = order_line.product_uom_qty - else: - order_line.qty_delivered = 0.0 - - def _get_bom_component_qty(self, bom): - bom_quantity = self.product_id.uom_id._compute_quantity(1, bom.product_uom_id, rounding_method='HALF-UP') - boms, lines = bom.explode(self.product_id, bom_quantity) - components = {} - for line, line_data in lines: - product = line.product_id.id - uom = line.product_uom_id - qty = line_data['qty'] - if components.get(product, False): - if uom.id != components[product]['uom']: - from_uom = uom - to_uom = self.env['uom.uom'].browse(components[product]['uom']) - qty = from_uom._compute_quantity(qty, to_uom) - components[product]['qty'] += qty - else: - # To be in the uom reference of the product - to_uom = self.env['product.product'].browse(product).uom_id - if uom.id != to_uom.id: - from_uom = uom - qty = from_uom._compute_quantity(qty, to_uom) - components[product] = {'qty': qty, 'uom': to_uom.id} - return components - - def _get_qty_procurement(self, previous_product_uom_qty=False): - self.ensure_one() - # Specific case when we change the qty on a SO 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']._bom_find(self.product_id, bom_type='phantom')[self.product_id] - if bom: - return previous_product_uom_qty and previous_product_uom_qty.get(self.id, 0.0) or self.qty_delivered - return super(SaleOrderLine, self)._get_qty_procurement(previous_product_uom_qty=previous_product_uom_qty) diff --git a/addons/sale_mrp/models/sale_order_line.py b/addons/sale_mrp/models/sale_order_line.py index 45146b92046..af82273ea73 100644 --- a/addons/sale_mrp/models/sale_order_line.py +++ b/addons/sale_mrp/models/sale_order_line.py @@ -1,58 +1,9 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models, _ +from odoo import api, models -class SaleOrder(models.Model): - _inherit = 'sale.order' - - mrp_production_count = fields.Integer( - "Count of MO generated", - compute='_compute_mrp_production_ids', - groups='mrp.group_mrp_user') - mrp_production_ids = fields.Many2many( - 'mrp.production', - compute='_compute_mrp_production_ids', - string='Manufacturing orders associated with this sales order.', - groups='mrp.group_mrp_user') - - @api.depends('procurement_group_id.stock_move_ids.created_production_id.procurement_group_id.mrp_production_ids') - def _compute_mrp_production_ids(self): - data = self.env['procurement.group'].read_group([('sale_id', 'in', self.ids)], ['ids:array_agg(id)'], ['sale_id']) - mrp_productions = dict() - for item in data: - procurement_groups = self.env['procurement.group'].browse(item['ids']) - mrp_productions[item['sale_id'][0]] = procurement_groups.stock_move_ids.created_production_id.procurement_group_id.mrp_production_ids | procurement_groups.mrp_production_ids - for sale in self: - mrp_production_ids = mrp_productions.get(sale.id, self.env['mrp.production']) - sale.mrp_production_count = len(mrp_production_ids) - sale.mrp_production_ids = mrp_production_ids - - def action_view_mrp_production(self): - self.ensure_one() - action = { - 'res_model': 'mrp.production', - 'type': 'ir.actions.act_window', - } - if len(self.mrp_production_ids) == 1: - action.update({ - 'view_mode': 'form', - 'res_id': self.mrp_production_ids.id, - }) - else: - action.update({ - 'name': _("Manufacturing Orders Generated by %s", self.name), - 'domain': [('id', 'in', self.mrp_production_ids.ids)], - 'view_mode': 'tree,form', - }) - return action - - def _get_additional_order_page_values(self): - rendering_context_values = super()._get_additional_order_page_values() - rendering_context_values['mrp_production_ids'] = self.mrp_production_ids - return rendering_context_values - class SaleOrderLine(models.Model): _inherit = 'sale.order.line'