From 5e2d48fbfa8f82530151da9d35ecbd45da5b5449 Mon Sep 17 00:00:00 2001 From: clesgow Date: Fri, 3 Jun 2022 14:04:38 +0000 Subject: [PATCH] [REF] mrp_subcontracting: Adapt subcontracting for BoM Overview The 'Structure & Cost' report has been changed to 'Overview', changing some of its functionnalities. The override previously done in mrp_subcontracting must be adapted to match the changes done in the mrp module. Also, the report has been converted to OWL in the process, requiring changes in the overriden files as well. Finally, the now obsolete code only related to the old 'Structure & Cost' report has been removed. Task-2628323 Part-of: odoo/odoo#93194 --- addons/mrp_subcontracting/__manifest__.py | 7 +- .../report/mrp_report_bom_structure.py | 107 +++++++++++------- .../report/mrp_report_bom_structure.xml | 33 ------ .../mrp_bom_overview_components_block.js | 9 ++ .../mrp_bom_overview_components_block.xml | 16 +++ .../mrp_bom_overview_line.js | 16 +++ .../mrp_bom_overview_special_line.js | 33 ++++++ .../mrp_bom_overview_special_line.xml | 26 +++++ .../tests/test_subcontracting.py | 21 ++-- 9 files changed, 184 insertions(+), 84 deletions(-) delete mode 100644 addons/mrp_subcontracting/report/mrp_report_bom_structure.xml create mode 100644 addons/mrp_subcontracting/static/src/components/bom_overview_components_block/mrp_bom_overview_components_block.js create mode 100644 addons/mrp_subcontracting/static/src/components/bom_overview_components_block/mrp_bom_overview_components_block.xml create mode 100644 addons/mrp_subcontracting/static/src/components/bom_overview_line/mrp_bom_overview_line.js create mode 100644 addons/mrp_subcontracting/static/src/components/bom_overview_special_line/mrp_bom_overview_special_line.js create mode 100644 addons/mrp_subcontracting/static/src/components/bom_overview_special_line/mrp_bom_overview_special_line.xml diff --git a/addons/mrp_subcontracting/__manifest__.py b/addons/mrp_subcontracting/__manifest__.py index c5ec547e89d..9685c8313fe 100644 --- a/addons/mrp_subcontracting/__manifest__.py +++ b/addons/mrp_subcontracting/__manifest__.py @@ -25,7 +25,6 @@ 'views/subcontracting_portal_templates.xml', 'views/stock_location_views.xml', 'wizard/stock_picking_return_views.xml', - 'report/mrp_report_bom_structure.xml', ], 'demo': [ 'data/mrp_subcontracting_demo.xml', @@ -34,6 +33,12 @@ 'web.assets_tests': [ 'mrp_subcontracting/static/tests/tours/subcontracting_portal_tour.js', ], + 'web.assets_backend': [ + 'mrp_subcontracting/static/src/components/**/*.js', + ], + 'web.assets_qweb': [ + 'mrp_subcontracting/static/**/*.xml', + ], 'mrp_subcontracting.assets_qweb': [ ('include', 'web.assets_qweb'), 'mrp_subcontracting/static/src/subcontracting_portal/subcontracting_portal.xml', diff --git a/addons/mrp_subcontracting/report/mrp_report_bom_structure.py b/addons/mrp_subcontracting/report/mrp_report_bom_structure.py index 84761475bbc..a99f0fac147 100644 --- a/addons/mrp_subcontracting/report/mrp_report_bom_structure.py +++ b/addons/mrp_subcontracting/report/mrp_report_bom_structure.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models, _ - +from odoo import api, models, _ class ReportBomStructure(models.AbstractModel): _inherit = 'report.mrp.report_bom_structure' @@ -19,46 +18,78 @@ class ReportBomStructure(models.AbstractModel): 'level': level or 0 } - def _get_price(self, bom, factor, product): - price = super()._get_price(bom, factor, product) - if bom and bom.type == 'subcontract': - bom_quantity = bom.product_qty * factor - seller = product._select_seller(quantity=bom_quantity, uom_id=bom.product_uom_id, params={'subcontractor_ids': bom.subcontractor_ids}) + def _get_bom_data(self, bom, warehouse, product=False, line_qty=False, bom_line=False, level=0, parent_bom=False, index=0, product_info=False, ignore_stock=False): + res = super()._get_bom_data(bom, warehouse, product, line_qty, bom_line, level, parent_bom, index, product_info, ignore_stock) + if bom.type == 'subcontract' and not self.env.context.get('minimized', False): + seller = res['product']._select_seller(quantity=res['quantity'], uom_id=bom.product_uom_id, params={'subcontractor_ids': bom.subcontractor_ids}) if seller: - price += seller.product_uom._compute_price(seller.price, product.uom_id) * bom_quantity - return price - - def _get_bom(self, bom_id=False, product_id=False, line_qty=False, line_id=False, level=False): - res = super(ReportBomStructure, self)._get_bom(bom_id, product_id, line_qty, line_id, level) - bom = res['bom'] - if bom and bom.type == 'subcontract': - bom_quantity = line_qty - if line_id: - current_line = self.env['mrp.bom.line'].browse(int(line_id)) - bom_quantity = current_line.product_uom_id._compute_quantity(line_qty, bom.product_uom_id) - - seller = res['product']._select_seller(quantity=bom_quantity, uom_id=bom.product_uom_id, params={'subcontractor_ids': bom.subcontractor_ids}) - if seller: - res['subcontracting'] = self._get_subcontracting_line(bom, seller, level, bom_quantity) - res['total'] += res['subcontracting']['bom_cost'] - res['bom_cost'] += res['subcontracting']['bom_cost'] + res['subcontracting'] = self._get_subcontracting_line(bom, seller, level + 1, res['quantity']) + if not self.env.context.get('minimized', False): + res['bom_cost'] += res['subcontracting']['bom_cost'] return res - def _get_sub_lines(self, bom, product_id, line_qty, line_id, level, unfolded_ids, unfolded): - res = super()._get_sub_lines(bom, product_id, line_qty, line_id, level, unfolded_ids, unfolded) - if bom and bom.type == 'subcontract': - product = self.env['product.product'].browse(product_id) + def _get_bom_array_lines(self, data, level, unfolded_ids, unfolded, parent_unfolded): + lines = super()._get_bom_array_lines(data, level, unfolded_ids, unfolded, parent_unfolded) - bom_quantity = line_qty - if line_id: - current_line = self.env['mrp.bom.line'].browse(int(line_id)) - bom_quantity = current_line.product_uom_id._compute_quantity(line_qty, bom.product_uom_id) + if data.get('subcontracting'): + subcontract_info = data['subcontracting'] + lines.append({ + 'name': _("Subcontracting: %s", subcontract_info['name']), + 'type': 'subcontract', + 'uom': False, + 'quantity': subcontract_info['quantity'], + 'bom_cost': subcontract_info['bom_cost'], + 'prod_cost': subcontract_info['prod_cost'], + 'level': subcontract_info['level'], + 'visible': level == 1 or unfolded or parent_unfolded + }) + return lines - seller = product._select_seller(quantity=bom_quantity, uom_id=bom.product_uom_id, params={'subcontractor_ids': bom.subcontractor_ids}) - if seller: - values_sub = self._get_subcontracting_line(bom, seller, level, bom_quantity) - values_sub['type'] = 'bom' - values_sub['name'] = _("Subcontracting: ") + values_sub['name'] - res.append(values_sub) + @api.model + def _format_route_info(self, rules, rules_delay, warehouse, product, bom, quantity): + res = super()._format_route_info(rules, rules_delay, warehouse, product, bom, quantity) + subcontract_rules = [rule for rule in rules if rule.action == 'buy' and bom and bom.type == 'subcontract'] + if subcontract_rules: + supplier = product._select_seller(quantity=quantity, uom_id=product.uom_id, params={'subcontractor_ids': bom.subcontractor_ids}) + if supplier: + return { + 'route_type': 'subcontract', + 'route_name': subcontract_rules[0].route_id.display_name, + 'route_detail': supplier.display_name, + 'lead_time': supplier.delay + rules_delay, + 'supplier_delay': supplier.delay + rules_delay, + 'manufacture_delay': product.produce_delay, + 'supplier': supplier, + } return res + + @api.model + def _get_quantities_info(self, product, bom_uom, parent_bom, product_info): + if parent_bom and parent_bom.type == 'subcontract' and product.detailed_type == 'product': + parent_product = parent_bom.product_id or parent_bom.product_tmpl_id.product_variant_id + route_info = product_info[parent_product.id].get(parent_bom.id, {}) + if route_info and route_info['route_type'] == 'subcontract': + subcontracting_loc = route_info['supplier'].partner_id.property_stock_subcontractor + subloc_product = product.with_context(location=subcontracting_loc.id, warehouse=False).read(['free_qty', 'qty_available'])[0] + stock_loc = f"subcontract_{subcontracting_loc.id}" + if not product_info[product.id]['consumptions'].get(stock_loc, False): + product_info[product.id]['consumptions'][stock_loc] = 0 + return { + 'free_qty': product.uom_id._compute_quantity(subloc_product['free_qty'], bom_uom), + 'on_hand_qty': product.uom_id._compute_quantity(subloc_product['qty_available'], bom_uom), + 'stock_loc': stock_loc, + } + + return super()._get_quantities_info(product, bom_uom, parent_bom, product_info) + + @api.model + def _get_resupply_availability(self, route_info, components): + if route_info.get('route_type') == 'subcontract': + max_component_delay = self._get_max_component_delay(components) + if max_component_delay is False: + return ('unavailable', False) + produce_delay = route_info.get('manufacture_delay', 0) + max_component_delay + supplier_delay = route_info.get('supplier_delay', 0) + return ('estimated', max(produce_delay, supplier_delay)) + return super()._get_resupply_availability(route_info, components) diff --git a/addons/mrp_subcontracting/report/mrp_report_bom_structure.xml b/addons/mrp_subcontracting/report/mrp_report_bom_structure.xml deleted file mode 100644 index fc9fb7f978a..00000000000 --- a/addons/mrp_subcontracting/report/mrp_report_bom_structure.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/addons/mrp_subcontracting/static/src/components/bom_overview_components_block/mrp_bom_overview_components_block.js b/addons/mrp_subcontracting/static/src/components/bom_overview_components_block/mrp_bom_overview_components_block.js new file mode 100644 index 00000000000..69d1c3a48cf --- /dev/null +++ b/addons/mrp_subcontracting/static/src/components/bom_overview_components_block/mrp_bom_overview_components_block.js @@ -0,0 +1,9 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { BomOverviewComponentsBlock } from "@mrp/components/bom_overview_components_block/mrp_bom_overview_components_block"; +import { BomOverviewSpecialLine } from "@mrp/components/bom_overview_special_line/mrp_bom_overview_special_line"; + +patch(BomOverviewComponentsBlock, "mrp_subcontracting", { + components: { ...BomOverviewComponentsBlock.components, BomOverviewSpecialLine }, +}); diff --git a/addons/mrp_subcontracting/static/src/components/bom_overview_components_block/mrp_bom_overview_components_block.xml b/addons/mrp_subcontracting/static/src/components/bom_overview_components_block/mrp_bom_overview_components_block.xml new file mode 100644 index 00000000000..c15bfbd7cc1 --- /dev/null +++ b/addons/mrp_subcontracting/static/src/components/bom_overview_components_block/mrp_bom_overview_components_block.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/addons/mrp_subcontracting/static/src/components/bom_overview_line/mrp_bom_overview_line.js b/addons/mrp_subcontracting/static/src/components/bom_overview_line/mrp_bom_overview_line.js new file mode 100644 index 00000000000..ac62ea922ce --- /dev/null +++ b/addons/mrp_subcontracting/static/src/components/bom_overview_line/mrp_bom_overview_line.js @@ -0,0 +1,16 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { BomOverviewLine } from "@mrp/components/bom_overview_line/mrp_bom_overview_line"; + +patch(BomOverviewLine.prototype, "mrp_subcontracting", { + /** + * @override + */ + async goToRoute(routeType) { + if (routeType == "subcontract") { + return this.goToAction(this.data.link_id, this.data.link_model); + } + return this._super(...arguments); + } +}); diff --git a/addons/mrp_subcontracting/static/src/components/bom_overview_special_line/mrp_bom_overview_special_line.js b/addons/mrp_subcontracting/static/src/components/bom_overview_special_line/mrp_bom_overview_special_line.js new file mode 100644 index 00000000000..e237d5a7a66 --- /dev/null +++ b/addons/mrp_subcontracting/static/src/components/bom_overview_special_line/mrp_bom_overview_special_line.js @@ -0,0 +1,33 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { useService } from "@web/core/utils/hooks"; +import { BomOverviewSpecialLine } from "@mrp/components/bom_overview_special_line/mrp_bom_overview_special_line"; + +patch(BomOverviewSpecialLine.prototype, "mrp_subcontracting", { + setup() { + this._super.apply(); + this.actionService = useService("action"); + }, + + //---- Handlers ---- + + async goToSubcontractor() { + return this.actionService.doAction({ + type: "ir.actions.act_window", + res_model: "res.partner", + res_id: this.subcontracting.partner_id, + views: [[false, "form"]], + target: "current", + context: { + active_id: this.subcontracting.partner_id, + }, + }); + }, + + //---- Getters ---- + + get subcontracting() { + return this.props.data.subcontracting || {}; + }, +}); diff --git a/addons/mrp_subcontracting/static/src/components/bom_overview_special_line/mrp_bom_overview_special_line.xml b/addons/mrp_subcontracting/static/src/components/bom_overview_special_line/mrp_bom_overview_special_line.xml new file mode 100644 index 00000000000..3e86ea27148 --- /dev/null +++ b/addons/mrp_subcontracting/static/src/components/bom_overview_special_line/mrp_bom_overview_special_line.xml @@ -0,0 +1,26 @@ + + + + + + Subcontracting: + + + + + + + + + + + + + + + + + + + + diff --git a/addons/mrp_subcontracting/tests/test_subcontracting.py b/addons/mrp_subcontracting/tests/test_subcontracting.py index 7ab0ff6c24a..a738cb26b70 100644 --- a/addons/mrp_subcontracting/tests/test_subcontracting.py +++ b/addons/mrp_subcontracting/tests/test_subcontracting.py @@ -591,28 +591,25 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon): report_values = self.env['report.mrp.report_bom_structure']._get_report_data(self.bom.id, searchQty=1, searchVariant=False) subcontracting_values = report_values['lines']['subcontracting'] self.assertEqual(subcontracting_values['name'], self.subcontractor_partner1.display_name) - self.assertEqual(report_values['lines']['total'], 20) # 10 For subcontracting + 5 for comp1 + 5 for subcontracting of comp2_bom - self.assertEqual(report_values['lines']['bom_cost'], 20) + self.assertEqual(report_values['lines']['bom_cost'], 20) # 10 For subcontracting + 5 for comp1 + 5 for subcontracting of comp2_bom self.assertEqual(subcontracting_values['bom_cost'], 10) self.assertEqual(subcontracting_values['prod_cost'], 10) - self.assertEqual(report_values['lines']['components'][0]['total'], 5) - self.assertEqual(report_values['lines']['components'][1]['total'], 5) + self.assertEqual(report_values['lines']['components'][0]['bom_cost'], 5) + self.assertEqual(report_values['lines']['components'][1]['bom_cost'], 5) report_values = self.env['report.mrp.report_bom_structure']._get_report_data(self.bom.id, searchQty=3, searchVariant=False) subcontracting_values = report_values['lines']['subcontracting'] - self.assertEqual(report_values['lines']['total'], 60) # 30 for subcontracting + 15 for comp1 + 15 for subcontracting of comp2_bom - self.assertEqual(report_values['lines']['bom_cost'], 60) + self.assertEqual(report_values['lines']['bom_cost'], 60) # 30 for subcontracting + 15 for comp1 + 15 for subcontracting of comp2_bom self.assertEqual(subcontracting_values['bom_cost'], 30) self.assertEqual(subcontracting_values['prod_cost'], 30) - self.assertEqual(report_values['lines']['components'][0]['total'], 15) - self.assertEqual(report_values['lines']['components'][1]['total'], 15) + self.assertEqual(report_values['lines']['components'][0]['bom_cost'], 15) + self.assertEqual(report_values['lines']['components'][1]['bom_cost'], 15) report_values = self.env['report.mrp.report_bom_structure']._get_report_data(self.bom.id, searchQty=5, searchVariant=False) subcontracting_values = report_values['lines']['subcontracting'] - self.assertEqual(report_values['lines']['total'], 80) # 50 for subcontracting + 25 for comp1 + 5 for subcontracting of comp2_bom - self.assertEqual(report_values['lines']['bom_cost'], 80) + self.assertEqual(report_values['lines']['bom_cost'], 80) # 50 for subcontracting + 25 for comp1 + 5 for subcontracting of comp2_bom self.assertEqual(subcontracting_values['bom_cost'], 50) self.assertEqual(subcontracting_values['prod_cost'], 50) - self.assertEqual(report_values['lines']['components'][0]['total'], 25) - self.assertEqual(report_values['lines']['components'][1]['total'], 5) + self.assertEqual(report_values['lines']['components'][0]['bom_cost'], 25) + self.assertEqual(report_values['lines']['components'][1]['bom_cost'], 5) def test_several_backorders(self): def process_picking(picking, qty):