[MIG/REF] mrp_bom_hierarchy: Refactor/Migration to 14.0

This commit is contained in:
Miquel Raïch
2022-06-16 16:05:29 +02:00
parent 034a104d7a
commit 9f1500bff8
10 changed files with 328 additions and 431 deletions

View File

@@ -1,20 +1 @@
##############################################################################
#
# Copyright (C) 2015 Eficent (<http://www.eficent.com/>)
# <contact@eficent.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import model from . import model

View File

@@ -1,35 +1,18 @@
############################################################################## # Copyright 2015-22 ForgeFlow S.L. (https://www.forgeflow.com)
# # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
# Copyright (C) 2015 Eficent (<http://www.eficent.com/>)
# <contact@eficent.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{ {
"name": "MRP BOM Hierarchy", "name": "MRP BoM Hierarchy",
"version": "1.0", "summary": "Make it easy to navigate through BoM hierarchy.",
"author": "Eficent, Odoo Community Association (OCA)", "version": "14.0.1.0.0",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"category": "Manufacturing", "category": "Manufacturing",
"depends": ["mrp"], "depends": ["mrp"],
"website": "https://github.com/OCA/manufacture", "website": "https://github.com/OCA/manufacture",
"license": "AGPL-3", "license": "AGPL-3",
"demo": [],
"data": [ "data": [
"view/mrp.xml", "view/mrp.xml",
], ],
"test": [],
"installable": True, "installable": True,
"auto_install": False, "auto_install": False,
} }

View File

@@ -1,20 +1 @@
############################################################################## from . import mrp_bom
#
# Copyright (C) 2014 Eficent (<http://www.eficent.com/>)
# <contact@eficent.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import mrp

View File

