mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
@@ -11,4 +11,5 @@
|
||||
"data": [
|
||||
"views/mrp_bom_views.xml",
|
||||
],
|
||||
"demo": ["demo/product_product_demo.xml"],
|
||||
}
|
||||
|
||||
197
mrp_bom_attribute_match/demo/product_product_demo.xml
Normal file
197
mrp_bom_attribute_match/demo/product_product_demo.xml
Normal file
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo noupdate="1">
|
||||
|
||||
<!-- PRODUCTS -->
|
||||
|
||||
<record id="bam_pp_top" model="product.template">
|
||||
<field name="name">Top Level</field>
|
||||
<field name="type">product</field>
|
||||
<field name="sale_ok" eval="True" />
|
||||
<field name="uom_id" ref="uom.product_uom_unit" />
|
||||
<field name="uom_po_id" ref="uom.product_uom_unit" />
|
||||
<field
|
||||
name="route_ids"
|
||||
eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
<record id="bam_pp_sub_level" model="product.template">
|
||||
<field name="name">Sub Level</field>
|
||||
<field name="type">product</field>
|
||||
<field name="sale_ok" eval="True" />
|
||||
<field name="uom_id" ref="uom.product_uom_unit" />
|
||||
<field name="uom_po_id" ref="uom.product_uom_unit" />
|
||||
<field
|
||||
name="route_ids"
|
||||
eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
<record id="bam_pp_sub_sub" model="product.template">
|
||||
<field name="name">Sub Sub</field>
|
||||
<field name="type">product</field>
|
||||
<field name="sale_ok" eval="True" />
|
||||
<field name="uom_id" ref="uom.product_uom_unit" />
|
||||
<field name="uom_po_id" ref="uom.product_uom_unit" />
|
||||
<field
|
||||
name="route_ids"
|
||||
eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
<record id="bam_pp_sub_sub_2" model="product.template">
|
||||
<field name="name">Sub Sub 2</field>
|
||||
<field name="type">product</field>
|
||||
<field name="sale_ok" eval="True" />
|
||||
<field name="uom_id" ref="uom.product_uom_unit" />
|
||||
<field name="uom_po_id" ref="uom.product_uom_unit" />
|
||||
<field
|
||||
name="route_ids"
|
||||
eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
<!-- ATTRIBUTES -->
|
||||
<record id="bam_attr_1" model="product.attribute">
|
||||
<field name="name">attr1</field>
|
||||
<field name="sequence">10</field>
|
||||
</record>
|
||||
<record id="bam_attr_1_office" model="product.attribute.value">
|
||||
<field name="name">office</field>
|
||||
<field name="attribute_id" ref="bam_attr_1" />
|
||||
<field name="sequence">1</field>
|
||||
</record>
|
||||
<record id="bam_attr_1_gaming" model="product.attribute.value">
|
||||
<field name="name">gaming</field>
|
||||
<field name="attribute_id" ref="bam_attr_1" />
|
||||
<field name="sequence">1</field>
|
||||
</record>
|
||||
|
||||
<record id="bam_attr_2" model="product.attribute">
|
||||
<field name="name">attr2</field>
|
||||
<field name="sequence">10</field>
|
||||
</record>
|
||||
<record id="bam_attr_2_v1" model="product.attribute.value">
|
||||
<field name="name">v1</field>
|
||||
<field name="attribute_id" ref="bam_attr_2" />
|
||||
<field name="sequence">1</field>
|
||||
</record>
|
||||
<record id="bam_attr_2_v2" model="product.attribute.value">
|
||||
<field name="name">v2</field>
|
||||
<field name="attribute_id" ref="bam_attr_2" />
|
||||
<field name="sequence">1</field>
|
||||
</record>
|
||||
|
||||
<!-- PTALs -->
|
||||
<record id="bam_ptal_1_1" model="product.template.attribute.line">
|
||||
<field name="product_tmpl_id" ref="bam_pp_top" />
|
||||
<field name="attribute_id" ref="bam_attr_1" />
|
||||
<field
|
||||
name="value_ids"
|
||||
eval="[(6, 0, [ref('mrp_bom_attribute_match.bam_attr_1_office'), ref('mrp_bom_attribute_match.bam_attr_1_gaming')])]"
|
||||
/>
|
||||
</record>
|
||||
<record id="bam_ptal_1_2" model="product.template.attribute.line">
|
||||
<field name="product_tmpl_id" ref="bam_pp_top" />
|
||||
<field name="attribute_id" ref="bam_attr_2" />
|
||||
<field
|
||||
name="value_ids"
|
||||
eval="[(6, 0, [ref('mrp_bom_attribute_match.bam_attr_2_v1'), ref('mrp_bom_attribute_match.bam_attr_2_v2')])]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
<record id="bam_ptal_2_1" model="product.template.attribute.line">
|
||||
<field name="product_tmpl_id" ref="bam_pp_sub_level" />
|
||||
<field name="attribute_id" ref="bam_attr_2" />
|
||||
<field
|
||||
name="value_ids"
|
||||
eval="[(6, 0, [ref('mrp_bom_attribute_match.bam_attr_2_v1'), ref('mrp_bom_attribute_match.bam_attr_2_v2')])]"
|
||||
/>
|
||||
</record>
|
||||
<record id="bam_ptal_2_2" model="product.template.attribute.line">
|
||||
<field name="product_tmpl_id" ref="bam_pp_sub_level" />
|
||||
<field name="attribute_id" ref="bam_attr_1" />
|
||||
<field
|
||||
name="value_ids"
|
||||
eval="[(6, 0, [ref('mrp_bom_attribute_match.bam_attr_1_gaming'), ref('mrp_bom_attribute_match.bam_attr_1_office')])]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
<record id="bam_ptal_3" model="product.template.attribute.line">
|
||||
<field name="product_tmpl_id" ref="bam_pp_sub_sub" />
|
||||
<field name="attribute_id" ref="bam_attr_1" />
|
||||
<field
|
||||
name="value_ids"
|
||||
eval="[(6, 0, [ref('mrp_bom_attribute_match.bam_attr_1_office'), ref('mrp_bom_attribute_match.bam_attr_1_gaming')])]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
<record id="bam_ptal_4" model="product.template.attribute.line">
|
||||
<field name="product_tmpl_id" ref="bam_pp_sub_sub_2" />
|
||||
<field name="attribute_id" ref="bam_attr_2" />
|
||||
<field
|
||||
name="value_ids"
|
||||
eval="[(6, 0, [ref('mrp_bom_attribute_match.bam_attr_2_v1'), ref('mrp_bom_attribute_match.bam_attr_2_v2')])]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
<!-- PTAVs -->
|
||||
<function model="ir.model.data" name="_update_xmlids">
|
||||
<value
|
||||
model="base"
|
||||
eval="[{
|
||||
'xml_id': 'mrp_bom_attribute_match.bam_pp_top_ptav_1',
|
||||
'record': obj().env.ref('mrp_bom_attribute_match.bam_ptal_1_1').product_template_value_ids[0],
|
||||
'noupdate': True,
|
||||
},{
|
||||
'xml_id': 'mrp_bom_attribute_match.bam_pp_top_ptav_2',
|
||||
'record': obj().env.ref('mrp_bom_attribute_match.bam_ptal_1_2').product_template_value_ids[0],
|
||||
'noupdate': True,
|
||||
},{
|
||||
'xml_id': 'mrp_bom_attribute_match.bam_pp_sub_level_ptav_1',
|
||||
'record': obj().env.ref('mrp_bom_attribute_match.bam_ptal_2_1').product_template_value_ids[0],
|
||||
'noupdate': True,
|
||||
},{
|
||||
'xml_id': 'mrp_bom_attribute_match.bam_pp_sub_level_ptav_2',
|
||||
'record': obj().env.ref('mrp_bom_attribute_match.bam_ptal_2_2').product_template_value_ids[0],
|
||||
'noupdate': True,
|
||||
},{
|
||||
'xml_id': 'mrp_bom_attribute_match.bam_pp_sub_sub_ptav_1',
|
||||
'record': obj().env.ref('mrp_bom_attribute_match.bam_ptal_3').product_template_value_ids[0],
|
||||
'noupdate': True,
|
||||
},{
|
||||
'xml_id': 'mrp_bom_attribute_match.bam_pp_sub_sub_2_ptav_1',
|
||||
'record': obj().env.ref('mrp_bom_attribute_match.bam_ptal_4').product_template_value_ids[0],
|
||||
'noupdate': True,
|
||||
}]"
|
||||
/>
|
||||
</function>
|
||||
|
||||
<!-- BOMs -->
|
||||
<record id="bam_bom_sub_level" model="mrp.bom">
|
||||
<field name="product_tmpl_id" ref="bam_pp_sub_level" />
|
||||
</record>
|
||||
<record id="bam_bom_sub_level_line_1" model="mrp.bom.line">
|
||||
<field name="component_template_id" ref="bam_pp_sub_sub" />
|
||||
<field name="product_qty">1</field>
|
||||
<field name="product_uom_id" ref="uom.product_uom_unit" />
|
||||
<field name="bom_id" ref="bam_bom_sub_level" />
|
||||
</record>
|
||||
<record id="bam_bom_sub_level_line_2" model="mrp.bom.line">
|
||||
<field name="component_template_id" ref="bam_pp_sub_sub_2" />
|
||||
<field name="product_qty">1</field>
|
||||
<field name="product_uom_id" ref="uom.product_uom_unit" />
|
||||
<field name="bom_id" ref="bam_bom_sub_level" />
|
||||
</record>
|
||||
|
||||
<record id="bam_bom_top_level" model="mrp.bom">
|
||||
<field name="product_tmpl_id" ref="bam_pp_top" />
|
||||
</record>
|
||||
<record id="bam_bom_top_level_line_1" model="mrp.bom.line">
|
||||
<field name="component_template_id" ref="bam_pp_sub_level" />
|
||||
<field name="product_qty">1</field>
|
||||
<field name="product_uom_id" ref="uom.product_uom_unit" />
|
||||
<field name="bom_id" ref="bam_bom_top_level" />
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,8 +1,8 @@
|
||||
# Copyright 2023 Camptocamp SA (https://www.camptocamp.com).
|
||||
# @author Iván Todorovich <ivan.todorovich@camptocamp.com>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models
|
||||
from odoo.tools import float_round
|
||||
|
||||
|
||||
class ReportBomStructure(models.AbstractModel):
|
||||
@@ -32,6 +32,9 @@ class ReportBomStructure(models.AbstractModel):
|
||||
if to_ignore_line_ids:
|
||||
for to_ignore_line_id in to_ignore_line_ids:
|
||||
bom.bom_line_ids = [(3, to_ignore_line_id, 0)]
|
||||
product = bom._get_component_template_product(
|
||||
line, product, line.product_id
|
||||
)
|
||||
components, total = super()._get_bom_lines(
|
||||
bom, bom_quantity, product, line_id, level
|
||||
)
|
||||
@@ -43,3 +46,69 @@ class ReportBomStructure(models.AbstractModel):
|
||||
if isinstance(value, models.NewId):
|
||||
component[key] = value.origin
|
||||
return components, total
|
||||
|
||||
def _get_price(self, bom, factor, product):
|
||||
"""Replaced in order to implement component_template logic"""
|
||||
price = 0
|
||||
if bom.operation_ids:
|
||||
# routing are defined on a BoM and don't have a concept of quantity.
|
||||
# It means that the operation time are defined for the quantity on
|
||||
# the BoM (the user produces a batch of products). E.g the user
|
||||
# product a batch of 10 units with a 5 minutes operation, the time
|
||||
# will be the 5 for a quantity between 1-10, then doubled for
|
||||
# 11-20,...
|
||||
operation_cycle = float_round(
|
||||
factor, precision_rounding=1, rounding_method="UP"
|
||||
)
|
||||
operations = self._get_operation_line(bom, operation_cycle, 0)
|
||||
price += sum([op["total"] for op in operations])
|
||||
|
||||
for line in bom.bom_line_ids:
|
||||
if line._skip_bom_line(product):
|
||||
continue
|
||||
if line.child_bom_id:
|
||||
qty = line.product_uom_id._compute_quantity(
|
||||
line.product_qty * (factor / bom.product_qty),
|
||||
line.child_bom_id.product_uom_id,
|
||||
)
|
||||
sub_price = self._get_price(line.child_bom_id, qty, line.product_id)
|
||||
price += sub_price
|
||||
else:
|
||||
prod_qty = line.product_qty * factor / bom.product_qty
|
||||
company = bom.company_id or self.env.company
|
||||
# Modification start
|
||||
if line.component_template_id:
|
||||
vals = product.product_template_attribute_value_ids.mapped(
|
||||
"product_attribute_value_id"
|
||||
).ids
|
||||
match_found = False
|
||||
for prod in line.component_template_id.product_variant_ids:
|
||||
pavs = prod.product_template_attribute_value_ids.mapped(
|
||||
"product_attribute_value_id"
|
||||
)
|
||||
match = set(pavs.ids).issubset(set(vals))
|
||||
if match:
|
||||
match_found = True
|
||||
break
|
||||
if match_found:
|
||||
not_rounded_price = (
|
||||
prod.uom_id._compute_price(
|
||||
prod.with_company(company).standard_price,
|
||||
line.product_uom_id,
|
||||
)
|
||||
* prod_qty
|
||||
)
|
||||
price += company.currency_id.round(not_rounded_price)
|
||||
else:
|
||||
continue
|
||||
# Modification end
|
||||
else:
|
||||
not_rounded_price = (
|
||||
line.product_id.uom_id._compute_price(
|
||||
line.product_id.with_company(company).standard_price,
|
||||
line.product_uom_id,
|
||||
)
|
||||
* prod_qty
|
||||
)
|
||||
price += company.currency_id.round(not_rounded_price)
|
||||
return price
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
from . import test_mrp_bom_attribute_match
|
||||
from . import test_mrp_bom_attribute_match_nested
|
||||
|
||||
@@ -1,134 +1,171 @@
|
||||
from odoo.tests import Form, common
|
||||
from odoo.models import BaseModel
|
||||
from odoo.tests import Form, SavepointCase
|
||||
|
||||
|
||||
class TestMrpAttachmentMgmtBase(common.SavepointCase):
|
||||
class TestMrpBomAttributeMatchBase(SavepointCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls._create_products(cls)
|
||||
cls._create_boms(cls)
|
||||
|
||||
def _create_products(self):
|
||||
self.warehouse = self.env.ref("stock.warehouse0")
|
||||
route_manufacture = self.warehouse.manufacture_pull_id.route_id.id
|
||||
self.product_sword = self.env["product.template"].create(
|
||||
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
||||
cls.warehouse = cls.env.ref("stock.warehouse0")
|
||||
cls.route_manufacture = cls.warehouse.manufacture_pull_id.route_id
|
||||
# Create products
|
||||
cls.product_sword = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "Plastic Sword",
|
||||
"type": "product",
|
||||
}
|
||||
)
|
||||
self.product_surf = self.env["product.template"].create(
|
||||
cls.product_surf = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "Surf",
|
||||
"type": "product",
|
||||
}
|
||||
)
|
||||
self.product_fin = self.env["product.template"].create(
|
||||
cls.product_fin = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "Surf Fin",
|
||||
"type": "product",
|
||||
}
|
||||
)
|
||||
self.product_plastic = self.env["product.template"].create(
|
||||
cls.product_plastic = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "Plastic Component",
|
||||
"type": "product",
|
||||
}
|
||||
)
|
||||
self.p1 = self.env["product.template"].create(
|
||||
cls.p1 = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "P1",
|
||||
"type": "product",
|
||||
"route_ids": [(6, 0, [route_manufacture])],
|
||||
"route_ids": [(6, 0, cls.route_manufacture.ids)],
|
||||
}
|
||||
)
|
||||
self.p2 = self.env["product.template"].create(
|
||||
cls.p2 = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "P2",
|
||||
"type": "product",
|
||||
"route_ids": [(6, 0, [route_manufacture])],
|
||||
"route_ids": [(6, 0, cls.route_manufacture.ids)],
|
||||
}
|
||||
)
|
||||
self.p3 = self.env["product.template"].create(
|
||||
cls.p3 = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "P3",
|
||||
"type": "product",
|
||||
"route_ids": [(6, 0, [route_manufacture])],
|
||||
"route_ids": [(6, 0, cls.route_manufacture.ids)],
|
||||
}
|
||||
)
|
||||
self.product_9 = self.env["product.product"].create(
|
||||
cls.product_9 = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Paper",
|
||||
}
|
||||
)
|
||||
self.product_10 = self.env["product.product"].create(
|
||||
cls.product_10 = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Stone",
|
||||
}
|
||||
)
|
||||
self.product_attribute = self.env["product.attribute"].create(
|
||||
cls.product_attribute = cls.env["product.attribute"].create(
|
||||
{"name": "Colour", "display_type": "radio", "create_variant": "always"}
|
||||
)
|
||||
self.attribute_value_ids = self.env["product.attribute.value"].create(
|
||||
cls.attribute_value_ids = cls.env["product.attribute.value"].create(
|
||||
[
|
||||
{"name": "Cyan", "attribute_id": self.product_attribute.id},
|
||||
{"name": "Magenta", "attribute_id": self.product_attribute.id},
|
||||
{"name": "Cyan", "attribute_id": cls.product_attribute.id},
|
||||
{"name": "Magenta", "attribute_id": cls.product_attribute.id},
|
||||
]
|
||||
)
|
||||
self.plastic_attrs = self.env["product.template.attribute.line"].create(
|
||||
cls.plastic_attrs = cls.env["product.template.attribute.line"].create(
|
||||
{
|
||||
"attribute_id": self.product_attribute.id,
|
||||
"product_tmpl_id": self.product_plastic.id,
|
||||
"value_ids": [(6, 0, self.product_attribute.value_ids.ids)],
|
||||
"attribute_id": cls.product_attribute.id,
|
||||
"product_tmpl_id": cls.product_plastic.id,
|
||||
"value_ids": [(6, 0, cls.product_attribute.value_ids.ids)],
|
||||
}
|
||||
)
|
||||
self.sword_attrs = self.env["product.template.attribute.line"].create(
|
||||
cls.sword_attrs = cls.env["product.template.attribute.line"].create(
|
||||
{
|
||||
"attribute_id": self.product_attribute.id,
|
||||
"product_tmpl_id": self.product_sword.id,
|
||||
"value_ids": [(6, 0, self.product_attribute.value_ids.ids)],
|
||||
"attribute_id": cls.product_attribute.id,
|
||||
"product_tmpl_id": cls.product_sword.id,
|
||||
"value_ids": [(6, 0, cls.product_attribute.value_ids.ids)],
|
||||
}
|
||||
)
|
||||
# Create boms
|
||||
cls.bom_id = cls._create_bom(
|
||||
cls.product_sword,
|
||||
[
|
||||
dict(
|
||||
component_template_id=cls.product_plastic.id,
|
||||
product_qty=1,
|
||||
),
|
||||
dict(
|
||||
product_id=cls.product_9,
|
||||
product_qty=1,
|
||||
),
|
||||
],
|
||||
)
|
||||
cls.fin_bom_id = cls._create_bom(
|
||||
cls.product_fin,
|
||||
[
|
||||
dict(
|
||||
product_id=cls.product_plastic.product_variant_ids[0],
|
||||
product_qty=1,
|
||||
),
|
||||
],
|
||||
)
|
||||
cls.surf_bom_id = cls._create_bom(
|
||||
cls.product_surf,
|
||||
[
|
||||
dict(
|
||||
product_id=cls.product_fin.product_variant_ids[0],
|
||||
product_qty=1,
|
||||
),
|
||||
],
|
||||
)
|
||||
cls.p1_bom_id = cls._create_bom(
|
||||
cls.p1,
|
||||
[
|
||||
dict(
|
||||
product_id=cls.p2.product_variant_ids[0],
|
||||
product_qty=1,
|
||||
),
|
||||
],
|
||||
)
|
||||
cls.p2_bom_id = cls._create_bom(
|
||||
cls.p2,
|
||||
[
|
||||
dict(
|
||||
product_id=cls.p3.product_variant_ids[0],
|
||||
product_qty=1,
|
||||
),
|
||||
],
|
||||
)
|
||||
cls.p3_bom_id = cls._create_bom(
|
||||
cls.p3,
|
||||
[
|
||||
dict(
|
||||
product_id=cls.p1.product_variant_ids[0],
|
||||
product_qty=1,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def _create_boms(self):
|
||||
mrp_bom_form = Form(self.env["mrp.bom"])
|
||||
mrp_bom_form.product_tmpl_id = self.product_sword
|
||||
with mrp_bom_form.bom_line_ids.new() as line_form:
|
||||
line_form.component_template_id = self.product_plastic
|
||||
line_form.product_qty = 1
|
||||
self.bom_id = mrp_bom_form.save()
|
||||
|
||||
mrp_bom_form = Form(self.env["mrp.bom"])
|
||||
mrp_bom_form.product_tmpl_id = self.product_fin
|
||||
with mrp_bom_form.bom_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_plastic.product_variant_ids[0]
|
||||
line_form.product_qty = 1
|
||||
self.fin_bom_id = mrp_bom_form.save()
|
||||
|
||||
mrp_bom_form = Form(self.env["mrp.bom"])
|
||||
mrp_bom_form.product_tmpl_id = self.product_surf
|
||||
with mrp_bom_form.bom_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_fin.product_variant_ids[0]
|
||||
line_form.product_qty = 1
|
||||
self.surf_bom_id = mrp_bom_form.save()
|
||||
|
||||
mrp_bom_form = Form(self.env["mrp.bom"])
|
||||
mrp_bom_form.product_tmpl_id = self.p1
|
||||
with mrp_bom_form.bom_line_ids.new() as line_form:
|
||||
line_form.product_id = self.p2.product_variant_ids[0]
|
||||
line_form.product_qty = 1
|
||||
self.p1_bom_id = mrp_bom_form.save()
|
||||
|
||||
mrp_bom_form = Form(self.env["mrp.bom"])
|
||||
mrp_bom_form.product_tmpl_id = self.p2
|
||||
with mrp_bom_form.bom_line_ids.new() as line_form:
|
||||
line_form.product_id = self.p3.product_variant_ids[0]
|
||||
line_form.product_qty = 1
|
||||
self.p2_bom_id = mrp_bom_form.save()
|
||||
|
||||
mrp_bom_form = Form(self.env["mrp.bom"])
|
||||
mrp_bom_form.product_tmpl_id = self.p3
|
||||
with mrp_bom_form.bom_line_ids.new() as line_form:
|
||||
line_form.product_id = self.p1.product_variant_ids[0]
|
||||
line_form.product_qty = 1
|
||||
self.p3_bom_id = mrp_bom_form.save()
|
||||
@classmethod
|
||||
def _create_bom(cls, product, line_form_vals):
|
||||
if product._name == "product.template":
|
||||
template = product
|
||||
product = cls.env["product.product"]
|
||||
else:
|
||||
template = product.product_tmpl_id
|
||||
with Form(cls.env["mrp.bom"]) as form:
|
||||
form.product_tmpl_id = template
|
||||
form.product_id = product
|
||||
for vals in line_form_vals:
|
||||
with form.bom_line_ids.new() as line_form:
|
||||
for key, value in vals.items():
|
||||
field = line_form._model._fields.get(key)
|
||||
if field and field.relational: # pragma: no cover
|
||||
if value and not isinstance(value, BaseModel):
|
||||
value = cls.env[field.comodel_name].browse(value)
|
||||
elif not value:
|
||||
value = cls.env[field.comodel_name]
|
||||
setattr(line_form, key, value)
|
||||
return form.save()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tests import Form
|
||||
|
||||
from .common import TestMrpAttachmentMgmtBase
|
||||
from .common import TestMrpBomAttributeMatchBase
|
||||
|
||||
|
||||
class TestMrpAttachmentMgmt(TestMrpAttachmentMgmtBase):
|
||||
class TestMrpAttachmentMgmt(TestMrpBomAttributeMatchBase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
@@ -67,7 +67,7 @@ class TestMrpAttachmentMgmt(TestMrpAttachmentMgmtBase):
|
||||
self.mo_sword.action_confirm()
|
||||
# Assert correct component variant was selected automatically
|
||||
self.assertEqual(
|
||||
self.mo_sword.move_raw_ids.product_id.display_name,
|
||||
self.mo_sword.move_raw_ids.product_id[0].display_name,
|
||||
"Plastic Component (Cyan)",
|
||||
)
|
||||
|
||||
@@ -81,9 +81,8 @@ class TestMrpAttachmentMgmt(TestMrpAttachmentMgmtBase):
|
||||
mo_form.bom_id = self.bom_id
|
||||
mo_form.product_qty = 1
|
||||
self.mo_sword = mo_form.save()
|
||||
with self.assertRaises(UserError):
|
||||
# Add some materials to consume before marking this MO as to do.
|
||||
self.mo_sword.action_confirm()
|
||||
# Add some materials to consume before marking this MO as to do.
|
||||
self.mo_sword.action_confirm()
|
||||
|
||||
def test_manufacturing_order_3(self):
|
||||
# Delete attribute from sword
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
from .common import TestMrpBomAttributeMatchBase
|
||||
|
||||
|
||||
class TestMrpBomAttributeMatchNested(TestMrpBomAttributeMatchBase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
attr1 = self.env["product.attribute"].create(
|
||||
{"name": "style", "display_type": "radio", "create_variant": "always"}
|
||||
)
|
||||
a1vs = self.env["product.attribute.value"].create(
|
||||
[
|
||||
{"name": "office", "attribute_id": attr1.id},
|
||||
{"name": "gaming", "attribute_id": attr1.id},
|
||||
]
|
||||
)
|
||||
|
||||
top = self.env["product.template"].create(
|
||||
{
|
||||
"name": "Top-Level",
|
||||
"type": "product",
|
||||
}
|
||||
)
|
||||
self.env["product.template.attribute.line"].create(
|
||||
{
|
||||
"attribute_id": attr1.id,
|
||||
"product_tmpl_id": top.id,
|
||||
"value_ids": [(6, 0, a1vs.ids)],
|
||||
}
|
||||
)
|
||||
|
||||
sub = self.env["product.template"].create(
|
||||
{
|
||||
"name": "Sub-Level",
|
||||
"type": "product",
|
||||
}
|
||||
)
|
||||
self.env["product.template.attribute.line"].create(
|
||||
{
|
||||
"attribute_id": attr1.id,
|
||||
"product_tmpl_id": sub.id,
|
||||
"value_ids": [(6, 0, a1vs.ids)],
|
||||
}
|
||||
)
|
||||
|
||||
subsub = self.env["product.template"].create(
|
||||
{
|
||||
"name": "Sub Sub 1",
|
||||
"type": "product",
|
||||
}
|
||||
)
|
||||
self.env["product.template.attribute.line"].create(
|
||||
{
|
||||
"attribute_id": attr1.id,
|
||||
"product_tmpl_id": subsub.id,
|
||||
"value_ids": [(6, 0, a1vs.ids)],
|
||||
}
|
||||
)
|
||||
|
||||
self.bom_sub = self._create_bom(
|
||||
sub,
|
||||
[
|
||||
dict(
|
||||
component_template_id=subsub.id,
|
||||
product_qty=1,
|
||||
),
|
||||
],
|
||||
)
|
||||
self.bom_top = self._create_bom(
|
||||
top,
|
||||
[
|
||||
dict(
|
||||
component_template_id=sub.id,
|
||||
product_qty=1,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def test_nested(self):
|
||||
BomStructureReport = self.env["report.mrp.report_bom_structure"]
|
||||
|
||||
BomStructureReport._get_report_data(self.bom_sub.id)
|
||||
BomStructureReport._get_report_data(self.bom_top.id)
|
||||
Reference in New Issue
Block a user