From 6aa8b7dd67e5db48b869279d768e81774dd90dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Voet=20=28ryv=29?= Date: Fri, 6 Aug 2021 11:33:06 +0000 Subject: [PATCH] [IMP] mrp_subcontracting: improve mrp bom report - The "BoM Structure & Cost" takes now in account the subcontracting cost. The subcontracting cost depends of the best supplier info in product and subcontractor register in the BoM. - Also add this subcontracting cost (to the cost field) when we click on "Compute Price from BoM" in the product view. task-2486811 Part-of: odoo/odoo#75041 --- addons/mrp/report/mrp_report_bom_structure.py | 96 +++++++++--------- .../mrp/report/mrp_report_bom_structure.xml | 2 +- addons/mrp_account/tests/test_bom_price.py | 6 +- addons/mrp_subcontracting/__init__.py | 1 + addons/mrp_subcontracting/__manifest__.py | 1 + addons/mrp_subcontracting/models/product.py | 11 ++- addons/mrp_subcontracting/report/__init__.py | 4 + .../report/mrp_report_bom_structure.py | 63 ++++++++++++ .../report/mrp_report_bom_structure.xml | 31 ++++++ .../tests/test_subcontracting.py | 98 +++++++++++++++++++ .../models/__init__.py | 1 + .../models/product_product.py | 17 ++++ .../tests/test_subcontracting_account.py | 61 +++++++++++- 13 files changed, 338 insertions(+), 54 deletions(-) create mode 100644 addons/mrp_subcontracting/report/__init__.py create mode 100644 addons/mrp_subcontracting/report/mrp_report_bom_structure.py create mode 100644 addons/mrp_subcontracting/report/mrp_report_bom_structure.xml create mode 100644 addons/mrp_subcontracting_account/models/product_product.py diff --git a/addons/mrp/report/mrp_report_bom_structure.py b/addons/mrp/report/mrp_report_bom_structure.py index c704db5e9f4..308e8b50f30 100644 --- a/addons/mrp/report/mrp_report_bom_structure.py +++ b/addons/mrp/report/mrp_report_bom_structure.py @@ -19,7 +19,7 @@ class ReportBomStructure(models.AbstractModel): quantity = float(data.get('quantity', 1)) for product_variant_id in candidates.ids: if data and data.get('childs'): - doc = self._get_pdf_line(bom_id, product_id=product_variant_id, qty=quantity, child_bom_ids=json.loads(data.get('childs'))) + doc = self._get_pdf_line(bom_id, product_id=product_variant_id, qty=quantity, child_bom_ids=set(json.loads(data.get('childs')))) else: doc = self._get_pdf_line(bom_id, product_id=product_variant_id, qty=quantity, unfolded=True) doc['report_type'] = 'pdf' @@ -27,7 +27,7 @@ class ReportBomStructure(models.AbstractModel): docs.append(doc) if not candidates: if data and data.get('childs'): - doc = self._get_pdf_line(bom_id, qty=quantity, child_bom_ids=json.loads(data.get('childs'))) + doc = self._get_pdf_line(bom_id, qty=quantity, child_bom_ids=set(json.loads(data.get('childs')))) else: doc = self._get_pdf_line(bom_id, qty=quantity, unfolded=True) doc['report_type'] = 'pdf' @@ -260,48 +260,46 @@ class ReportBomStructure(models.AbstractModel): price += company.currency_id.round(not_rounded_price) return price - def _get_pdf_line(self, bom_id, product_id=False, qty=1, child_bom_ids=[], unfolded=False): - - def get_sub_lines(bom, product_id, line_qty, line_id, level): - data = self._get_bom(bom_id=bom.id, product_id=product_id, line_qty=line_qty, line_id=line_id, level=level) - bom_lines = data['components'] - lines = [] - for bom_line in bom_lines: - lines.append({ - 'name': bom_line['prod_name'], - 'type': 'bom', - 'quantity': bom_line['prod_qty'], - 'uom': bom_line['prod_uom'], - 'prod_cost': bom_line['prod_cost'], - 'bom_cost': bom_line['total'], - 'level': bom_line['level'], - 'code': bom_line['code'], - 'child_bom': bom_line['child_bom'], - 'prod_id': bom_line['prod_id'] - }) - if bom_line['child_bom'] and (unfolded or bom_line['child_bom'] in child_bom_ids): - line = self.env['mrp.bom.line'].browse(bom_line['line_id']) - lines += (get_sub_lines(line.child_bom_id, line.product_id.id, bom_line['prod_qty'], line, level + 1)) - if data['operations']: - lines.append({ - 'name': _('Operations'), - 'type': 'operation', - 'quantity': data['operations_time'], - 'uom': _('minutes'), - 'bom_cost': data['operations_cost'], - 'level': level, - }) - for operation in data['operations']: - if unfolded or 'operation-' + str(bom.id) in child_bom_ids: - lines.append({ - 'name': operation['name'], - 'type': 'operation', - 'quantity': operation['duration_expected'], - 'uom': _('minutes'), - 'bom_cost': operation['total'], - 'level': level + 1, - }) - if data['byproducts']: + def _get_sub_lines(self, bom, product_id, line_qty, line_id, level, child_bom_ids, unfolded): + data = self._get_bom(bom_id=bom.id, product_id=product_id, line_qty=line_qty, line_id=line_id, level=level) + bom_lines = data['components'] + lines = [] + for bom_line in bom_lines: + lines.append({ + 'name': bom_line['prod_name'], + 'type': 'bom', + 'quantity': bom_line['prod_qty'], + 'uom': bom_line['prod_uom'], + 'prod_cost': bom_line['prod_cost'], + 'bom_cost': bom_line['total'], + 'level': bom_line['level'], + 'code': bom_line['code'], + 'child_bom': bom_line['child_bom'], + 'prod_id': bom_line['prod_id'] + }) + if bom_line['child_bom'] and (unfolded or bom_line['child_bom'] in child_bom_ids): + line = self.env['mrp.bom.line'].browse(bom_line['line_id']) + lines += (self._get_sub_lines(line.child_bom_id, line.product_id.id, bom_line['prod_qty'], line, level + 1, child_bom_ids, unfolded)) + if data['operations']: + lines.append({ + 'name': _('Operations'), + 'type': 'operation', + 'quantity': data['operations_time'], + 'uom': _('minutes'), + 'bom_cost': data['operations_cost'], + 'level': level, + }) + for operation in data['operations']: + if unfolded or 'operation-' + str(bom.id) in child_bom_ids: + lines.append({ + 'name': operation['name'], + 'type': 'operation', + 'quantity': operation['duration_expected'], + 'uom': _('minutes'), + 'bom_cost': operation['total'], + 'level': level + 1, + }) + if data['byproducts']: lines.append({ 'name': _('Byproducts'), 'type': 'byproduct', @@ -321,12 +319,16 @@ class ReportBomStructure(models.AbstractModel): 'bom_cost': byproduct['bom_cost'], 'level': level + 1, }) - return lines + return lines + + def _get_pdf_line(self, bom_id, product_id=False, qty=1, child_bom_ids=None, unfolded=False): + if child_bom_ids is None: + child_bom_ids = set() bom = self.env['mrp.bom'].browse(bom_id) product_id = product_id or bom.product_id.id or bom.product_tmpl_id.product_variant_id.id - data = self._get_bom(bom_id=bom_id, product_id=product_id, line_qty=qty) - pdf_lines = get_sub_lines(bom, product_id, qty, False, 1) + data = self._get_bom(bom_id=bom_id, product_id=product_id, line_qty=qty, ) + pdf_lines = self._get_sub_lines(bom, product_id, qty, False, 1, child_bom_ids, unfolded) data['components'] = [] data['lines'] = pdf_lines data['extra_column_count'] = self._get_extra_column_count() diff --git a/addons/mrp/report/mrp_report_bom_structure.xml b/addons/mrp/report/mrp_report_bom_structure.xml index 87a6facd2fd..6d28f59ee3a 100644 --- a/addons/mrp/report/mrp_report_bom_structure.xml +++ b/addons/mrp/report/mrp_report_bom_structure.xml @@ -148,7 +148,7 @@ - + diff --git a/addons/mrp_account/tests/test_bom_price.py b/addons/mrp_account/tests/test_bom_price.py index 1766c089fd6..cdd1858b625 100644 --- a/addons/mrp_account/tests/test_bom_price.py +++ b/addons/mrp_account/tests/test_bom_price.py @@ -6,7 +6,7 @@ from odoo.tests import common, Form from odoo.tools.float_utils import float_round, float_compare -class TestBom(common.TransactionCase): +class TestBomPrice(common.TransactionCase): def _create_product(self, name, price): return self.Product.create({ @@ -16,11 +16,9 @@ class TestBom(common.TransactionCase): }) def setUp(self): - super(TestBom, self).setUp() + super(TestBomPrice, self).setUp() self.Product = self.env['product.product'] self.Bom = self.env['mrp.bom'] - #self.Routing = self.env['mrp.routing'] - self.operation = self.env['mrp.routing.workcenter'] # Products. self.dining_table = self._create_product('Dining Table', 1000) diff --git a/addons/mrp_subcontracting/__init__.py b/addons/mrp_subcontracting/__init__.py index abbc8a489ee..99d4d43ad32 100644 --- a/addons/mrp_subcontracting/__init__.py +++ b/addons/mrp_subcontracting/__init__.py @@ -4,6 +4,7 @@ from odoo import SUPERUSER_ID, api from . import models from . import wizard +from . import report def uninstall_hook(cr, registry): diff --git a/addons/mrp_subcontracting/__manifest__.py b/addons/mrp_subcontracting/__manifest__.py index fef10cdddd6..34a473c4b60 100644 --- a/addons/mrp_subcontracting/__manifest__.py +++ b/addons/mrp_subcontracting/__manifest__.py @@ -21,6 +21,7 @@ 'views/product_views.xml', 'views/mrp_production_views.xml', 'wizard/stock_picking_return_views.xml', + 'report/mrp_report_bom_structure.xml', ], 'demo': [ 'data/mrp_subcontracting_demo.xml', diff --git a/addons/mrp_subcontracting/models/product.py b/addons/mrp_subcontracting/models/product.py index 88e969cc24b..b9cedbc224e 100644 --- a/addons/mrp_subcontracting/models/product.py +++ b/addons/mrp_subcontracting/models/product.py @@ -13,5 +13,14 @@ class SupplierInfo(models.Model): def _compute_is_subcontractor(self): for supplier in self: boms = supplier.product_id.variant_bom_ids - boms |= supplier.product_tmpl_id.bom_ids.filtered(lambda b: not b.product_id) + boms |= supplier.product_tmpl_id.bom_ids.filtered(lambda b: not b.product_id or b.product_id in (supplier.product_id or supplier.product_tmpl_id.product_variant_ids)) supplier.is_subcontractor = supplier.name in boms.subcontractor_ids + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + def _prepare_sellers(self, params=False): + if params and params.get('subcontractor_ids'): + return super()._prepare_sellers(params=params).filtered(lambda s: s.name in params.get('subcontractor_ids')) + return super()._prepare_sellers(params=params) diff --git a/addons/mrp_subcontracting/report/__init__.py b/addons/mrp_subcontracting/report/__init__.py new file mode 100644 index 00000000000..107bd0b5a44 --- /dev/null +++ b/addons/mrp_subcontracting/report/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import mrp_report_bom_structure diff --git a/addons/mrp_subcontracting/report/mrp_report_bom_structure.py b/addons/mrp_subcontracting/report/mrp_report_bom_structure.py new file mode 100644 index 00000000000..de7830ce322 --- /dev/null +++ b/addons/mrp_subcontracting/report/mrp_report_bom_structure.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, _ + + +class ReportBomStructure(models.AbstractModel): + _inherit = 'report.mrp.report_bom_structure' + + def _get_subcontracting_line(self, bom, seller, level, bom_quantity): + ratio_uom_seller = seller.product_uom.ratio / bom.product_uom_id.ratio + return { + 'name': seller.name.display_name, + 'partner_id': seller.name.id, + 'quantity': bom_quantity, + 'uom': bom.product_uom_id.name, + 'prod_cost': seller.price / ratio_uom_seller, + 'bom_cost': seller.price / ratio_uom_seller * bom_quantity, + '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}) + if seller: + price += seller.product_uom._compute_price(seller.price, product.uom_id) + 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'] + return res + + def _get_sub_lines(self, bom, product_id, line_qty, line_id, level, child_bom_ids, unfolded): + res = super()._get_sub_lines(bom, product_id, line_qty, line_id, level, child_bom_ids, unfolded) + if bom and bom.type == 'subcontract': + product = self.env['product.product'].browse(product_id) + + 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 = 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) + + return res diff --git a/addons/mrp_subcontracting/report/mrp_report_bom_structure.xml b/addons/mrp_subcontracting/report/mrp_report_bom_structure.xml new file mode 100644 index 00000000000..af7f35d6f4c --- /dev/null +++ b/addons/mrp_subcontracting/report/mrp_report_bom_structure.xml @@ -0,0 +1,31 @@ + + + diff --git a/addons/mrp_subcontracting/tests/test_subcontracting.py b/addons/mrp_subcontracting/tests/test_subcontracting.py index 3b0f5d2e4d9..47c92f9b049 100644 --- a/addons/mrp_subcontracting/tests/test_subcontracting.py +++ b/addons/mrp_subcontracting/tests/test_subcontracting.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import Command from odoo.tests import Form from odoo.tests.common import TransactionCase from odoo.addons.mrp_subcontracting.tests.common import TestMrpSubcontractingCommon @@ -492,6 +493,103 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon): mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]) self.assertEqual(len(mo), 1) + def test_flow_flexible_bom_1(self): + """ Record Component for a bom subcontracted with a flexible and flexible + warning consumption """ + self.bom.consumption = 'flexible' + # Create a receipt picking from the subcontractor + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_in') + picking_form.partner_id = self.subcontractor_partner1 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished + move.product_uom_qty = 1 + picking_receipt = picking_form.save() + picking_receipt.action_confirm() + + self.assertEqual(picking_receipt.display_action_record_components, 'facultative') + action = picking_receipt.action_record_components() + mo = self.env['mrp.production'].browse(action['res_id']) + mo_form = Form(mo.with_context(**action['context']), view=action['view_id']) + mo_form.qty_producing = 1 + with mo_form.move_line_raw_ids.edit(0) as ml: + self.assertEqual(ml.product_id, self.comp1) + self.assertEqual(ml.qty_done, 1) + ml.qty_done = 2 + mo = mo_form.save() + mo.subcontracting_record_component() + self.assertEqual(mo.move_raw_ids[0].move_line_ids.qty_done, 2) + + # We should not be able to call the 'record_components' button + self.assertEqual(picking_receipt.display_action_record_components, 'hide') + + picking_receipt.button_validate() + self.assertEqual(mo.state, 'done') + avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True) + self.assertEqual(avail_qty_comp1, -2) + + def test_flow_warning_bom_1(self): + """ Record Component for a bom subcontracted with a flexible and flexible + warning consumption """ + self.bom.consumption = 'warning' + # Create a receipt picking from the subcontractor + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_in') + picking_form.partner_id = self.subcontractor_partner1 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished + move.product_uom_qty = 1 + picking_receipt = picking_form.save() + picking_receipt.action_confirm() + + self.assertEqual(picking_receipt.display_action_record_components, 'facultative') + action = picking_receipt.action_record_components() + mo = self.env['mrp.production'].browse(action['res_id']) + mo_form = Form(mo.with_context(**action['context']), view=action['view_id']) + mo_form.qty_producing = 1 + with mo_form.move_line_raw_ids.edit(0) as ml: + self.assertEqual(ml.product_id, self.comp1) + self.assertEqual(ml.qty_done, 1) + ml.qty_done = 2 + mo = mo_form.save() + action_warning = mo.subcontracting_record_component() + warning = Form(self.env['mrp.consumption.warning'].with_context(**action_warning['context'])) + warning = warning.save() + warning.action_cancel() + + action_warning = mo.subcontracting_record_component() + warning = Form(self.env['mrp.consumption.warning'].with_context(**action_warning['context'])) + warning = warning.save() + warning.action_confirm() + + self.assertEqual(mo.move_raw_ids[0].move_line_ids.qty_done, 2) + + # We should not be able to call the 'record_components' button + self.assertEqual(picking_receipt.display_action_record_components, 'hide') + + picking_receipt.button_validate() + self.assertEqual(mo.state, 'done') + avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True) + self.assertEqual(avail_qty_comp1, -2) + + def test_mrp_report_bom_structure_subcontracting(self): + self.comp2_bom.write({'type': 'subcontract', 'subcontractor_ids': [Command.link(self.subcontractor_partner1.id)]}) + self.env['product.supplierinfo'].create({ + 'product_tmpl_id': self.finished.product_tmpl_id.id, + 'name': self.subcontractor_partner1.id, + 'price': 10, + }) + supplier = self.env['product.supplierinfo'].create({ + 'product_tmpl_id': self.comp2.product_tmpl_id.id, + 'name': self.subcontractor_partner1.id, + 'price': 5, + }) + self.assertTrue(supplier.is_subcontractor) + self.comp1.standard_price = 5 + 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(subcontracting_values['bom_cost'], 10) + self.assertEqual(report_values['lines']['total'], 20) # 10 For subcontracting + 5 for comp1 + 5 for subcontracting of comp2_bom + class TestSubcontractingTracking(TransactionCase): def setUp(self): diff --git a/addons/mrp_subcontracting_account/models/__init__.py b/addons/mrp_subcontracting_account/models/__init__.py index ac9517445ae..aee2f0054db 100644 --- a/addons/mrp_subcontracting_account/models/__init__.py +++ b/addons/mrp_subcontracting_account/models/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import stock_picking +from . import product_product diff --git a/addons/mrp_subcontracting_account/models/product_product.py b/addons/mrp_subcontracting_account/models/product_product.py new file mode 100644 index 00000000000..1d7547424fe --- /dev/null +++ b/addons/mrp_subcontracting_account/models/product_product.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + +class ProductProduct(models.Model): + _inherit = 'product.product' + + def _compute_bom_price(self, bom, boms_to_recompute=False, byproduct_bom=False): + """ Add the price of the subcontracting supplier if it exists with the bom configuration. + """ + price = super()._compute_bom_price(bom, boms_to_recompute, byproduct_bom) + if bom and bom.type == 'subcontract': + seller = self._select_seller(quantity=bom.product_qty, uom_id=bom.product_uom_id, params={'subcontractor_ids': bom.subcontractor_ids}) + if seller: + price += seller.product_uom._compute_price(seller.price, self.uom_id) + return price diff --git a/addons/mrp_subcontracting_account/tests/test_subcontracting_account.py b/addons/mrp_subcontracting_account/tests/test_subcontracting_account.py index 60e13d9e10f..4b54b3ca305 100644 --- a/addons/mrp_subcontracting_account/tests/test_subcontracting_account.py +++ b/addons/mrp_subcontracting_account/tests/test_subcontracting_account.py @@ -1,8 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.addons.mrp_subcontracting.tests.common import TestMrpSubcontractingCommon +from odoo import Command from odoo.tests.common import Form +from odoo.tools.float_utils import float_round, float_compare + +from odoo.addons.mrp_subcontracting.tests.common import TestMrpSubcontractingCommon +from odoo.addons.mrp_account.tests.test_bom_price import TestBomPrice class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon): def test_subcontracting_account_flow_1(self): @@ -132,3 +136,58 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon): self.assertEqual(len(f_layers), 4) for layer in f_layers: self.assertEqual(layer.value, 100 + 50) + +class TestBomPriceSubcontracting(TestBomPrice): + + def test_01_compute_price_subcontracting_cost(self): + """Test calculation of bom cost with subcontracting.""" + self.table_head.uom_po_id = self.dozen + partner = self.env['res.partner'].create({ + 'name': 'A name can be a Many2one...' + }) + (self.bom_1 | self.bom_2).write({ + 'type': 'subcontract', + 'subcontractor_ids': [Command.link(partner.id)] + }) + suppliers = self.env['product.supplierinfo'].create([ + { + 'name': partner.id, + 'product_tmpl_id': self.dining_table.product_tmpl_id.id, + 'price': 150.0, + }, { + 'name': partner.id, + 'product_tmpl_id': self.table_head.product_tmpl_id.id, + 'price': 120.0, # 10 by Unit because uom_po_id is in dozen + } + ]) + self.assertEqual(suppliers.mapped('is_subcontractor'), [True, True]) + + # ----------------------------------------------------------------- + # Cost of BoM (Dining Table 1 Unit) + # ----------------------------------------------------------------- + # Component Cost = Table Head 1 Unit * 300 = 300 (478.75 from it's components) + # Screw 5 Unit * 10 = 50 + # Leg 4 Unit * 25 = 100 + # Glass 1 Unit * 100 = 100 + # Subcontracting 1 Unit * 150 = 150 + # Total = 700 [878.75 if components of Table Head considered] (for 1 Unit) + # ----------------------------------------------------------------- + self.assertEqual(self.dining_table.standard_price, 1000, "Initial price of the Product should be 1000") + self.dining_table.button_bom_cost() + self.assertEqual(float_round(self.dining_table.standard_price, precision_digits=2), 700.0, "After computing price from BoM price should be 700") + + # Cost of BoM (Table Head 1 Dozen) + # ----------------------------------------------------------------- + # Component Cost = Plywood Sheet 12 Unit * 200 = 2400 + # Bolt 60 Unit * 10 = 600 + # Colour 12 Unit * 100 = 1200 + # Corner Slide 57 Unit * 25 = 1425 + # Subcontracting 1 Dozen * 120 = 120 + # Total = 5745 + # 1 Unit price (5745/12) = 478.75 + # ----------------------------------------------------------------- + + self.assertEqual(self.table_head.standard_price, 300, "Initial price of the Product should be 300") + self.Product.browse([self.dining_table.id, self.table_head.id]).action_bom_cost() + self.assertEqual(float_compare(self.table_head.standard_price, 478.75, precision_digits=2), 0, "After computing price from BoM price should be 878.75") + self.assertEqual(float_compare(self.dining_table.standard_price, 878.75, precision_digits=2), 0, "After computing price from BoM price should be 878.75")