[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
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<odoo>
|
||||
<template id="report_mrp_bom_line_inherit_mrp_subcontracting" inherit_id="mrp.report_mrp_bom_line">
|
||||
<xpath expr="//t[@name='operations']" position="after">
|
||||
<t t-if="data.get('subcontracting')">
|
||||
<t t-set="space_td" t-value="'margin-left: '+ str(data['subcontracting']['level'] * 20) + 'px;'"/>
|
||||
<tr class="o_mrp_bom_report_line" t-att-parent_id="data['bom'].id">
|
||||
<td name="td_subcontracting">
|
||||
<div t-att-style="space_td">
|
||||
<div class="o_mrp_bom_no_fold">
|
||||
Subcontracting: <a href="#" class="o_mrp_bom_action" t-att-data-res-id="data['subcontracting']['partner_id']" t-att-data-model="'res.partner'"><t t-esc="data['subcontracting']['name']"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td/>
|
||||
<t t-foreach="range(data.get('extra_column_count', 0))" t-as="index">
|
||||
<td/>
|
||||
</t>
|
||||
<td class="text-end">
|
||||
<span t-esc="data['subcontracting']['quantity']" t-options='{"widget": "float", "decimal_precision": "Product Unit of Measure"}'/>
|
||||
</td>
|
||||
<td groups="uom.group_uom"><span t-esc="data['subcontracting']['uom']"/></td>
|
||||
<td class="o_mrp_prod_cost text-end">
|
||||
<span t-esc="data['subcontracting']['prod_cost']" t-options='{"widget": "monetary", "display_currency": currency}'/>
|
||||
</td>
|
||||
<td class="o_mrp_bom_cost text-end">
|
||||
<span t-esc="data['subcontracting']['bom_cost']" t-options='{"widget": "monetary", "display_currency": currency}'/>
|
||||
</td>
|
||||
<td/>
|
||||
</tr>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
+9
@@ -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 },
|
||||
});
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mrp_subcontracting.BomOverviewComponentsBlock" t-inherit="mrp.BomOverviewComponentsBlock" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//t[@name='byproducts']" position="after">
|
||||
<t t-if="data.subcontracting">
|
||||
<BomOverviewSpecialLine
|
||||
bus="props.bus"
|
||||
type="'subcontracting'"
|
||||
showOptions="props.showOptions"
|
||||
data="props.data"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
+16
@@ -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);
|
||||
}
|
||||
});
|
||||
+33
@@ -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 || {};
|
||||
},
|
||||
});
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mrp_subcontracting.BomOverviewSpecialLine" t-inherit="mrp.BomOverviewSpecialLine" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//td[@name='td_mrp_bom']" position="inside">
|
||||
<t t-if="props.type == 'subcontracting'">Subcontracting: <a href="#" t-on-click.prevent="goToSubcontractor" t-esc="subcontracting.name"/></t>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//td[@name='quantity']" position="inside">
|
||||
<span t-if="props.type == 'subcontracting'" t-esc="formatFloat(subcontracting.quantity)"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//td[@name='uom']" position="inside">
|
||||
<span t-if="props.type == 'subcontracting'" t-esc="subcontracting.uom"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//td[@name='bom_cost']" position="inside">
|
||||
<span t-if="props.type == 'subcontracting'" t-esc="formatMonetary(subcontracting.bom_cost)"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//td[@name='prod_cost']" position="inside">
|
||||
<span t-if="props.type == 'subcontracting'" t-esc="formatMonetary(subcontracting.prod_cost)"/>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user