Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion addons/mrp/report/mrp_report_mo_overview.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ def _get_report_data(self, production_id):

if production.bom_id:
currency = (production.company_id or self.env.company).currency_id
missing_components = (bom_line for bom_line in production.bom_id.bom_line_ids if bom_line not in (production.move_raw_ids.bom_line_id + self._get_kit_bom_lines(production.bom_id)))
current_bom_lines = production.move_raw_ids.bom_line_id | self._get_kit_bom_lines(production.bom_id)
missing_components = production.bom_id.bom_line_ids.filtered(
lambda bom_line: bom_line not in current_bom_lines and
not bom_line._skip_bom_line(production.product_id)
)
missing_operations = (bom_line for bom_line in production.bom_id.operation_ids if bom_line not in production.workorder_ids.operation_id)
for line in missing_components:
line_cost = line.product_id.uom_id._compute_price(line.product_id.standard_price, line.product_uom_id) * line.product_qty
Expand Down
122 changes: 122 additions & 0 deletions addons/mrp/tests/test_stock_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,3 +513,125 @@ def test_mo_overview_same_component(self):
self.assertEqual(len(repl1), 1)
self.assertEqual(repl0[0]['summary']['quantity'], 3)
self.assertEqual(repl1[0]['summary']['quantity'], 5)

def test_report_price_variants(self):
"""
This tests the MO's report price when a variant is involved. It makes sure
that the BoM price takes only the current variant and not all of them. It also
tests that the lines that were removed from the MO but are still in the bom are
used in the BoM cost computing. Lastly, it makes sure that Kits are also accounted
for and used in the BoM cost as they should.
"""
# Create a color variant, which will be used to create a Product
attribute_color = self.env['product.attribute'].create({'name': 'Color'})
value_black, value_white = self.env['product.attribute.value'].create({
'name': name,
'attribute_id': attribute_color.id,
} for name in ['Black', 'White'])
# Create 3 product templates, one for the variants, to check to not all variants are used
# to compute the cost of the MO, another one to make sure the 'missing components' are still
# taken into account (they are the product that were removed from the MO but are still present
# in the BoM), and the last one to make sure that kits are still taken in as well.
product_variants, missing_product, kit_product = self.env['product.template'].create([
{
'name': 'Variant Product',
'type': 'consu',
'attribute_line_ids': [Command.create(
{
'attribute_id': attribute_color.id,
'value_ids': [
Command.link(value_black.id),
Command.link(value_white.id),
],
},
)],
},
{
'name': 'Missing Component',
'type': 'consu',
'standard_price': 40,
},
{
'name': 'Kit Product',
'type': 'consu',
'standard_price': 60,
},
])

variant_black = product_variants.product_variant_ids[0]
variant_white = product_variants.product_variant_ids[1]
variant_black.standard_price = 50
variant_white.standard_price = 30
bom_normal, _ = self.env['mrp.bom'].create([
{
'product_tmpl_id': product_variants.id,
'type': 'normal',
'bom_line_ids': [Command.create({
'product_id': missing_product.product_variant_id.id,
'product_qty': 1,
}),
Command.create({
'product_id': kit_product.product_variant_id.id,
'product_qty': 1,
})],
},
{
'product_tmpl_id': kit_product.id,
'type': 'phantom',
'bom_line_ids': [Command.create({
'product_id': missing_product.product_variant_id.id,
'product_qty': 1,
})]
},
])
white_tmpl = bom_normal.product_tmpl_id.product_variant_ids.product_template_attribute_value_ids.filtered(lambda tmpl: tmpl.product_attribute_value_id == value_white)
black_tmpl = bom_normal.product_tmpl_id.product_variant_ids.product_template_attribute_value_ids.filtered(lambda tmpl: tmpl.product_attribute_value_id == value_black)
black_white_product, black_product, white_product = self.env['product.product'].create([{
'name': 'Black White',
'type': 'consu',
'standard_price': 25,
},
{
'name': 'Black Only',
'type': 'consu',
'standard_price': 15,
},
{
'name': 'White Only',
'type': 'consu',
'standard_price': 10,
},
])
self.env['mrp.bom.line'].create([{
'bom_id': bom_normal.id,
'product_id': black_white_product.id,
'product_qty': 1,
'bom_product_template_attribute_value_ids': [Command.set([black_tmpl.id, white_tmpl.id])],
},
{
'bom_id': bom_normal.id,
'product_id': black_product.id,
'product_qty': 1,
'bom_product_template_attribute_value_ids': [Command.set([black_tmpl.id])],
},
{
'bom_id': bom_normal.id,
'product_id': white_product.id,
'product_qty': 1,
'bom_product_template_attribute_value_ids': [Command.set([white_tmpl.id])],
}
])
mo = self.env['mrp.production'].create({
'product_id': variant_black.id,
'product_qty': 1,
'bom_id': bom_normal.id,
})
mo_report = self.env['report.mrp.report_mo_overview'].get_report_values(mo.id)
self.assertEqual(mo_report['data']['extras']['unit_bom_cost'], mo_report['data']['extras']['unit_mo_cost'], 'The BoM unit cost should be equal to the sum of the products of said BoM')
# Check that the missing components (the components that were removed from the MO but are still in the BoM)
# are taken into account when computing the BoM cost
mo.move_raw_ids.filtered(lambda m: m.product_id == missing_product.product_variant_id and m.bom_line_id.bom_id.type != 'phantom').unlink()
# When a product has two possible variants, and then is deleted, it should be taken in the missing components
mo.move_raw_ids.filtered(lambda m: m.product_id == black_white_product and m.bom_line_id.bom_id.type != 'phantom').unlink()
mo_report = self.env['report.mrp.report_mo_overview'].get_report_values(mo.id)
self.assertEqual(mo_report['data']['extras']['unit_bom_cost'], mo_report['data']['extras']['unit_mo_cost'] + missing_product.standard_price + black_white_product.standard_price, 'The BoM unit cost should take the missing components into account, which are the deleted MO lines')