@@ -1,280 +0,0 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from odoo import fields, models
class Bom(models.Model):
_inherit = "mrp.bom"
def get_child_boms(self, cr, uid, ids, context=None):
result = {}
if not ids:
return result
for curr_id in ids:
result[curr_id] = True
# Now add the children
cr.execute(
"""
WITH RECURSIVE children AS (
SELECT bom_id, id
FROM mrp_bom
WHERE bom_id IN %s
UNION ALL
SELECT a.bom_id, a.id
FROM mrp_bom a
JOIN children b ON(a.bom_id = b.id)
)
SELECT * FROM children order by bom_id
""",
(tuple(ids),),
)
res = cr.fetchall()
for _x, y in res:
result[y] = True
return result
def _get_boms_from_product(self, cr, uid, ids, context=None):
result = {}
bom_obj = self.pool.get("mrp.bom")
for p in ids:
product_bom_ids = bom_obj.search(cr, uid, [("product_id", "=", p)])
bom_ids = bom_obj.get_child_boms(cr, uid, product_bom_ids, context=context)
for bom_id in bom_ids:
result[bom_id] = True
return result
def _is_bom(self, cr, uid, ids, name, arg, context=None):
result = {}
if context is None:
context = {}
for bom in self.browse(cr, uid, ids, context=context):
result[bom.id] = False
if bom.bom_lines:
result[bom.id] = True
return result
def _bom_hierarchy_indent_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
if not ids:
return []
res = []
for bom in self.browse(cr, uid, ids, context=None):
data = []
b = bom
while b:
if b.name and b.bom_id:
data.insert(0, ">")
else:
data.insert(0, "")
b = b.bom_id
data = "".join(data)
res.append((bom.id, data))
return dict(res)
def _complete_bom_hierarchy_code_calc(
self, cr, uid, ids, prop, unknow_none, unknow_dict
):
if not ids:
return []
res = []
for bom in self.browse(cr, uid, ids, context=None):
data = []
b = bom
while b:
if b.code:
data.insert(0, b.code)
elif b.position:
data.insert(0, b.position)
elif b.product_id.default_code:
data.insert(0, b.product_id.default_code)
else:
data.insert(0, "")
b = b.bom_id
data = " / ".join(data)
data = "[" + data + "] "
res.append((bom.id, data))
return dict(res)
def _complete_bom_hierarchy_name_calc(
self, cr, uid, ids, prop, unknow_none, unknow_dict
):
if not ids:
return []
res = []
for bom in self.browse(cr, uid, ids, context=None):
data = []
b = bom
while b:
if b.name:
data.insert(0, b.name)
elif b.product_id.name:
data.insert(0, b.product_id.name)
else:
data.insert(0, "")
b = b.bom_id
data = " / ".join(data)
res.append((bom.id, data))
return dict(res)
def _is_parent(self, cr, uid, ids, prop, unknow_none, unknow_dict):
res = {}
for bom in self.browse(cr, uid, ids, context=None):
if not bom.bom_id:
res[bom.id] = True
else:
res[bom.id] = False
return res
def _product_has_own_bom(self, cr, uid, ids, prop, unknow_none, unknow_dict):
res = {}
for bom in self.browse(cr, uid, ids, context=None):
bom_ids = self.pool.get("mrp.bom").search(
cr,
uid,
[("product_id", "=", bom.product_id.id), ("bom_id", "=", False)],
context=None,
)
if bom_ids:
res[bom.id] = True
else:
res[bom.id] = False
return res
_columns = {
"is_parent": fields.function(
_is_parent,
string="Is parent BOM",
type="boolean",
readonly=True,
store=True,
),
"has_child": fields.function(
_is_bom, string="Has components", type="boolean", readonly=True
),
"product_has_own_bom": fields.function(
_product_has_own_bom,
string="Product has own BOM",
type="boolean",
readonly=True,
),
"bom_hierarchy_indent": fields.function(
_bom_hierarchy_indent_calc,
method=True,
type="char",
string="Level",
size=32,
readonly=True,
),
"complete_bom_hierarchy_code": fields.function(
_complete_bom_hierarchy_code_calc,
method=True,
type="char",
string="Complete Reference",
size=250,
help="Describes the full path of this "
"component within the BOM hierarchy using the BOM reference.",
store={
"mrp.bom": (get_child_boms, ["name", "code", "position", "bom_id"], 20),
"product.product": (_get_boms_from_product, ["default_code"], 20),
},
),
"complete_bom_hierarchy_name": fields.function(
_complete_bom_hierarchy_name_calc,
method=True,
type="char",
string="Complete Name",
size=250,
help="Describes the full path of this "
"component within the BOM hierarchy using the BOM name.",
store={
"mrp.bom": (get_child_boms, ["name", "bom_id"], 20),
"product.product": (_get_boms_from_product, ["name"], 20),
},
),
}
_order = "complete_bom_hierarchy_code"
def action_openChildTreeView(self, cr, uid, ids, context=None):
"""
:return dict: dictionary value for created view
"""
if context is None:
context = {}
bom = self.browse(cr, uid, ids[0], context)
child_bom_ids = self.pool.get("mrp.bom").search(
cr, uid, [("bom_id", "=", bom.id)]
)
res = self.pool.get("ir.actions.act_window").for_xml_id(
cr, uid, "mrp_bom_hierarchy", "action_mrp_bom_hierarchy_tree2", context
)
res["context"] = {
"default_bom_id": bom.id,
}
res["domain"] = "[('id', 'in', [" + ",".join(map(str, child_bom_ids)) + "])]"
res["nodestroy"] = False
return res
def action_openParentTreeView(self, cr, uid, ids, context=None):
"""
:return dict: dictionary value for created view
"""
if context is None:
context = {}
bom = self.browse(cr, uid, ids[0], context)
res = self.pool.get("ir.actions.act_window").for_xml_id(
cr, uid, "mrp_bom_hierarchy", "action_mrp_bom_hierarchy_tree2", context
)
if bom.bom_id:
for parent_bom_id in self.pool.get("mrp.bom").search(
cr, uid, [("id", "=", bom.bom_id.id)]
):
res["domain"] = "[('id','='," + str(parent_bom_id) + ")]"
res["nodestroy"] = False
return res
def action_openProductBOMTreeView(self, cr, uid, ids, context=None):
"""
:return dict: dictionary value for created view
"""
if context is None:
context = {}
bom = self.browse(cr, uid, ids[0], context)
product_bom_ids = self.pool.get("mrp.bom").search(
cr,
uid,
[("product_id", "=", bom.product_id.id), ("bom_id", "=", False)],
context=context,
)
res = self.pool.get("ir.actions.act_window").for_xml_id(
cr, uid, "mrp", "mrp_bom_form_action2", context
)
res["context"] = {
"default_product_id": bom.product_id.id,
}
res["domain"] = "[('id', 'in', [" + ",".join(map(str, product_bom_ids)) + "])]"
res["nodestroy"] = False
return res

View File

@@ -0,0 +1,263 @@
# Copyright 2015-22 ForgeFlow S.L. (https://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
import operator as py_operator
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class MrpBom(models.Model):
_inherit = "mrp.bom"
_order = "sequence, code, product_default_code, id"
@api.depends("bom_line_ids.bom_id", "product_id", "product_tmpl_id")
def _compute_product_has_other_bom(self):
for bom in self:
if bom.product_id:
bom_ids = self.env["mrp.bom"].search(
[("product_id", "=", bom.product_id.id), ("id", "!=", bom.id)],
)
else:
bom_ids = self.env["mrp.bom"].search(
[
("product_tmpl_id", "=", bom.product_tmpl_id.id),
("id", "!=", bom.id),
],
)
if bom_ids:
bom.product_has_other_bom = True
else:
bom.product_has_other_bom = False
@api.depends("bom_line_ids.bom_id", "product_id", "product_tmpl_id")
def _compute_parent_bom_ids(self):
for bom in self:
parent_bom_line_ids = self.env["mrp.bom.line"]._bom_line_find(
product_tmpl=bom.product_id.product_tmpl_id or bom.product_tmpl_id,
product=bom.product_id,
)
if parent_bom_line_ids:
bom.parent_bom_ids = parent_bom_line_ids.bom_id
bom.has_parent = True
else:
bom.parent_bom_ids = False
bom.has_parent = False
@api.depends("bom_line_ids.bom_id", "bom_line_ids.product_id")
def _compute_child_bom_ids(self):
for bom in self:
bom_line_ids = bom.bom_line_ids
bom.child_bom_ids = bom_line_ids.child_bom_id
bom.has_child = bool(bom.child_bom_ids)
def _search_has_child(self, operator, value):
if operator not in ["=", "!="]:
raise UserError(_("This operator is not supported"))
if value == "True":
value = True
elif value == "False":
value = False
if not isinstance(value, bool):
raise UserError(_("Value should be True or False (not %s)") % value)
ops = {"=": py_operator.eq, "!=": py_operator.ne}
ids = []
for bom in self.search([]):
if ops[operator](value, bom.has_child):
ids.append(bom.id)
return [("id", "in", ids)]
def _search_has_parent(self, operator, value):
if operator not in ["=", "!="]:
raise UserError(_("This operator is not supported"))
if value == "True":
value = True
elif value == "False":
value = False
if not isinstance(value, bool):
raise UserError(_("Value should be True or False (not %s)") % value)
ops = {"=": py_operator.eq, "!=": py_operator.ne}
ids = []
for bom in self.search([]):
if ops[operator](value, bom.has_parent):
ids.append(bom.id)
return [("id", "in", ids)]
@api.depends(
"product_id",
"product_id.default_code",
"product_id.product_tmpl_id.default_code",
"product_tmpl_id.default_code",
)
def _compute_internal_reference(self):
for bom in self:
bom.product_default_code = (
bom.product_id.default_code
or bom.product_id.product_tmpl_id.default_code
or bom.product_tmpl_id.default_code
)
child_bom_ids = fields.One2many("mrp.bom", compute="_compute_child_bom_ids")
parent_bom_ids = fields.One2many("mrp.bom", compute="_compute_parent_bom_ids")
has_child = fields.Boolean(
string="Has components",
compute="_compute_child_bom_ids",
search="_search_has_child",
)
has_parent = fields.Boolean(
string="Is component",
compute="_compute_parent_bom_ids",
search="_search_has_parent",
)
product_has_other_bom = fields.Boolean(
string="Product has other BoMs",
compute="_compute_product_has_other_bom",
)
product_default_code = fields.Char(
string="Internal Reference",
compute="_compute_internal_reference",
store="True",
)
def action_open_child_tree_view(self):
self.ensure_one()
res = self.env["ir.actions.actions"]._for_xml_id("mrp.mrp_bom_form_action")
res["context"] = {"default_bom_line_ids": self.bom_line_ids.ids}
if self.child_bom_ids:
res["domain"] = (
"[('id', 'in', [" + ",".join(map(str, self.child_bom_ids.ids)) + "])]"
)
return res
def action_open_parent_tree_view(self):
self.ensure_one()
res = self.env["ir.actions.actions"]._for_xml_id("mrp.mrp_bom_form_action")
if self.parent_bom_ids:
res["domain"] = (
"[('id', 'in', [" + ",".join(map(str, self.parent_bom_ids.ids)) + "])]"
)
return res
def action_open_product_other_bom_tree_view(self):
self.ensure_one()
if self.product_id:
product_bom_ids = self.env["mrp.bom"].search(
[("product_id", "=", self.product_id.id), ("id", "!=", self.id)],
)
else:
product_bom_ids = self.env["mrp.bom"].search(
[
("product_tmpl_id", "=", self.product_tmpl_id.id),
("id", "!=", self.id),
],
)
res = self.env["ir.actions.actions"]._for_xml_id("mrp.mrp_bom_form_action")
if self.product_id:
res["context"] = {
"default_product_id": self.product_id.id,
"default_product_tmpl_id": self.product_id.product_tmpl_id.id,
}
elif self.product_tmpl_id:
res["context"] = {
"default_product_tmpl_id": self.product_tmpl_id.id,
}
res["domain"] = (
"[('id', 'in', [" + ",".join(map(str, product_bom_ids.ids)) + "])]"
)
return res
class MrpBomLine(models.Model):
_inherit = "mrp.bom.line"
has_bom = fields.Boolean(
string="Has sub BoM",
compute="_compute_child_bom_id",
)
@api.depends("product_id", "bom_id")
def _compute_child_bom_id(self):
super()._compute_child_bom_id()
for line in self:
line.has_bom = bool(line.child_bom_id)
def action_open_product_bom_tree_view(self):
self.ensure_one()
res = self.env["ir.actions.actions"]._for_xml_id("mrp.mrp_bom_form_action")
res["domain"] = (
"[('id', 'in', [" + ",".join(map(str, self.child_bom_id.ids)) + "])]"
)
return res
@api.model
def _bom_line_find_domain(
self,
product_tmpl=None,
product=None,
picking_type=None,
company_id=False,
bom_type=False,
):
if product:
if not product_tmpl:
product_tmpl = product.product_tmpl_id
domain = [
"|",
("product_id", "=", product.id),
"&",
("product_id", "=", False),
("product_tmpl_id", "=", product_tmpl.id),
]
elif product_tmpl:
domain = [("product_tmpl_id", "=", product_tmpl.id)]
else:
# neither product nor template, makes no sense to search
raise UserError(
_(
"You should provide either a product or "
"a product template to search a BoM Line"
)
)
if picking_type:
domain += [
"|",
("bom_id.picking_type_id", "=", picking_type.id),
("bom_id.picking_type_id", "=", False),
]
if company_id or self.env.context.get("company_id"):
domain = domain + [
"|",
("company_id", "=", False),
("company_id", "=", company_id or self.env.context.get("company_id")),
]
if bom_type:
domain += [("bom_id.type", "=", bom_type)]
# order to prioritize bom line with product_id over the one without
return domain
@api.model
def _bom_line_find(
self,
product_tmpl=None,
product=None,
picking_type=None,
company_id=False,
bom_type=False,
):
"""Finds BoM lines for particular product, picking and company"""
if (
product
and product.type == "service"
or product_tmpl
and product_tmpl.type == "service"
):
return self.env["mrp.bom.line"]
domain = self._bom_line_find_domain(
product_tmpl=product_tmpl,
product=product,
picking_type=picking_type,
company_id=company_id,
bom_type=bom_type,
)
if domain is False:
return self.env["mrp.bom.line"]
return self.search(domain, order="sequence, product_id")

View File

@@ -1 +1,2 @@
* Jordi Ballester Alomar <jordi.ballester@forgeflow.com> * Jordi Ballester Alomar <jordi.ballester@forgeflow.com>
* Miquel Raïch Regué <miquel.raich@forgeflow.com>

View File

@@ -1,12 +1,5 @@
This module was written to extend the functionality of Bill of This module extends the functionality of Bill of Materials
Materials to support users to better maintain the BOM hierarchy. to support users to better maintain the BoM hierarchy.
This module replaces the existing BOM tree views with a new one, from The user can navigate from the tree view to child's BoM or parent's BoM,
which the user can create a complete BOM hierarchy. or to the product's BoM components with a single click.
The user can navigate from the tree view to child BOM's, or to the
product's BOM components with a single click.
The user can now search using the field 'Complete Reference' (or Name) to
find all the BOM hierarchy associated to a particular BOM Reference (or
Name) at once.

View File

@@ -1,2 +1,2 @@
To use this module, you need to go to 'Manufacturing | Products | Bill of To use this module, you need to go to 'Manufacturing | Products | Bill of
Materials Hierarchy'. Materials'.

View File

@@ -1,19 +1,19 @@
<section class="oe_container"> <section class="oe_container">
<div class="oe_row oe_spaced"> <div class="oe_row oe_spaced">
<div class="oe_span12"> <div class="oe_span12">
<h2 class="oe_slogan">MRP BOM Hierarchy</h2> <h2 class="oe_slogan">MRP BoM Hierarchy</h2>
<p> <p>
This module was written to extend the functionality of Bill of This module was written to extend the functionality of Bill of
Materials to support users to better maintain the BOM hierarchy. Materials to support users to better maintain the BoM hierarchy.
This module replaces the existing BOM tree views with a new one, from This module replaces the existing BoM tree views with a new one, from
which the user can create a complete BOM hierarchy. which the user can create a complete BoM hierarchy.
The user can navigate from the tree view to child BOM's, or to the The user can navigate from the tree view to child BoM's, or to the
product's BOM components with a single click. product's BoM components with a single click.
The user can now search using the field 'Complete Reference' (or Name) to The user can now search using the field 'Complete Reference' (or Name) to
find all the BOM hierarchy associated to a particular BOM Reference (or find all the BoM hierarchy associated to a particular BoM Reference (or
Name) at once. Name) at once.
</p> </p>
</div> </div>
@@ -76,7 +76,7 @@ Materials Hierarchy'
<h3>Contributors</h3> <h3>Contributors</h3>
<ul> <ul>
<li>Jordi Ballester Alomar &lt;<a <li>Jordi Ballester Alomar &lt;<a
href="mailto:jordi.ballester@eficent.com">jordi.ballester@eficent.com</a>&gt;</li> href="mailto:jordi.ballester@forgeflow.com">jordi.ballester@forgeflow.com</a>&gt;</li>
</ul> </ul>
</div> </div>
<div class="oe_span12"> <div class="oe_span12">

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<data>
<record id="mrp_bom_form_view" model="ir.ui.view"> <record id="mrp_bom_form_view" model="ir.ui.view">
<field name="name">mrp.bom.form</field> <field name="name">mrp.bom.form</field>
@@ -8,17 +7,17 @@
<field name="inherit_id" ref="mrp.mrp_bom_form_view" /> <field name="inherit_id" ref="mrp.mrp_bom_form_view" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath <xpath
expr="//notebook/page/field[@name='bom_lines']/tree//field[@name='product_id']" expr="//notebook/page/field[@name='bom_line_ids']/tree/field[@name='product_tmpl_id']"
position="after" position="after"
> >
<field name="is_parent" invisible="1" /> <field name="has_bom" invisible="1" />
<field name="product_has_own_bom" />
<button <button
string="View product's BOM" title="View product's BoM"
name="action_openProductBOMTreeView" name="action_open_product_bom_tree_view"
type="object" type="object"
icon="terp-folder-orange" class="btn btn-link"
attrs="{'invisible': [('is_parent', '=', True)]}" icon="fa-folder-open"
attrs="{'invisible': [('has_bom', '!=', True)]}"
/> />
</xpath> </xpath>
</field> </field>
@@ -29,15 +28,19 @@
<field name="model">mrp.bom</field> <field name="model">mrp.bom</field>
<field name="inherit_id" ref="mrp.view_mrp_bom_filter" /> <field name="inherit_id" ref="mrp.view_mrp_bom_filter" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//field[@name='product_id']" position="before"> <xpath expr="//separator[1]" position="before">
<field name="complete_bom_hierarchy_code" /> <field name="has_parent" />
<field name="complete_bom_hierarchy_name" /> <field name="has_child" />
<field name="bom_id" /> <separator />
<field name="is_parent" />
<filter <filter
string="Parent BOM" string="Has parent BoMs"
name="parent_bom" name="parent_bom"
domain="[('is_parent','=','True')]" domain="[('has_parent','=','True')]"
/>
<filter
string="Has child BoMs"
name="child_bom"
domain="[('has_child','=','True')]"
/> />
</xpath> </xpath>
</field> </field>
@@ -46,68 +49,40 @@
<record id="mrp_bom_hierarchy_tree_view" model="ir.ui.view"> <record id="mrp_bom_hierarchy_tree_view" model="ir.ui.view">
<field name="name">mrp.bom.tree</field> <field name="name">mrp.bom.tree</field>
<field name="model">mrp.bom</field> <field name="model">mrp.bom</field>
<field name="field_parent">child_complete_ids</field> <field name="inherit_id" ref="mrp.mrp_bom_tree_view" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Bill of Materials" colors="blue:method"> <field name="active" position="before">
<button <button
string="Child BOMs" title="Parent BoMs"
name="action_openChildTreeView" name="action_open_parent_tree_view"
type="object" type="object"
icon="STOCK_GO_DOWN" class="btn btn-link"
icon="fa-level-up"
attrs="{'invisible': [('has_parent', '!=', True)]}"
/> />
<field name="has_child" />
<field name="complete_bom_hierarchy_code" />
<field name="bom_hierarchy_indent" />
<field name="sequence" />
<field name="code" />
<field name="position" />
<field name="name" />
<field name="bom_id" />
<field name="is_parent" invisible="1" />
<field
name="product_id"
context="{'default_supply_method':'produce'}"
on_change="onchange_product_id(product_id, name)"
/>
<field name="product_has_own_bom" />
<button <button
string="View product's BOM" title="Child BoMs"
name="action_openProductBOMTreeView" name="action_open_child_tree_view"
type="object" type="object"
icon="terp-folder-orange" class="btn btn-link"
attrs="{'invisible': [('is_parent', '=', True)]}" icon="fa-level-down"
attrs="{'invisible': [('has_child', '!=', True)]}"
/> />
<field name="product_qty" /> <field name="has_parent" invisible="1" />
<field <field name="has_child" invisible="1" />
name="product_uom" </field>
on_change="onchange_uom(product_id, product_uom)" <field name="type" position="before">
groups="product.group_uom" <field name="product_has_other_bom" invisible="1" />
<button
title="View product's other BoMs"
name="action_open_product_other_bom_tree_view"
type="object"
class="btn btn-link"
icon="fa-folder-open"
attrs="{'invisible': [('product_has_other_bom', '!=', True)]}"
/> />
<field name="type" /> </field>
<field name="method" />
<field name="routing_id" groups="mrp.group_mrp_routings" />
<field name="date_start" />
<field name="date_stop" />
</tree>
</field> </field>
</record> </record>
<record id="mrp.mrp_bom_form_action2" model="ir.actions.act_window">
<field name="view_id" ref="mrp_bom_hierarchy_tree_view" />
</record>
<record id="mrp.mrp_bom_form_action" model="ir.actions.act_window">
<field name="view_id" ref="mrp_bom_hierarchy_tree_view" />
</record>
<record id="action_mrp_bom_hierarchy_tree2" model="ir.actions.act_window">
<field name="name">Bill of Material Hierarchy</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.bom</field>
<field name="view_type">form</field>
<field name="view_id" ref="mrp_bom_hierarchy_tree_view" />
<field name="domain">[]</field>
</record>
</data>
</odoo> </odoo>