summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorSimon Lejeune <sle@openerp.com>2020-04-21 10:10:45 +0200
committerSimon Lejeune <sle@openerp.com>2020-06-15 16:59:40 +0200
commitc660770ebd84c2c0d9e56ca5897effc6c138b827 (patch)
tree124f67a64758d2ff0899e29260a80b251f56f02a /addons
parentbeccf82e09d536255d9d9cb9bfe58ebae2559843 (diff)
[REF] mrp: remove routing model
Set the operations directly on the Bill of Material. Duplicate the demo data where a routing was shared. Adapt the tests. Remove the following feature: - set the same routing on parent and kit child bom - when planning, if the component of the kit have the same operation than a component of the parent bom, merge these operations task-2241471
Diffstat (limited to 'addons')
-rw-r--r--addons/mrp/data/mrp_data.xml9
-rw-r--r--addons/mrp/data/mrp_demo.xml159
-rw-r--r--addons/mrp/models/mrp_bom.py52
-rw-r--r--addons/mrp/models/mrp_production.py59
-rw-r--r--addons/mrp/models/mrp_routing.py35
-rw-r--r--addons/mrp/models/stock_move.py22
-rw-r--r--addons/mrp/report/mrp_report_bom_structure.py12
-rw-r--r--addons/mrp/security/ir.model.access.csv2
-rw-r--r--addons/mrp/security/mrp_security.xml6
-rw-r--r--addons/mrp/tests/common.py42
-rw-r--r--addons/mrp/tests/test_bom.py69
-rw-r--r--addons/mrp/tests/test_cancel_mo.py6
-rw-r--r--addons/mrp/tests/test_multicompany.py19
-rw-r--r--addons/mrp/tests/test_order.py16
-rw-r--r--addons/mrp/tests/test_procurement.py10
-rw-r--r--addons/mrp/tests/test_stock.py11
-rw-r--r--addons/mrp/tests/test_workorder_operation.py267
-rw-r--r--addons/mrp/views/mrp_bom_views.xml29
-rw-r--r--addons/mrp/views/mrp_production_views.xml34
-rw-r--r--addons/mrp/views/mrp_routing_views.xml114
-rw-r--r--addons/mrp/views/mrp_workcenter_views.xml2
-rw-r--r--addons/mrp/views/mrp_workorder_views.xml2
-rw-r--r--addons/mrp/views/res_config_settings_views.xml5
-rw-r--r--addons/mrp/views/stock_move_views.xml4
-rw-r--r--addons/mrp/wizard/change_production_qty.py2
-rw-r--r--addons/mrp_account/models/product.py2
-rw-r--r--addons/mrp_account/tests/test_bom_price.py92
-rw-r--r--addons/mrp_account/tests/test_mrp_account.py1
-rw-r--r--addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py4
-rw-r--r--addons/mrp_product_expiry/tests/test_mrp_product_expiry.py26
30 files changed, 492 insertions, 621 deletions
diff --git a/addons/mrp/data/mrp_data.xml b/addons/mrp/data/mrp_data.xml
index 64041d0b39b..c41f0576a30 100644
--- a/addons/mrp/data/mrp_data.xml
+++ b/addons/mrp/data/mrp_data.xml
@@ -2,15 +2,6 @@
<odoo>
<data noupdate="1">
- <record id="sequence_mrp_route" model="ir.sequence">
- <field name="name">Routing</field>
- <field name="code">mrp.routing</field>
- <field name="prefix">RO/</field>
- <field name="padding">5</field>
- <field name="number_next">1</field>
- <field name="number_increment">1</field>
- <field name="company_id" eval="False"/>
- </record>
<function model="res.company" name="create_missing_unbuild_sequences" />
<!--
Stock rules and routes
diff --git a/addons/mrp/data/mrp_demo.xml b/addons/mrp/data/mrp_demo.xml
index 99a0173d83d..da787db79cd 100644
--- a/addons/mrp/data/mrp_demo.xml
+++ b/addons/mrp/data/mrp_demo.xml
@@ -28,83 +28,6 @@
<field name="resource_calendar_id" ref="resource.resource_calendar_std"/>
</record>
-
-
- <!-- Resource: mrp.routing -->
-
- <record id="mrp_routing_0" model="mrp.routing">
- <field name="name">Primary Assembly</field>
- </record>
-
- <record id="mrp_routing_1" model="mrp.routing">
- <field name="name">Secondary Assembly</field>
- </record>
-
- <record id="mrp_routing_2" model="mrp.routing">
- <field name="name">Manual Component's Assembly</field>
- </record>
-
- <record id="mrp_routing_3" model="mrp.routing">
- <field name="name">Assemble Furniture</field>
- </record>
-
-
- <!-- Resource: mrp.routing.workcenter -->
-
- <record id="mrp_routing_workcenter_0" model="mrp.routing.workcenter">
- <field name="routing_id" ref="mrp_routing_0"/>
- <field name="workcenter_id" ref="mrp_workcenter_3"/>
- <field name="name">Manual Assembly</field>
- <field name="time_cycle">60</field>
- <field name="sequence">5</field>
- <field name="worksheet" type="base64" file="mrp/static/img/assebly-worksheet.pdf"/>
- </record>
-
- <record id="mrp_routing_workcenter_1" model="mrp.routing.workcenter">
- <field name="routing_id" ref="mrp_routing_1"/>
- <field name="workcenter_id" ref="mrp_workcenter_3"/>
- <field name="name">Long time assembly</field>
- <field name="time_cycle">180</field>
- <field name="sequence">15</field>
- <field name="worksheet" type="base64" file="mrp/static/img/cutting-worksheet.pdf"/>
- </record>
-
- <record id="mrp_routing_workcenter_3" model="mrp.routing.workcenter">
- <field name="routing_id" ref="mrp_routing_1"/>
- <field name="workcenter_id" ref="mrp_workcenter_3"/>
- <field name="name">Testing</field>
- <field name="time_cycle">60</field>
- <field name="sequence">10</field>
- <field name="worksheet" type="base64" file="mrp/static/img/assebly-worksheet.pdf"/>
- </record>
-
- <record id="mrp_routing_workcenter_4" model="mrp.routing.workcenter">
- <field name="routing_id" ref="mrp_routing_1"/>
- <field name="workcenter_id" ref="mrp_workcenter_1"/>
- <field name="name">Packing</field>
- <field name="time_cycle">30</field>
- <field name="sequence">5</field>
- <field name="worksheet" type="base64" file="mrp/static/img/cutting-worksheet.pdf"/>
- </record>
-
- <record id="mrp_routing_workcenter_2" model="mrp.routing.workcenter">
- <field name="routing_id" ref="mrp_routing_2"/>
- <field name="workcenter_id" ref="mrp_workcenter_2"/>
- <field name="time_cycle">120</field>
- <field name="sequence">5</field>
- <field name="name">Manual Assembly</field>
- <field name="worksheet" type="base64" file="mrp/static/img/assebly-worksheet.pdf"/>
- </record>
-
- <record id="mrp_routing_workcenter_5" model="mrp.routing.workcenter">
- <field name="routing_id" ref="mrp_routing_3"/>
- <field name="workcenter_id" ref="mrp_workcenter_3"/>
- <field name="time_cycle">120</field>
- <field name="sequence">10</field>
- <field name="name">Assembly</field>
- <field name="worksheet" type="base64" file="mrp/static/img/cutting-worksheet.pdf"/>
- </record>
-
<!-- Resource: mrp.bom -->
<record id="product.product_product_3_product_template" model="product.template">
@@ -114,7 +37,14 @@
<field name="product_tmpl_id" ref="product.product_product_3_product_template"/>
<field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">1</field>
- <field name="routing_id" ref="mrp_routing_0"/>
+ </record>
+ <record id="mrp_routing_workcenter_0" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_manufacture"/>
+ <field name="workcenter_id" ref="mrp_workcenter_3"/>
+ <field name="name">Manual Assembly</field>
+ <field name="time_cycle">60</field>
+ <field name="sequence">5</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/assebly-worksheet.pdf"/>
</record>
<record id="mrp_bom_manufacture_line_1" model="mrp.bom.line">
@@ -296,7 +226,14 @@
<field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">3</field>
<field name="consumption">flexible</field>
- <field name="routing_id" ref="mrp_routing_3"/>
+ </record>
+ <record id="mrp_routing_workcenter_5" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_desk"/>
+ <field name="workcenter_id" ref="mrp_workcenter_3"/>
+ <field name="time_cycle">120</field>
+ <field name="sequence">10</field>
+ <field name="name">Assembly</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/cutting-worksheet.pdf"/>
</record>
<record id="mrp_bom_desk_line_1" model="mrp.bom.line">
@@ -350,8 +287,16 @@
<field name="product_tmpl_id" ref="product_product_computer_desk_head_product_template"/>
<field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">1</field>
- <field name="routing_id" ref="mrp_routing_0"/>
</record>
+ <record id="mrp_routing_workcenter_0" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_table_top"/>
+ <field name="workcenter_id" ref="mrp_workcenter_3"/>
+ <field name="name">Manual Assembly</field>
+ <field name="time_cycle">60</field>
+ <field name="sequence">5</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/assebly-worksheet.pdf"/>
+ </record>
+
<record id="mrp_bom_line_wood_panel" model="mrp.bom.line">
<field name="product_id" ref="product_product_wood_panel"/>
<field name="product_qty">2</field>
@@ -371,7 +316,32 @@
<field name="product_tmpl_id" ref="product_product_plastic_laminate_product_template"/>
<field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">1</field>
- <field name="routing_id" ref="mrp_routing_1"/>
+ </record>
+ <record id="mrp_routing_workcenter_1" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_plastic_laminate"/>
+ <field name="workcenter_id" ref="mrp_workcenter_3"/>
+ <field name="name">Long time assembly</field>
+ <field name="time_cycle">180</field>
+ <field name="sequence">15</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/cutting-worksheet.pdf"/>
+ </record>
+
+ <record id="mrp_routing_workcenter_3" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_plastic_laminate"/>
+ <field name="workcenter_id" ref="mrp_workcenter_3"/>
+ <field name="name">Testing</field>
+ <field name="time_cycle">60</field>
+ <field name="sequence">10</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/assebly-worksheet.pdf"/>
+ </record>
+
+ <record id="mrp_routing_workcenter_4" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_plastic_laminate"/>
+ <field name="workcenter_id" ref="mrp_workcenter_1"/>
+ <field name="name">Packing</field>
+ <field name="time_cycle">30</field>
+ <field name="sequence">5</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/cutting-worksheet.pdf"/>
</record>
<record id="mrp_bom_line_plastic_laminate" model="mrp.bom.line">
<field name="product_id" ref="product_product_ply_veneer"/>
@@ -596,9 +566,34 @@
<field name="product_tmpl_id" ref="product.product_product_27_product_template"/>
<field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="sequence">2</field>
- <field name="routing_id" ref="mrp_routing_1"/>
<field name="code">SEC-ASSEM</field>
</record>
+ <record id="mrp_routing_workcenter_1" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_laptop_cust_rout"/>
+ <field name="workcenter_id" ref="mrp_workcenter_3"/>
+ <field name="name">Long time assembly</field>
+ <field name="time_cycle">180</field>
+ <field name="sequence">15</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/cutting-worksheet.pdf"/>
+ </record>
+
+ <record id="mrp_routing_workcenter_3" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_laptop_cust_rout"/>
+ <field name="workcenter_id" ref="mrp_workcenter_3"/>
+ <field name="name">Testing</field>
+ <field name="time_cycle">60</field>
+ <field name="sequence">10</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/assebly-worksheet.pdf"/>
+ </record>
+
+ <record id="mrp_routing_workcenter_4" model="mrp.routing.workcenter">
+ <field name="bom_id" ref="mrp_bom_laptop_cust_rout"/>
+ <field name="workcenter_id" ref="mrp_workcenter_1"/>
+ <field name="name">Packing</field>
+ <field name="time_cycle">30</field>
+ <field name="sequence">5</field>
+ <field name="worksheet" type="base64" file="mrp/static/img/cutting-worksheet.pdf"/>
+ </record>
<record id="mrp_bom_laptop_cust_rout_line_1" model="mrp.bom.line">
<field name="product_id" ref="product_product_drawer_drawer"/>
<field name="product_qty">1</field>
diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py
index 362b63e5ee8..127f07af4b4 100644
--- a/addons/mrp/models/mrp_bom.py
+++ b/addons/mrp/models/mrp_bom.py
@@ -48,10 +48,7 @@ class MrpBom(models.Model):
help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control", domain="[('category_id', '=', product_uom_category_id)]")
product_uom_category_id = fields.Many2one(related='product_tmpl_id.uom_id.category_id')
sequence = fields.Integer('Sequence', help="Gives the sequence order when displaying a list of bills of material.")
- routing_id = fields.Many2one(
- 'mrp.routing', 'Routing', check_company=True,
- help="The operations for producing this BoM. When a routing is specified, the production orders will "
- " be executed through work orders, otherwise everything is processed in the production order itself. ")
+ operation_ids = fields.One2many('mrp.routing.workcenter', 'bom_id', 'Operations', copy=True)
ready_to_produce = fields.Selection([
('all_available', ' When all components are available'),
('asap', 'When components for 1st operation are available')], string='Manufacturing Readiness',
@@ -119,11 +116,6 @@ class MrpBom(models.Model):
for line in self.bom_line_ids:
line.bom_product_template_attribute_value_ids = False
- @api.onchange('routing_id')
- def onchange_routing_id(self):
- for line in self.bom_line_ids:
- line.operation_id = False
-
@api.model
def name_create(self, name):
# prevent to use string as product_tmpl_id
@@ -261,12 +253,6 @@ class MrpBomLine(models.Model):
sequence = fields.Integer(
'Sequence', default=1,
help="Gives the sequence order when displaying.")
- routing_id = fields.Many2one(
- 'mrp.routing', 'Routing',
- related='bom_id.routing_id', store=True, readonly=False,
- help="The list of operations to produce the finished product. The routing is mainly used to "
- "compute work center costs during operations and to plan future loads on work centers "
- "based on production planning.")
bom_id = fields.Many2one(
'mrp.bom', 'Parent BoM',
index=True, ondelete='cascade', required=True)
@@ -276,9 +262,10 @@ class MrpBomLine(models.Model):
'product.template.attribute.value', string="Apply on Variants", ondelete='restrict',
domain="[('id', 'in', possible_bom_product_template_attribute_value_ids)]",
help="BOM Product Variants needed to apply this line.")
+ allowed_operation_ids = fields.Many2many('mrp.routing.workcenter', compute='_compute_allowed_operation_ids')
operation_id = fields.Many2one(
'mrp.routing.workcenter', 'Consumed in Operation', check_company=True,
- domain="[('routing_id', '=', routing_id), '|', ('company_id', '=', company_id), ('company_id', '=', False)]",
+ domain="[('id', 'in', allowed_operation_ids)]",
help="The operation where the components are consumed, or the finished products created.")
child_bom_id = fields.Many2one(
'mrp.bom', 'Sub BoM', compute='_compute_child_bom_id')
@@ -327,6 +314,20 @@ class MrpBomLine(models.Model):
for line in self:
line.child_line_ids = line.child_bom_id.bom_line_ids.ids or False
+ @api.depends('bom_id')
+ def _compute_allowed_operation_ids(self):
+ for bom_line in self:
+ if not bom_line.bom_id.operation_ids:
+ bom_line.allowed_operation_ids = self.env['mrp.routing.workcenter']
+ else:
+ operation_domain = [
+ ('id', 'in', bom_line.bom_id.operation_ids.ids),
+ '|',
+ ('company_id', '=', bom_line.company_id.id),
+ ('company_id', '=', False)
+ ]
+ bom_line.allowed_operation_ids = self.env['mrp.routing.workcenter'].search(operation_domain)
+
@api.onchange('product_uom_id')
def onchange_product_uom_id(self):
res = {}
@@ -403,11 +404,24 @@ class MrpByProduct(models.Model):
default=1.0, digits='Product Unit of Measure', required=True)
product_uom_id = fields.Many2one('uom.uom', 'Unit of Measure', required=True)
bom_id = fields.Many2one('mrp.bom', 'BoM', ondelete='cascade')
- routing_id = fields.Many2one(
- 'mrp.routing', 'Routing', store=True, related='bom_id.routing_id')
+ allowed_operation_ids = fields.Many2many('mrp.routing.workcenter', compute='_compute_allowed_operation_ids')
operation_id = fields.Many2one(
'mrp.routing.workcenter', 'Produced in Operation', check_company=True,
- domain="[('routing_id', '=', routing_id), '|', ('company_id', '=', company_id), ('company_id', '=', False)]")
+ domain="[('id', 'in', allowed_operation_ids)]")
+
+ @api.depends('bom_id')
+ def _compute_allowed_operation_ids(self):
+ for byproduct in self:
+ if not byproduct.bom_id.operation_ids:
+ byproduct.allowed_operation_ids = self.env['mrp.routing.workcenter']
+ else:
+ operation_domain = [
+ ('id', 'in', byproduct.bom_id.operation_ids.ids),
+ '|',
+ ('company_id', '=', byproduct.company_id.id),
+ ('company_id', '=', False)
+ ]
+ byproduct.allowed_operation_ids = self.env['mrp.routing.workcenter'].search(operation_domain)
@api.onchange('product_id')
def onchange_product_id(self):
diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index f6529bff906..f266a176813 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -130,12 +130,7 @@ class MrpProduction(models.Model):
('type', '=', 'normal')]""",
check_company=True,
help="Bill of Materials allow you to define the list of required components to make a finished product.")
- routing_id = fields.Many2one(
- 'mrp.routing', 'Routing',
- readonly=True, compute='_compute_routing', store=True,
- help="The list of operations (list of work centers) to produce the finished product. The routing "
- "is mainly used to compute work center costs during operations and to plan future loads on "
- "work centers based on production planning.")
+ bom_has_operations = fields.Boolean(compute='_compute_bom_has_operations')
state = fields.Selection([
('draft', 'Draft'),
@@ -277,6 +272,14 @@ class MrpProduction(models.Model):
def _set_date_planned_finished(self):
self.move_finished_ids.write({'date_expected': self.date_planned_finished})
+ @api.depends('bom_id')
+ def _compute_bom_has_operations(self):
+ for production in self:
+ if not production.bom_id:
+ production.bom_has_operations = False
+ else:
+ production.bom_has_operations = len(production.bom_id.operation_ids) > 0
+
@api.depends('move_raw_ids.delay_alert_date')
def _compute_delay_alert_date(self):
delay_alert_date_data = self.env['stock.move'].read_group([('id', 'in', self.move_raw_ids.ids), ('delay_alert_date', '!=', False)], ['delay_alert_date:max'], 'raw_material_production_id')
@@ -367,14 +370,6 @@ class MrpProduction(models.Model):
for production in self:
production.finished_move_line_ids = production.move_finished_ids.mapped('move_line_ids')
- @api.depends('bom_id.routing_id', 'bom_id.routing_id.operation_ids')
- def _compute_routing(self):
- for production in self:
- if production.bom_id.routing_id.operation_ids:
- production.routing_id = production.bom_id.routing_id.id
- else:
- production.routing_id = False
-
@api.depends('workorder_ids')
def _compute_workorder_count(self):
data = self.env['mrp.workorder'].read_group([('production_id', 'in', self.ids)], ['production_id'], ['production_id'])
@@ -418,7 +413,7 @@ class MrpProduction(models.Model):
production.state = 'done'
elif production.move_finished_ids.filtered(lambda m: m.state not in ('cancel', 'done') and m.product_id.id == production.product_id.id)\
and (production.qty_produced >= production.product_qty)\
- and (not production.routing_id or all(wo_state in ('cancel', 'done') for wo_state in production.workorder_ids.mapped('state'))):
+ and (not production.bom_id.operation_ids or all(wo_state in ('cancel', 'done') for wo_state in production.workorder_ids.mapped('state'))):
production.state = 'to_close'
elif production.workorder_ids and any(wo_state in ('progress') for wo_state in production.workorder_ids.mapped('state'))\
or production.qty_produced > 0 and production.qty_produced < production.product_qty:
@@ -435,7 +430,7 @@ class MrpProduction(models.Model):
if production.state not in ('draft', 'done', 'cancel'):
relevant_move_state = production.move_raw_ids._get_relevant_state_among_moves()
if relevant_move_state == 'partially_available':
- if production.routing_id and production.routing_id.operation_ids and production.bom_id.ready_to_produce == 'asap':
+ if production.bom_id.operation_ids and production.bom_id.ready_to_produce == 'asap':
production.reservation_state = production._get_ready_to_produce_state()
else:
production.reservation_state = 'confirmed'
@@ -516,7 +511,7 @@ class MrpProduction(models.Model):
@api.onchange('date_planned_start')
def _onchange_date_planned_start(self):
- if not self.routing_id:
+ if not self.bom_id.operation_ids:
self.date_planned_finished = self.date_planned_start + datetime.timedelta(hours=1)
@api.onchange('bom_id', 'product_id', 'product_qty', 'product_uom_id')
@@ -537,7 +532,7 @@ class MrpProduction(models.Model):
else:
self.move_raw_ids = [(2, move.id) for move in self.move_raw_ids.filtered(lambda m: m.bom_line_id)]
- @api.onchange('location_src_id', 'move_raw_ids', 'routing_id')
+ @api.onchange('location_src_id', 'move_raw_ids', 'bom_id')
def _onchange_location(self):
source_location = self.location_src_id
self.move_raw_ids.update({
@@ -567,7 +562,7 @@ class MrpProduction(models.Model):
raise UserError(_('You cannot move a planned manufacturing order.'))
if 'move_raw_ids' in vals and production.state != 'draft':
production._autoconfirm_production()
- if not production.routing_id and vals.get('date_planned_start') and not vals.get('date_planned_finished'):
+ if not production.bom_id.operation_ids and vals.get('date_planned_start') and not vals.get('date_planned_finished'):
new_date_planned_start = fields.Datetime.to_datetime(vals.get('date_planned_start'))
if not production.date_planned_finished or new_date_planned_start >= production.date_planned_finished:
production.date_planned_finished = new_date_planned_start + datetime.timedelta(hours=1)
@@ -738,14 +733,12 @@ class MrpProduction(models.Model):
def _get_ready_to_produce_state(self):
""" returns 'assigned' if enough components are reserved in order to complete
- the first operation in the routing. If not returns 'waiting'
+ the first operation of the bom. If not returns 'waiting'
"""
self.ensure_one()
- first_operation = self.routing_id.operation_ids[0]
- # Get BoM line related to first opeation in rounting. If there is only
- # one opeation in the routing then it will need all BoM lines.
+ first_operation = self.bom_id.operation_ids[0]
bom_line_ids = self.env['mrp.bom.line']
- if len(self.routing_id.operation_ids) == 1:
+ if len(self.bom_id.operation_ids) == 1:
moves_in_first_operation = self.move_raw_ids
else:
moves_in_first_operation = self.move_raw_ids.filtered(lambda move: move.operation_id == first_operation)
@@ -852,7 +845,7 @@ class MrpProduction(models.Model):
def button_plan(self):
""" Create work orders. And probably do stuff, like things. """
- orders_to_plan = self.filtered(lambda order: order.routing_id and order.state == 'confirmed')
+ orders_to_plan = self.filtered(lambda order: order.bom_id.operation_ids and order.state == 'confirmed')
for order in orders_to_plan:
order.move_raw_ids.filtered(lambda m: m.state == 'draft')._action_confirm()
# `propagate_date` enables the automatic rescheduling which could lead to hard to
@@ -952,8 +945,8 @@ class MrpProduction(models.Model):
workorders = self.env['mrp.workorder']
original_one = False
for bom, bom_data in exploded_boms:
- # If the routing of the parent BoM and phantom BoM are the same, don't recreate work orders, but use one master routing
- if bom.routing_id.id and (not bom_data['parent_line'] or bom_data['parent_line'].bom_id.routing_id.id != bom.routing_id.id):
+ # If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
+ if bom.operation_ids and (not bom_data['parent_line'] or bom_data['parent_line'].bom_id.operation_ids != bom.operation_ids):
temp_workorders = self._workorders_create(bom, bom_data)
workorders += temp_workorders
if temp_workorders: # In order to avoid two "ending work orders"
@@ -975,7 +968,7 @@ class MrpProduction(models.Model):
if self.product_id.tracking == 'serial':
quantity = 1.0
- for operation in bom.routing_id.operation_ids:
+ for operation in bom.operation_ids:
workorder = workorders.create({
'name': operation.name,
'production_id': self.id,
@@ -994,9 +987,7 @@ class MrpProduction(models.Model):
# get the raw moves to attach to this operation
moves_raw = self.env['stock.move']
for move in self.move_raw_ids:
- if move.operation_id == operation and move.bom_line_id.bom_id.routing_id == bom.routing_id:
- moves_raw |= move
- if move.operation_id == operation and not move.bom_line_id:
+ if move.operation_id == operation:
moves_raw |= move
moves_finished = self.move_finished_ids.filtered(lambda move: move.operation_id == operation)
@@ -1004,9 +995,9 @@ class MrpProduction(models.Model):
# be consumed at the last workorder of the linked routing.
# - Raw moves from a BoM where no rounting was set should be consumed at the last
# workorder of the main routing.
- if len(workorders) == len(bom.routing_id.operation_ids):
- moves_raw |= self.move_raw_ids.filtered(lambda move: not move.operation_id and move.bom_line_id.bom_id.routing_id == bom.routing_id)
- moves_raw |= self.move_raw_ids.filtered(lambda move: not move.workorder_id and not move.bom_line_id.bom_id.routing_id)
+ if len(workorders) == len(bom.operation_ids):
+ moves_raw |= self.move_raw_ids.filtered(lambda move: not move.operation_id and move.bom_line_id.bom_id.operation_ids and move.bom_line_id.bom_id == bom)
+ moves_raw |= self.move_raw_ids.filtered(lambda move: not move.workorder_id and not move.bom_line_id.bom_id.operation_ids)
moves_finished |= self.move_finished_ids.filtered(lambda move: move.product_id != self.product_id and not move.operation_id)
diff --git a/addons/mrp/models/mrp_routing.py b/addons/mrp/models/mrp_routing.py
index 7b8f8bfd06b..9104e684221 100644
--- a/addons/mrp/models/mrp_routing.py
+++ b/addons/mrp/models/mrp_routing.py
@@ -4,32 +4,6 @@
from odoo import api, fields, models, _
-class MrpRouting(models.Model):
- """ Specifies routings of work centers """
- _name = 'mrp.routing'
- _description = 'Routings'
-
- name = fields.Char('Routing', required=True)
- active = fields.Boolean(
- 'Active', default=True,
- help="If the active field is set to False, it will allow you to hide the routing without removing it.")
- code = fields.Char(
- 'Reference',
- copy=False, default=lambda self: _('New'), readonly=True)
- note = fields.Text('Description')
- operation_ids = fields.One2many(
- 'mrp.routing.workcenter', 'routing_id', 'Operations',
- copy=True)
- company_id = fields.Many2one(
- 'res.company', 'Company', default=lambda self: self.env.company)
-
- @api.model
- def create(self, vals):
- if 'code' not in vals or vals['code'] == _('New'):
- vals['code'] = self.env['ir.sequence'].next_by_code('mrp.routing') or _('New')
- return super(MrpRouting, self).create(vals)
-
-
class MrpRoutingWorkcenter(models.Model):
_name = 'mrp.routing.workcenter'
_description = 'Work Center Usage'
@@ -41,14 +15,13 @@ class MrpRoutingWorkcenter(models.Model):
sequence = fields.Integer(
'Sequence', default=100,
help="Gives the sequence order when displaying a list of routing Work Centers.")
- routing_id = fields.Many2one(
- 'mrp.routing', 'Parent Routing',
+ bom_id = fields.Many2one(
+ 'mrp.bom', 'Bill of Material',
index=True, ondelete='cascade', required=True,
- help="The routing contains all the Work Centers used and for how long. This will create work orders afterwards "
- "which alters the execution of the manufacturing order.")
+ help="The Bill of Material this operation is linked to")
company_id = fields.Many2one(
'res.company', 'Company',
- readonly=True, related='routing_id.company_id', store=True)
+ readonly=True, related='bom_id.company_id', store=True)
worksheet_type = fields.Selection([
('pdf', 'PDF'), ('google_slide', 'Google Slide'), ('text', 'Text')],
string="Work Sheet", default="pdf",
diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py
index c60c9f051e6..8702a292155 100644
--- a/addons/mrp/models/stock_move.py
+++ b/addons/mrp/models/stock_move.py
@@ -76,10 +76,10 @@ class StockMove(models.Model):
'mrp.unbuild', 'Disassembly Order', check_company=True)
consume_unbuild_id = fields.Many2one(
'mrp.unbuild', 'Consumed Disassembly Order', check_company=True)
+ allowed_operation_ids = fields.Many2many('mrp.routing.workcenter', compute='_compute_allowed_operation_ids')
operation_id = fields.Many2one(
'mrp.routing.workcenter', 'Operation To Consume', check_company=True,
- domain="[('routing_id', '=', routing_id), '|', ('company_id', '=', company_id), ('company_id', '=', False)]")
- routing_id = fields.Many2one(related='raw_material_production_id.routing_id')
+ domain="[('id', 'in', allowed_operation_ids)]")
workorder_id = fields.Many2one(
'mrp.workorder', 'Work Order To Consume', check_company=True)
# Quantities to process, in normalized UoMs
@@ -118,6 +118,24 @@ class StockMove(models.Model):
move.order_finished_lot_ids = False
move.finished_lots_exist = False
+ @api.depends('raw_material_production_id.bom_id')
+ def _compute_allowed_operation_ids(self):
+ for move in self:
+ if (
+ not move.raw_material_production_id or
+ not move.raw_material_production_id.bom_id or not
+ move.raw_material_production_id.bom_id.operation_ids
+ ):
+ move.allowed_operation_ids = self.env['mrp.routing.workcenter']
+ else:
+ operation_domain = [
+ ('id', 'in', move.raw_material_production_id.bom_id.operation_ids.ids),
+ '|',
+ ('company_id', '=', self.company_id.id),
+ ('company_id', '=', False)
+ ]
+ move.allowed_operation_ids = self.env['mrp.routing.workcenter'].search(operation_domain)
+
@api.depends('product_id.tracking')
def _compute_needs_lots(self):
for move in self:
diff --git a/addons/mrp/report/mrp_report_bom_structure.py b/addons/mrp/report/mrp_report_bom_structure.py
index 05c3bafbd3d..b9108828b37 100644
--- a/addons/mrp/report/mrp_report_bom_structure.py
+++ b/addons/mrp/report/mrp_report_bom_structure.py
@@ -55,7 +55,7 @@ class ReportBomStructure(models.AbstractModel):
@api.model
def get_operations(self, bom_id=False, qty=0, level=0):
bom = self.env['mrp.bom'].browse(bom_id)
- lines = self._get_operation_line(bom.routing_id, float_round(qty / bom.product_qty, precision_rounding=1, rounding_method='UP'), level)
+ lines = self._get_operation_line(bom, float_round(qty / bom.product_qty, precision_rounding=1, rounding_method='UP'), level)
values = {
'bom_id': bom_id,
'currency': self.env.company.currency_id,
@@ -106,7 +106,7 @@ class ReportBomStructure(models.AbstractModel):
else:
product = bom.product_tmpl_id
attachments = self.env['mrp.document'].search([('res_model', '=', 'product.template'), ('res_id', '=', product.id)])
- operations = self._get_operation_line(bom.routing_id, float_round(bom_quantity / bom.product_qty, precision_rounding=1, rounding_method='UP'), 0)
+ operations = self._get_operation_line(bom, float_round(bom_quantity / bom.product_qty, precision_rounding=1, rounding_method='UP'), 0)
company = bom.company_id or self.env.company
lines = {
'bom': bom,
@@ -163,10 +163,10 @@ class ReportBomStructure(models.AbstractModel):
total += sub_total
return components, total
- def _get_operation_line(self, routing, qty, level):
+ def _get_operation_line(self, bom, qty, level):
operations = []
total = 0.0
- for operation in routing.operation_ids:
+ for operation in bom.operation_ids:
operation_cycle = float_round(qty / operation.workcenter_id.capacity, precision_rounding=1, rounding_method='UP')
duration_expected = operation_cycle * operation.time_cycle + operation.workcenter_id.time_stop + operation.workcenter_id.time_start
total = ((duration_expected / 60.0) * operation.workcenter_id.costs_hour)
@@ -181,7 +181,7 @@ class ReportBomStructure(models.AbstractModel):
def _get_price(self, bom, factor, product):
price = 0
- if bom.routing_id:
+ 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
@@ -189,7 +189,7 @@ class ReportBomStructure(models.AbstractModel):
# 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.routing_id, operation_cycle, 0)
+ operations = self._get_operation_line(bom, operation_cycle, 0)
price += sum([op['total'] for op in operations])
for line in bom.bom_line_ids:
diff --git a/addons/mrp/security/ir.model.access.csv b/addons/mrp/security/ir.model.access.csv
index 3b3ebdf97d2..b450c61d549 100644
--- a/addons/mrp/security/ir.model.access.csv
+++ b/addons/mrp/security/ir.model.access.csv
@@ -4,14 +4,12 @@ access_mrp_workcenter_productivity_loss,mrp.workcenter.productivity.loss,model_m
access_mrp_workcenter_productivity_loss_type,mrp.workcenter.productivity.loss.type,model_mrp_workcenter_productivity_loss_type,mrp.group_mrp_user,1,0,0,0
access_mrp_workcenter_productivity,mrp.workcenter.productivity,model_mrp_workcenter_productivity,mrp.group_mrp_user,1,1,1,1
access_mrp_workcenter,mrp.workcenter,model_mrp_workcenter,mrp.group_mrp_user,1,0,0,0
-access_mrp_routing,mrp.routing,model_mrp_routing,mrp.group_mrp_user,1,0,0,0
access_mrp_routing_workcenter,mrp.routing.workcenter,model_mrp_routing_workcenter,mrp.group_mrp_user,1,0,0,0
access_mrp_bom,mrp.bom,model_mrp_bom,group_mrp_user,1,0,0,0
access_mrp_bom_line,mrp.bom.line,model_mrp_bom_line,group_mrp_user,1,0,0,0
access_mrp_bom_byproduct_user,mrp.bom.byproduct,model_mrp_bom_byproduct,mrp.group_mrp_user,1,0,0,0
access_mrp_production,mrp.production user,model_mrp_production,mrp.group_mrp_user,1,1,1,1
access_mrp_workcenter_manager,mrp.workcenter.manager,model_mrp_workcenter,mrp.group_mrp_manager,1,1,1,1
-access_mrp_routing_manager,mrp.routing.manager,model_mrp_routing,mrp.group_mrp_manager,1,1,1,1
access_mrp_routing_workcenter_manager,mrp.routing.workcenter.manager,model_mrp_routing_workcenter,mrp.group_mrp_manager,1,1,1,1
access_mrp_bom_manager,mrp.bom.manager,model_mrp_bom,mrp.group_mrp_manager,1,1,1,1
access_mrp_bom_line_manager,mrp.bom.line.manager,model_mrp_bom_line,mrp.group_mrp_manager,1,1,1,1
diff --git a/addons/mrp/security/mrp_security.xml b/addons/mrp/security/mrp_security.xml
index c1c350dffd1..8e48b287826 100644
--- a/addons/mrp/security/mrp_security.xml
+++ b/addons/mrp/security/mrp_security.xml
@@ -78,12 +78,6 @@
<field name="domain_force">['|',('company_id', 'in', company_ids),('company_id','=',False)]</field>
</record>
- <record model="ir.rule" id="mrp_routing_rule">
- <field name="name">mrp_routing multi-company</field>
- <field name="model_id" search="[('model','=','mrp.routing')]" model="ir.model"/>
- <field name="domain_force">['|',('company_id', 'in', company_ids),('company_id','=',False)]</field>
- </record>
-
<record model="ir.rule" id="mrp_routing_workcenter_rule">
<field name="name">mrp_routing_workcenter multi-company</field>
<field name="model_id" search="[('model','=','mrp.routing.workcenter')]" model="ir.model"/>
diff --git a/addons/mrp/tests/common.py b/addons/mrp/tests/common.py
index 382bbb849fe..9b9e5978d65 100644
--- a/addons/mrp/tests/common.py
+++ b/addons/mrp/tests/common.py
@@ -91,40 +91,14 @@ class TestMrpCommon(common2.TestStockCommon):
'time_stop': 5,
'time_efficiency': 80,
})
- cls.routing_1 = cls.env['mrp.routing'].create({
- 'name': 'Simple Line',
- })
- cls.routing_2 = cls.env['mrp.routing'].create({
- 'name': 'Complicated Line',
- })
- cls.operation_1 = cls.env['mrp.routing.workcenter'].create({
- 'name': 'Gift Wrap Maching',
- 'workcenter_id': cls.workcenter_1.id,
- 'routing_id': cls.routing_1.id,
- 'time_cycle': 15,
- 'sequence': 1,
- })
- cls.operation_2 = cls.env['mrp.routing.workcenter'].create({
- 'name': 'Cutting Machine',
- 'workcenter_id': cls.workcenter_1.id,
- 'routing_id': cls.routing_2.id,
- 'time_cycle': 12,
- 'sequence': 1,
- })
- cls.operation_3 = cls.env['mrp.routing.workcenter'].create({
- 'name': 'Weld Machine',
- 'workcenter_id': cls.workcenter_1.id,
- 'routing_id': cls.routing_2.id,
- 'time_cycle': 18,
- 'sequence': 2,
- })
cls.bom_1 = cls.env['mrp.bom'].create({
'product_id': cls.product_4.id,
'product_tmpl_id': cls.product_4.product_tmpl_id.id,
'product_uom_id': cls.uom_unit.id,
'product_qty': 4.0,
- 'routing_id': cls.routing_2.id,
+ 'operation_ids': [
+ ],
'type': 'normal',
'bom_line_ids': [
(0, 0, {'product_id': cls.product_2.id, 'product_qty': 2}),
@@ -135,7 +109,9 @@ class TestMrpCommon(common2.TestStockCommon):
'product_tmpl_id': cls.product_5.product_tmpl_id.id,
'product_uom_id': cls.product_5.uom_id.id,
'product_qty': 1.0,
- 'routing_id': cls.routing_1.id,
+ 'operation_ids': [
+ (0, 0, {'name': 'Gift Wrap Maching', 'workcenter_id': cls.workcenter_1.id, 'time_cycle': 15, 'sequence': 1}),
+ ],
'type': 'phantom',
'sequence': 2,
'bom_line_ids': [
@@ -147,13 +123,17 @@ class TestMrpCommon(common2.TestStockCommon):
'product_tmpl_id': cls.product_6.product_tmpl_id.id,
'product_uom_id': cls.uom_dozen.id,
'product_qty': 2.0,
- 'routing_id': cls.routing_2.id,
+ 'operation_ids': [
+ (0, 0, {'name': 'Cutting Machine', 'workcenter_id': cls.workcenter_1.id, 'time_cycle': 12, 'sequence': 1}),
+ (0, 0, {'name': 'Weld Machine', 'workcenter_id': cls.workcenter_1.id, 'time_cycle': 18, 'sequence': 2}),
+ ],
'type': 'normal',
'bom_line_ids': [
(0, 0, {'product_id': cls.product_5.id, 'product_qty': 2}),
(0, 0, {'product_id': cls.product_4.id, 'product_qty': 8}),
(0, 0, {'product_id': cls.product_2.id, 'product_qty': 12})
]})
+
cls.stock_location_14 = cls.env['stock.location'].create({
'name': 'Shelf 2',
'location_id': cls.env.ref('stock.warehouse0').lot_stock_id.id,
@@ -177,4 +157,4 @@ class TestMrpCommon(common2.TestStockCommon):
'type': 'product',
'tracking': 'none',
'categ_id': cls.env.ref('product.product_category_all').id,
- }) \ No newline at end of file
+ })
diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py
index d57a3a100cf..77cadb63613 100644
--- a/addons/mrp/tests/test_bom.py
+++ b/addons/mrp/tests/test_bom.py
@@ -26,9 +26,14 @@ class TestBoM(TestMrpCommon):
'product_tmpl_id': self.product_7_template.id,
'product_uom_id': self.uom_unit.id,
'product_qty': 4.0,
- 'routing_id': self.routing_2.id,
'type': 'normal',
})
+ test_bom.write({
+ 'operation_ids': [
+ (0, 0, {'name': 'Cutting Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 12, 'sequence': 1}),
+ (0, 0, {'name': 'Weld Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 18, 'sequence': 2}),
+ ],
+ })
test_bom_l1 = self.env['mrp.bom.line'].create({
'bom_id': test_bom.id,
'product_id': self.product_2.id,
@@ -78,9 +83,13 @@ class TestBoM(TestMrpCommon):
'product_tmpl_id': self.product_5.product_tmpl_id.id,
'product_uom_id': self.product_5.uom_id.id,
'product_qty': 1.0,
- 'routing_id': self.routing_1.id,
'type': 'phantom'
})
+ test_bom_1.write({
+ 'operation_ids': [
+ (0, 0, {'name': 'Gift Wrap Maching', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 15, 'sequence': 1}),
+ ],
+ })
test_bom_1_l1 = self.env['mrp.bom.line'].create({
'bom_id': test_bom_1.id,
'product_id': self.product_3.id,
@@ -92,9 +101,14 @@ class TestBoM(TestMrpCommon):
'product_tmpl_id': self.product_7_template.id,
'product_uom_id': self.uom_unit.id,
'product_qty': 4.0,
- 'routing_id': self.routing_2.id,
'type': 'normal',
})
+ test_bom_2.write({
+ 'operation_ids': [
+ (0, 0, {'name': 'Cutting Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 12, 'sequence': 1}),
+ (0, 0, {'name': 'Weld Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 18, 'sequence': 2}),
+ ]
+ })
test_bom_2_l1 = self.env['mrp.bom.line'].create({
'bom_id': test_bom_2.id,
'product_id': self.product_2.id,
@@ -278,6 +292,11 @@ class TestBoM(TestMrpCommon):
bom_form_crumble.product_uom_id = uom_kg
bom_crumble = bom_form_crumble.save()
+ workcenter = self.env['mrp.workcenter'].create({
+ 'costs_hour': 10,
+ 'name': 'Deserts Table'
+ })
+
with Form(bom_crumble) as bom:
with bom.bom_line_ids.new() as line:
line.product_id = butter
@@ -287,32 +306,19 @@ class TestBoM(TestMrpCommon):
line.product_id = biscuit
line.product_uom_id = uom_kg
line.product_qty = 6
-
- workcenter = self.env['mrp.workcenter'].create({
- 'costs_hour': 10,
- 'name': 'Deserts Table'
- })
-
- routing_form = Form(self.env['mrp.routing'])
- routing_form.name = "Crumble process"
- routing_crumble = routing_form.save()
-
- with Form(routing_crumble) as routing:
- with routing.operation_ids.new() as operation:
+ with bom.operation_ids.new() as operation:
operation.workcenter_id = workcenter
operation.name = 'Prepare biscuits'
operation.time_cycle_manual = 5
- with routing.operation_ids.new() as operation:
+ with bom.operation_ids.new() as operation:
operation.workcenter_id = workcenter
operation.name = 'Prepare butter'
operation.time_cycle_manual = 3
- with routing.operation_ids.new() as operation:
+ with bom.operation_ids.new() as operation:
operation.workcenter_id = workcenter
operation.name = 'Mix manually'
operation.time_cycle_manual = 5
- bom_crumble.routing_id = routing_crumble.id
-
# TEST BOM STRUCTURE VALUE WITH BOM QUANTITY
report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_crumble.id, searchQty=11, searchVariant=False)
# 5 min 'Prepare biscuits' + 3 min 'Prepare butter' + 5 min 'Mix manually' = 13 minutes
@@ -381,6 +387,13 @@ class TestBoM(TestMrpCommon):
bom_form_cheese_cake.product_uom_id = self.uom_unit
bom_cheese_cake = bom_form_cheese_cake.save()
+ workcenter_2 = self.env['mrp.workcenter'].create({
+ 'name': 'cake mounting',
+ 'costs_hour': 20,
+ 'time_start': 10,
+ 'time_stop': 15
+ })
+
with Form(bom_cheese_cake) as bom:
with bom.bom_line_ids.new() as line:
line.product_id = cream
@@ -390,29 +403,15 @@ class TestBoM(TestMrpCommon):
line.product_id = crumble
line.product_uom_id = uom_kg
line.product_qty = 5.4
-
- workcenter_2 = self.env['mrp.workcenter'].create({
- 'name': 'cake mounting',
- 'costs_hour': 20,
- 'time_start': 10,
- 'time_stop': 15
- })
-
- routing_form = Form(self.env['mrp.routing'])
- routing_form.name = "Cheese cake process"
- routing_cheese = routing_form.save()
-
- with Form(routing_cheese) as routing:
- with routing.operation_ids.new() as operation:
+ with bom.operation_ids.new() as operation:
operation.workcenter_id = workcenter
operation.name = 'Mix cheese and crumble'
operation.time_cycle_manual = 10
- with routing.operation_ids.new() as operation:
+ with bom.operation_ids.new() as operation:
operation.workcenter_id = workcenter_2
operation.name = 'Cake mounting'
operation.time_cycle_manual = 5
- bom_cheese_cake.routing_id = routing_cheese.id
# TEST CHEESE BOM STRUCTURE VALUE WITH BOM QUANTITY
report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_cheese_cake.id, searchQty=60, searchVariant=False)
diff --git a/addons/mrp/tests/test_cancel_mo.py b/addons/mrp/tests/test_cancel_mo.py
index fdbfb50531f..3a132ba02c4 100644
--- a/addons/mrp/tests/test_cancel_mo.py
+++ b/addons/mrp/tests/test_cancel_mo.py
@@ -87,7 +87,11 @@ class TestMrpCancelMO(TestMrpCommon):
mo_data = self.generate_mo()
manufacturing_order = mo_data[0]
bom = mo_data[1]
- bom.routing_id = self.routing_1
+ bom.write({
+ 'operation_ids': [
+ (0, 0, {'name': 'Gift Wrap Maching', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 15, 'sequence': 1}),
+ ],
+ })
manufacturing_order.button_plan()
workorder = manufacturing_order.workorder_ids
diff --git a/addons/mrp/tests/test_multicompany.py b/addons/mrp/tests/test_multicompany.py
index 5b43be07224..b6593cfb517 100644
--- a/addons/mrp/tests/test_multicompany.py
+++ b/addons/mrp/tests/test_multicompany.py
@@ -177,25 +177,6 @@ class TestMrpMulticompany(common.TransactionCase):
with self.assertRaises(UserError):
produce_form.save()
- def test_workcenter_1(self):
- """Check it is not possible to use a routing of Company B in a
- workcenter of Company A. """
-
- workcenter = self.env['mrp.workcenter'].create({
- 'name': 'WC1',
- 'company_id': self.company_a.id,
- 'resource_calendar_id': self.company_a.resource_calendar_id.id,
- })
- with self.assertRaises(UserError):
- self.env['mrp.routing'].create({
- 'name': 'WC1',
- 'company_id': self.company_b.id,
- 'operation_ids': [(0, 0, {
- 'name': 'operation_1',
- 'workcenter_id': workcenter.id,
- })]
- })
-
def test_partner_1(self):
""" On a product without company, as a user of Company B, check it is not possible to use a
location limited to Company A as `property_stock_production` """
diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py
index f13f41d44ae..90f25506ba6 100644
--- a/addons/mrp/tests/test_order.py
+++ b/addons/mrp/tests/test_order.py
@@ -63,7 +63,6 @@ class TestMrpOrder(TestMrpCommon):
test_date_planned = Dt.now() - timedelta(days=1)
test_quantity = 2.0
- self.bom_1.routing_id = False
man_order_form = Form(self.env['mrp.production'].with_user(self.user_mrp_user))
man_order_form.product_id = self.product_4
man_order_form.bom_id = self.bom_1
@@ -149,15 +148,6 @@ class TestMrpOrder(TestMrpCommon):
# check sub product availability state is assigned
self.assertEqual(production_2.reservation_state, 'assigned', 'Production order should be availability for assigned state')
- def test_empty_routing(self):
- """ Check what happens when you work with an empty routing"""
- routing = self.env['mrp.routing'].create({'name': 'Routing without operations'})
- self.bom_3.routing_id = routing.id
- production_form = Form(self.env['mrp.production'])
- production_form.product_id = self.product_6
- production = production_form.save()
- self.assertEqual(production.routing_id.id, False, 'The routing field should be empty on the mo')
-
def test_split_move_line(self):
""" Consume more component quantity than the initial demand.
It should create extra move and share the quantity between the two stock
@@ -911,8 +901,6 @@ class TestMrpOrder(TestMrpCommon):
bp.product_qty = 2.0
bp.product_uom_id = dozen
- self.bom_1.routing_id = False
-
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product_4
mo_form.bom_id = self.bom_1
@@ -1231,14 +1219,10 @@ class TestMrpOrder(TestMrpCommon):
'uom_id': unit.id,
'uom_po_id': unit.id,
})
- routing = self.env['mrp.routing'].create({
- 'name': 'Secondary Assembly',
- })
bom = self.env['mrp.bom'].create({
'product_tmpl_id': plastic_laminate.product_tmpl_id.id,
'product_uom_id': unit.id,
'sequence': 1,
- 'routing_id': routing.id,
'bom_line_ids': [(0, 0, {
'product_id': ply_veneer.id,
'product_qty': 1,
diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py
index cf60399acaa..9e1d25e59ec 100644
--- a/addons/mrp/tests/test_procurement.py
+++ b/addons/mrp/tests/test_procurement.py
@@ -340,7 +340,10 @@ class TestProcurement(TestMrpCommon):
'product_tmpl_id': parent_product.id,
'product_uom_id': self.uom_unit.id,
'product_qty': 4.0,
- 'routing_id': self.routing_2.id,
+ 'operation_ids': [
+ (0, 0, {'name': 'Cutting Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 12, 'sequence': 1}),
+ (0, 0, {'name': 'Weld Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 18, 'sequence': 2}),
+ ],
'type': 'normal',
})
self.env['mrp.bom.line'].create({
@@ -353,7 +356,10 @@ class TestProcurement(TestMrpCommon):
'product_tmpl_id': child_product.id,
'product_uom_id': self.uom_unit.id,
'product_qty': 4.0,
- 'routing_id': self.routing_2.id,
+ 'operation_ids': [
+ (0, 0, {'name': 'Cutting Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 12, 'sequence': 1}),
+ (0, 0, {'name': 'Weld Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 18, 'sequence': 2}),
+ ],
'type': 'normal',
})
self.env['mrp.bom.line'].create({
diff --git a/addons/mrp/tests/test_stock.py b/addons/mrp/tests/test_stock.py
index f2c012737c7..0d1097abbf3 100644
--- a/addons/mrp/tests/test_stock.py
+++ b/addons/mrp/tests/test_stock.py
@@ -26,13 +26,6 @@ class TestWarehouse(common.TestMrpCommon):
'name': 'Assembly Line 1',
'resource_calendar_id': self.env.ref('resource.resource_calendar_std').id,
})
- mrp_routing = self.env['mrp.routing'].create({
- 'name': 'Primary Assembly',
- 'operation_ids': [(0, 0, {
- 'workcenter_id': mrp_workcenter.id,
- 'name': 'Manual Assembly',
- })]
- })
inventory = self.env['stock.inventory'].create({
'name': 'Initial inventory',
'line_ids': [(0, 0, {
@@ -54,7 +47,9 @@ class TestWarehouse(common.TestMrpCommon):
'product_qty': 1,
'product_uom_id': unit.id
})],
- 'routing_id': mrp_routing.id
+ 'operation_ids': [
+ (0, 0, {'name': 'Cutting Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 12, 'sequence': 1}),
+ ],
})
def new_mo_laptop(self):
diff --git a/addons/mrp/tests/test_workorder_operation.py b/addons/mrp/tests/test_workorder_operation.py
index de8b40ae1b4..3e4a89ec644 100644
--- a/addons/mrp/tests/test_workorder_operation.py
+++ b/addons/mrp/tests/test_workorder_operation.py
@@ -36,7 +36,9 @@ class TestWorkOrderProcessCommon(TestMrpCommon):
'product_tmpl_id': cls.product_4.product_tmpl_id.id,
'product_uom_id': cls.uom_unit.id,
'product_qty': 4.0,
- 'routing_id': cls.routing_1.id,
+ 'operation_ids': [
+ (0, 0, {'name': 'Gift Wrap Maching', 'workcenter_id': cls.workcenter_1.id, 'time_cycle': 15, 'sequence': 1}),
+ ],
'type': 'normal',
'bom_line_ids': [
(0, 0, {'product_id': cls.product_2.id, 'product_qty': 2}),
@@ -70,44 +72,41 @@ class TestWorkOrderProcessCommon(TestMrpCommon):
'name': 'Assembly Line 1',
'resource_calendar_id': cls.env.ref('resource.resource_calendar_std').id,
})
- cls.routing = cls.env['mrp.routing'].create({
- 'name': 'Assemble Furniture',
- 'operation_ids': [(0, 0, {
- 'workcenter_id': cls.mrp_workcenter.id,
- 'name': 'Manual Assembly',
- })]
- })
cls.mrp_bom_desk = cls.env['mrp.bom'].create({
'product_tmpl_id': cls.dining_table.product_tmpl_id.id,
'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
'sequence': 3,
'consumption': 'flexible',
- 'routing_id': cls.routing.id,
+ 'operation_ids': [
+ (0, 0, {'workcenter_id': cls.mrp_workcenter.id, 'name': 'Manual Assembly'}),
+ ],
+ })
+ cls.mrp_bom_desk.write({
'bom_line_ids': [
(0, 0, {
'product_id': cls.product_table_sheet.id,
'product_qty': 1,
'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
'sequence': 1,
- 'operation_id': cls.routing.operation_ids.id}),
+ 'operation_id': cls.mrp_bom_desk.operation_ids.id}),
(0, 0, {
'product_id': cls.product_table_leg.id,
'product_qty': 4,
'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
'sequence': 2,
- 'operation_id': cls.routing.operation_ids.id}),
+ 'operation_id': cls.mrp_bom_desk.operation_ids.id}),
(0, 0, {
'product_id': cls.product_bolt.id,
'product_qty': 4,
'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
'sequence': 3,
- 'operation_id': cls.routing.operation_ids.id}),
+ 'operation_id': cls.mrp_bom_desk.operation_ids.id}),
(0, 0, {
'product_id': cls.product_screw.id,
'product_qty': 10,
'product_uom_id': cls.env.ref('uom.product_uom_unit').id,
'sequence': 4,
- 'operation_id': cls.routing.operation_ids.id}),
+ 'operation_id': cls.mrp_bom_desk.operation_ids.id}),
]
})
cls.mrp_workcenter_1 = cls.env['mrp.workcenter'].create({
@@ -118,26 +117,6 @@ class TestWorkOrderProcessCommon(TestMrpCommon):
'name': 'Assembly Line 1',
'resource_calendar_id': cls.env.ref('resource.resource_calendar_std').id,
})
- cls.routing_1 = cls.env['mrp.routing'].create({
- 'name': 'Secondary Assembly',
- 'operation_ids': [
- (0, 0, {
- 'workcenter_id': cls.mrp_workcenter_1.id,
- 'name': 'Packing',
- 'time_cycle': 30,
- 'sequence': 5}),
- (0, 0, {
- 'workcenter_id': cls.mrp_workcenter_3.id,
- 'name': 'Testing',
- 'time_cycle': 60,
- 'sequence': 10}),
- (0, 0, {
- 'workcenter_id': cls.mrp_workcenter_3.id,
- 'name': 'Long time assembly',
- 'time_cycle': 180,
- 'sequence': 15}),
- ]
- })
class TestWorkOrderProcess(TestWorkOrderProcessCommon):
@@ -270,11 +249,30 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
self.env['stock.move'].search([('product_id', '=', product_bolt.id)])._do_unreserve()
- bom.routing_id = self.routing_1
+ bom.operation_ids = False
+ bom.write({
+ 'operation_ids': [
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_1.id,
+ 'name': 'Packing',
+ 'time_cycle': 30,
+ 'sequence': 5}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Testing',
+ 'time_cycle': 60,
+ 'sequence': 10}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Long time assembly',
+ 'time_cycle': 180,
+ 'sequence': 15}),
+ ]
+ })
- bom.bom_line_ids.filtered(lambda p: p.product_id == product_table_sheet).operation_id = bom.routing_id.operation_ids[0]
- bom.bom_line_ids.filtered(lambda p: p.product_id == product_table_leg).operation_id = bom.routing_id.operation_ids[1]
- bom.bom_line_ids.filtered(lambda p: p.product_id == product_bolt).operation_id = bom.routing_id.operation_ids[2]
+ bom.bom_line_ids.filtered(lambda p: p.product_id == product_table_sheet).operation_id = bom.operation_ids[0]
+ bom.bom_line_ids.filtered(lambda p: p.product_id == product_table_leg).operation_id = bom.operation_ids[1]
+ bom.bom_line_ids.filtered(lambda p: p.product_id == product_bolt).operation_id = bom.operation_ids[2]
production_table_form = Form(self.env['mrp.production'])
production_table_form.product_id = dining_table
@@ -399,8 +397,9 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
# Set manual time cycle 20 and 10.
# --------------------------------
- self.operation_1.write({'time_cycle_manual': 20})
- (self.operation_2 | self.operation_3).write({'time_cycle_manual': 10})
+ self.bom_3.operation_ids[0].time_cycle_manual = 10
+ self.bom_3.operation_ids[1].time_cycle_manual = 10
+ self.bom_2.operation_ids.time_cycle_manual = 20
man_order_form = Form(self.env['mrp.production'])
man_order_form.product_id = self.product_6
@@ -484,9 +483,10 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
# - kit bom: Stone Tool: 1 operation
# operation 1: Gift Wrapping
workorders = man_order.workorder_ids
- kit_wo = man_order.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_1)
- door_wo_1 = man_order.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_2)
- door_wo_2 = man_order.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_3)
+ kit_wo = man_order.workorder_ids.filtered(lambda wo: wo.operation_id.name == "Gift Wrap Maching")
+ door_wo_1 = man_order.workorder_ids.filtered(lambda wo: wo.operation_id.name == "Cutting Machine")
+ door_wo_2 = man_order.workorder_ids.filtered(lambda wo: wo.operation_id.name == "Weld Machine")
+
for workorder in workorders:
self.assertEqual(workorder.workcenter_id, self.workcenter_1, "Workcenter does not match.")
self.assertEqual(kit_wo.state, 'ready', "Workorder should be in ready state.")
@@ -524,7 +524,6 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
door_wo_2.record_production()
self.assertEqual(door_wo_2.state, 'done', "Workorder should be in done state.")
-
def test_01_without_workorder(self):
""" Testing consume quants and produced quants without workorder """
unit = self.ref("uom.product_uom_unit")
@@ -816,7 +815,6 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
laptop = self.laptop
graphics_card = self.graphics_card
unit = self.env.ref("uom.product_uom_unit")
- three_step_routing = self.routing_1
laptop.tracking = 'serial'
@@ -829,7 +827,23 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
'product_qty': 1,
'product_uom_id': unit.id
})],
- 'routing_id': three_step_routing.id
+ 'operation_ids': [
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_1.id,
+ 'name': 'Packing',
+ 'time_cycle': 30,
+ 'sequence': 5}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Testing',
+ 'time_cycle': 60,
+ 'sequence': 10}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Long time assembly',
+ 'time_cycle': 180,
+ 'sequence': 15}),
+ ],
})
mo_laptop_form = Form(self.env['mrp.production'])
@@ -891,7 +905,23 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
'product_tmpl_id': drawer.product_tmpl_id.id,
'product_uom_id': self.env.ref('uom.product_uom_unit').id,
'sequence': 2,
- 'routing_id': self.routing_1.id,
+ 'operation_ids': [
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_1.id,
+ 'name': 'Packing',
+ 'time_cycle': 30,
+ 'sequence': 5}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Testing',
+ 'time_cycle': 60,
+ 'sequence': 10}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Long time assembly',
+ 'time_cycle': 180,
+ 'sequence': 15}),
+ ],
'bom_line_ids': [(0, 0, {
'product_id': drawer_drawer.id,
'product_qty': 1,
@@ -1156,10 +1186,14 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
calendar wc1 : [q1][q2]
calendar wc2 : [q1][q2]"""
self.workcenter_1.alternative_workcenter_ids = self.wc_alt_1 | self.wc_alt_2
- self.planning_bom.routing_id = self.routing_2
+ self.planning_bom.operation_ids = False
+ self.planning_bom.write({
+ 'operation_ids': [
+ (0, 0, {'name': 'Cutting Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 12, 'sequence': 1, 'batch': 'yes', 'batch_size': 1}),
+ (0, 0, {'name': 'Weld Machine', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 18, 'sequence': 2}),
+ ],
+ })
# Allow second workorder to start once the first one is not ended yet
- self.operation_2.batch = 'yes'
- self.operation_2.batch_size = 1
self.env['mrp.workcenter'].search([]).write({'capacity': 1})
# workcenters work 24/7
self.full_availability()
@@ -1175,8 +1209,8 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
self.assertEqual(mo.workorder_ids[0].workcenter_id, self.wc_alt_2, "wrong workcenter")
self.assertEqual(mo.workorder_ids[1].workcenter_id, self.wc_alt_1, "wrong workcenter")
- duration1 = self.operation_2.time_cycle * 100.0 / self.wc_alt_2.time_efficiency + self.wc_alt_2.time_start
- duration2 = 2.0 * self.operation_2.time_cycle * 100.0 / self.wc_alt_1.time_efficiency + self.wc_alt_1.time_start + self.wc_alt_1.time_stop
+ duration1 = self.planning_bom.operation_ids[0].time_cycle * 100.0 / self.wc_alt_2.time_efficiency + self.wc_alt_2.time_start
+ duration2 = 2.0 * self.planning_bom.operation_ids[0].time_cycle * 100.0 / self.wc_alt_1.time_efficiency + self.wc_alt_1.time_start + self.wc_alt_1.time_stop
wo2_start = mo.workorder_ids[1].date_planned_start
wo2_stop = mo.workorder_ids[1].date_planned_finished
@@ -1262,7 +1296,6 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
""" Marking a workorder as done before the theoretical date should update
the reservation slot in the calendar the be able to reserve the next
production sooner """
- self.workcenter_1.alternative_workcenter_ids = self.wc_alt_1 | self.wc_alt_2
self.env['mrp.workcenter'].search([]).write({'tz': 'UTC'}) # compute all date in UTC
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product_4
@@ -1274,7 +1307,7 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
mo.button_plan()
wo = mo.workorder_ids
self.assertAlmostEqual(wo.date_planned_start, datetime(2019, 5, 13, 9, 0, 0, 0), delta=timedelta(seconds=10))
- self.assertAlmostEqual(wo.date_planned_finished, datetime(2019, 5, 13, 9, 0, 0, 0) + timedelta(minutes=85.58), delta=timedelta(seconds=10))
+ self.assertAlmostEqual(wo.date_planned_finished, datetime(2019, 5, 13, 9, 0, 0, 0) + timedelta(minutes=60), delta=timedelta(seconds=10))
wo.button_start()
wo.record_production()
# Marking workorder as done should change the finished date
@@ -1302,7 +1335,7 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
self.workcenter_1.time_efficiency = 100
self.workcenter_1.time_start = 0
self.workcenter_1.time_stop = 0
- self.routing_1.operation_ids.time_cycle = 60
+ self.planning_bom.operation_ids.time_cycle = 60
self.product_4.tracking = 'serial'
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product_4
@@ -1372,7 +1405,23 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
'product_tmpl_id': self.product_5.product_tmpl_id.id,
'product_uom_id': self.uom_unit.id,
'product_qty': 1.0,
- 'routing_id': self.routing_1.id,
+ 'operation_ids': [
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_1.id,
+ 'name': 'Packing',
+ 'time_cycle': 30,
+ 'sequence': 5}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Testing',
+ 'time_cycle': 60,
+ 'sequence': 10}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Long time assembly',
+ 'time_cycle': 180,
+ 'sequence': 15}),
+ ],
'type': 'normal',
'bom_line_ids': [
(0, 0, {'product_id': self.product_1.id, 'product_qty': 1}),
@@ -1403,14 +1452,32 @@ class TestWorkOrderProcess(TestWorkOrderProcessCommon):
def test_conflict_and_replan(self):
""" TEST Json data conflicted and the replan button of a workorder """
- self.routing_1.operation_ids[0].write({'workcenter_id': self.mrp_workcenter_3.id})
dining_table = self.dining_table
bom = self.mrp_bom_desk
- bom.routing_id = self.routing_1
+ bom.operation_ids = False
+ bom.write({
+ 'operation_ids': [
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Packing',
+ 'time_cycle': 30,
+ 'sequence': 5}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Testing',
+ 'time_cycle': 60,
+ 'sequence': 10}),
+ (0, 0, {
+ 'workcenter_id': self.mrp_workcenter_3.id,
+ 'name': 'Long time assembly',
+ 'time_cycle': 180,
+ 'sequence': 15}),
+ ]})
+
- bom.bom_line_ids.filtered(lambda p: p.product_id == self.product_table_sheet).operation_id = bom.routing_id.operation_ids[0].id
- bom.bom_line_ids.filtered(lambda p: p.product_id == self.product_table_leg).operation_id = bom.routing_id.operation_ids[1].id
- bom.bom_line_ids.filtered(lambda p: p.product_id == self.product_bolt).operation_id = bom.routing_id.operation_ids[2].id
+ bom.bom_line_ids.filtered(lambda p: p.product_id == self.product_table_sheet).operation_id = bom.operation_ids[0].id
+ bom.bom_line_ids.filtered(lambda p: p.product_id == self.product_table_leg).operation_id = bom.operation_ids[1].id
+ bom.bom_line_ids.filtered(lambda p: p.product_id == self.product_bolt).operation_id = bom.operation_ids[2].id
production_table_form = Form(self.env['mrp.production'])
production_table_form.product_id = dining_table
@@ -1513,54 +1580,38 @@ class TestRoutingAndKits(SavepointCase):
cls.workcenter_kit1 = cls.env['mrp.workcenter'].create({
'name': 'workcenter2',
})
- cls.routing_finished1 = cls.env['mrp.routing'].create({
- 'name': 'routing for finished1',
- })
- cls.operation_finished1 = cls.env['mrp.routing.workcenter'].create({
- 'sequence': 1,
- 'name': 'finished operation 1',
- 'workcenter_id': cls.workcenter_finished1.id,
- 'routing_id': cls.routing_finished1.id,
- })
- cls.operation_finished2 = cls.env['mrp.routing.workcenter'].create({
- 'sequence': 1,
- 'name': 'finished operation 2',
- 'workcenter_id': cls.workcenter_finished1.id,
- 'routing_id': cls.routing_finished1.id,
- })
- cls.routing_kit1 = cls.env['mrp.routing'].create({
- 'name': 'routing for kit1',
- })
- cls.operation_kit1 = cls.env['mrp.routing.workcenter'].create({
- 'name': 'Kit operation',
- 'workcenter_id': cls.workcenter_kit1.id,
- 'routing_id': cls.routing_kit1.id,
- })
cls.bom_finished1 = cls.env['mrp.bom'].create({
'product_id': cls.finished1.id,
'product_tmpl_id': cls.finished1.product_tmpl_id.id,
'product_uom_id': cls.uom_unit.id,
'product_qty': 1,
'type': 'normal',
- 'routing_id': cls.routing_finished1.id,
'bom_line_ids': [
(0, 0, {'product_id': cls.compfinished1.id, 'product_qty': 1}),
(0, 0, {'product_id': cls.kit1.id, 'product_qty': 1}),
- ]})
+ ],
+ 'operation_ids': [
+ (0, 0, {'sequence': 1, 'name': 'finished operation 1', 'workcenter_id': cls.workcenter_finished1.id}),
+ (0, 0, {'sequence': 2, 'name': 'finished operation 2', 'workcenter_id': cls.workcenter_finished1.id}),
+ ],
+ })
cls.bom_kit1 = cls.env['mrp.bom'].create({
'product_id': cls.kit1.id,
'product_tmpl_id': cls.kit1.product_tmpl_id.id,
'product_uom_id': cls.uom_unit.id,
'product_qty': 1,
'type': 'phantom',
- 'routing_id': cls.routing_kit1.id,
'bom_line_ids': [
(0, 0, {'product_id': cls.compkit1.id, 'product_qty': 1}),
- ]})
+ ],
+ 'operation_ids': [
+ (0, 0, {'name': 'Kit operation', 'workcenter_id': cls.workcenter_kit1.id})
+ ]
+ })
def test_1(self):
"""Operations are set on `self.bom_kit1` but none on `self.bom_finished1`."""
- self.bom_kit1.bom_line_ids.operation_id = self.operation_kit1
+ self.bom_kit1.bom_line_ids.operation_id = self.bom_kit1.operation_ids[0]
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.finished1
@@ -1594,9 +1645,9 @@ class TestRoutingAndKits(SavepointCase):
def test_3(self):
"""Operations are set both `self.bom_kit1` and `self.bom_finished1`."""
- self.bom_kit1.bom_line_ids.operation_id = self.operation_kit1
- self.bom_finished1.bom_line_ids[0].operation_id = self.operation_finished1
- self.bom_finished1.bom_line_ids[1].operation_id = self.operation_finished2
+ self.bom_kit1.bom_line_ids.operation_id = self.bom_kit1.operation_ids
+ self.bom_finished1.bom_line_ids[0].operation_id = self.bom_finished1.operation_ids[0]
+ self.bom_finished1.bom_line_ids[1].operation_id = self.bom_finished1.operation_ids[1]
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.finished1
@@ -1653,12 +1704,12 @@ class TestRoutingAndKits(SavepointCase):
def test_5(self):
# Main bom: set the normal component to the first of the two operations of the routing.
bomline_compfinished = self.bom_finished1.bom_line_ids.filtered(lambda bl: bl.product_id == self.compfinished1)
- bomline_compfinished.operation_id = self.operation_finished1
+ bomline_compfinished.operation_id = self.bom_finished1.operation_ids[0]
# Main bom: the kit do not have an operation set but there's one on its bom
bomline_kit1 = self.bom_finished1.bom_line_ids - bomline_compfinished
self.assertFalse(bomline_kit1.operation_id.id)
- self.bom_kit1.bom_line_ids.operation_id = self.bom_kit1.routing_id.operation_ids
+ self.bom_kit1.bom_line_ids.operation_id = self.bom_kit1.operation_ids
# Main bom: add a kit without routing
kit2 = self.env['product.product'].create({
@@ -1693,28 +1744,10 @@ class TestRoutingAndKits(SavepointCase):
self.assertEqual(mo.workorder_ids[1].raw_workorder_line_ids.product_id, compkit2)
self.assertEqual(mo.workorder_ids[2].raw_workorder_line_ids.product_id, self.compkit1)
- def test_6(self):
- """ Use the same routing on `self.bom_fnished1` and `self.kit1`. The workorders should not
- be duplicated.
- """
- self.bom_finished1.bom_line_ids[0].operation_id = self.operation_finished1
- self.bom_finished1.bom_line_ids[1].operation_id = self.operation_finished2
- self.bom_kit1.routing_id = self.routing_finished1
- self.bom_kit1.bom_line_ids.operation_id = self.operation_finished1
-
- mo_form = Form(self.env['mrp.production'])
- mo_form.product_id = self.finished1
- mo_form.bom_id = self.bom_finished1
- mo_form.product_qty = 1.0
- mo = mo_form.save()
-
- mo.action_confirm()
- mo.button_plan()
-
- self.assertEqual(len(mo.workorder_ids), 2)
- self.assertEqual(set(mo.workorder_ids[0].raw_workorder_line_ids.product_id.ids), set([self.compfinished1.id, self.compkit1.id]))
- self.assertFalse(mo.workorder_ids[1].raw_workorder_line_ids.product_id.id)
-
+ # -------------------------------------------------------------------------
+ # Those 2 next tests aren't related to routing and kit but to flexible
+ # consumption.
+ # -------------------------------------------------------------------------
def test_merge_lot(self):
""" Produce 10 units of product tracked by lot on two workorder. On the
first one, produce 4 onto lot1 then 6 onto lot1 as well. The second
@@ -1768,7 +1801,7 @@ class TestRoutingAndKits(SavepointCase):
move.location_id = mo.location_src_id
move.location_dest_id = mo.production_location_id
move.product_uom_qty = 2
- move.operation_id = mo.routing_id.operation_ids[0]
+ move.operation_id = mo.bom_id.operation_ids[0]
mo = mo_form.save()
self.assertEqual(len(mo.move_raw_ids), 3)
mo.action_confirm()
@@ -1802,7 +1835,7 @@ class TestRoutingAndKits(SavepointCase):
move.location_id = mo.location_src_id
move.location_dest_id = mo.production_location_id
move.product_uom_qty = 2
- move.operation_id = mo.routing_id.operation_ids[0]
+ move.operation_id = mo.bom_id.operation_ids[0]
mo = mo_form.save()
new_move = mo.move_raw_ids.filtered(lambda move: move.additional)
self.assertEqual(len(mo.move_raw_ids), 3)
diff --git a/addons/mrp/views/mrp_bom_views.xml b/addons/mrp/views/mrp_bom_views.xml
index 768f08f22ee..323466f8c5e 100644
--- a/addons/mrp/views/mrp_bom_views.xml
+++ b/addons/mrp/views/mrp_bom_views.xml
@@ -14,7 +14,7 @@
<field name="arch" type="xml">
<form string="Byproduct">
<group>
- <field name="routing_id" invisible="1"/>
+ <field name="allowed_operation_ids" invisible="1"/>
<field name="company_id"/>
<field name="product_id"/>
<label for="product_qty"/>
@@ -36,6 +36,11 @@
<form string="Bill of Material">
<sheet>
<div class="oe_button_box" name="button_box">
+ <button name="%(action_mrp_routing_time)d" type="action" class="oe_stat_button" icon="fa-clock-o" groups="mrp.group_mrp_routings">
+ <div class="o_field_widget o_stat_info">
+ <span class="o_stat_text">Routing<br/>Performance</span>
+ </div>
+ </button>
<button name="%(action_report_mrp_bom)d" type="action"
class="oe_stat_button" icon="fa-bars" string="Structure &amp; Cost"/>
</div>
@@ -51,7 +56,6 @@
<field name="product_qty"/>
<field name="product_uom_id" options="{'no_open':True,'no_create':True}" groups="uom.group_uom"/>
</div>
- <field name="routing_id" attrs="{'invisible': [('type','not in',('normal','phantom'))]}" groups="mrp.group_mrp_routings" context="{'default_company_id': company_id}"/>
</group>
<group>
<field name="code"/>
@@ -72,10 +76,9 @@
</group>
<notebook>
<page string="Components" name="components">
- <field name="bom_line_ids" widget="one2many" context="{'default_parent_product_tmpl_id': product_tmpl_id, 'default_product_id': False, 'default_company_id': company_id, 'default_routing_id': routing_id}">
+ <field name="bom_line_ids" widget="one2many" context="{'default_parent_product_tmpl_id': product_tmpl_id, 'default_product_id': False, 'default_company_id': company_id, 'default_bom_id': id}">
<tree string="Components" editable="bottom">
<field name="company_id" invisible="1"/>
- <field name="routing_id" invisible="1"/>
<field name="sequence" widget="handle"/>
<field name="product_id" context="{'default_type': 'product'}"/>
<field name="product_tmpl_id" invisible="1"/>
@@ -88,6 +91,7 @@
<field name="possible_bom_product_template_attribute_value_ids" invisible="1"/>
<field name="product_uom_id" options="{'no_open':True,'no_create':True}" groups="uom.group_uom"/>
<field name="bom_product_template_attribute_value_ids" widget="many2many_tags" options="{'no_create': True}" attrs="{'column_invisible': [('parent.product_id', '!=', False)]}" groups="product.group_product_variant"/>
+ <field name="allowed_operation_ids" invisible="1"/>
<field name="operation_id" groups="mrp.group_mrp_routings" attrs="{'column_invisible': [('parent.type','not in', ('normal', 'phantom'))]}" options="{'no_quick_create':True,'no_create_edit':True}"/>
</tree>
</field>
@@ -96,13 +100,13 @@
name="by_products"
attrs="{'invisible': [('type','!=','normal')]}"
groups="mrp.group_mrp_byproducts">
- <field name="byproduct_ids" context="{'form_view_ref' : 'mrp.mrp_bom_byproduct_form_view', 'default_company_id': company_id, 'default_routing_id': routing_id}">
+ <field name="byproduct_ids" context="{'form_view_ref' : 'mrp.mrp_bom_byproduct_form_view', 'default_company_id': company_id, 'default_bom_id': id}">
<tree string="By-products" editable="top">
<field name="company_id" invisible="1"/>
- <field name="routing_id" invisible="1"/>
<field name="product_id" context="{'default_type': 'product'}"/>
<field name="product_qty"/>
<field name="product_uom_id" groups="uom.group_uom"/>
+ <field name="allowed_operation_ids" invisible="1"/>
<field name="operation_id" groups="mrp.group_mrp_routings" options="{'no_quick_create':True,'no_create_edit':True}"/>
</tree>
</field>
@@ -118,6 +122,15 @@
</group>
</group>
</page>
+ <page string="Operations"
+ name="operations"
+ attrs="{'invisible': [('type','not in',('normal','phantom'))]}"
+ groups="mrp.group_mrp_routings">
+ <field name="operation_ids"
+ attrs="{'invisible': [('type','not in',('normal','phantom'))]}"
+ groups="mrp.group_mrp_routings"
+ context="{'default_company_id': company_id}"/>
+ </page>
</notebook>
</sheet>
<div class="oe_chatter">
@@ -142,7 +155,6 @@
<field name="company_id" groups="base.group_multi_company" optional="show"/>
<field name="product_qty" optional="show"/>
<field name="product_uom_id" groups="uom.group_uom" optional="show" string="Unit of Measure"/>
- <field name="routing_id" groups="mrp.group_mrp_routings" optional="show"/>
</tree>
</field>
</record>
@@ -187,7 +199,6 @@
<filter string="Product" name="product" domain="[]" context="{'group_by': 'product_tmpl_id'}"/>
<filter string='BoM Type' name="group_by_type" domain="[]" context="{'group_by' : 'type'}"/>
<filter string='Unit of Measure' name="default_unit_of_measure" domain="[]" context="{'group_by' : 'product_uom_id'}"/>
- <filter string="Routing" name="routings" domain="[]" context="{'group_by': 'routing_id'}"/>
</group>
</search>
</field>
@@ -239,8 +250,8 @@
</group>
<group string="Operation">
<field name="company_id" invisible="1"/>
- <field name="routing_id" invisible="1"/>
<field name="sequence" groups="base.group_no_one"/>
+ <field name="allowed_operation_ids" invisible="1"/>
<field name="operation_id" groups="mrp.group_mrp_routings"/>
</group>
</group>
diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml
index e7b3c222a19..ac291ee7210 100644
--- a/addons/mrp/views/mrp_production_views.xml
+++ b/addons/mrp/views/mrp_production_views.xml
@@ -17,7 +17,6 @@
<field name="bom_id" readonly="1" optional="hide"/>
<field name="origin" optional="show"/>
<field name="user_id" optional="hide" widget="many2one_avatar_user"/>
- <field name="routing_id" groups="mrp.group_mrp_routings" optional="show"/>
<field name="reservation_state" optional="show"/>
<field name="product_qty" sum="Total Qty" string="Quantity" readonly="1" optional="show"/>
<field name="company_id" readonly="1" groups="base.group_multi_company" optional="show"/>
@@ -45,14 +44,15 @@
<field name="arch" type="xml">
<form string="Manufacturing Orders">
<header>
+ <field name="bom_has_operations" invisible="1"/>
<field name="confirm_cancel" invisible="1"/>
<button name="button_mark_done" attrs="{'invisible': [('state', '!=', 'to_close')]}" string="Mark as Done" type="object" class="oe_highlight"/>
<button name="action_confirm" attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('is_locked', '=', False)]}" string="Mark as Todo" type="object" class="oe_highlight"/>
<button name="action_assign" attrs="{'invisible': ['|', '|', ('is_locked', '=', False), ('state', 'in', ('draft', 'done', 'cancel')), ('reserve_visible', '=', False)]}" string="Check availability" type="object" class="oe_highlight"/>
- <button name="button_plan" attrs="{'invisible': ['|', ('state', '!=', 'confirmed'), ('routing_id', '=', False)]}" type="object" string="Plan" class="oe_highlight"/>
+ <button name="button_plan" attrs="{'invisible': ['|', ('state', '!=', 'confirmed'), ('bom_has_operations', '=', False)]}" type="object" string="Plan" class="oe_highlight"/>
<button name="button_unplan" type="object" string="Unplan" attrs="{'invisible': ['|', '|', ('state', '!=', 'planned'), ('date_planned_start', '=', False), ('date_planned_finished', '=', False)]}"/>
- <button name="open_produce_product" attrs="{'invisible': ['|', '|', '|', ('state', '=', 'to_close'), ('is_locked', '=', False), ('reservation_state', '!=', 'assigned'), ('routing_id', '!=', False)]}" string="Produce" type="object" class="oe_highlight"/>
- <button name="open_produce_product" attrs="{'invisible': ['|', '|', '|', ('state', '=', 'to_close'), ('is_locked', '=', False), ('reservation_state', 'not in', ('confirmed', 'waiting')), ('routing_id', '!=', False)]}" string="Produce" type="object"/>
+ <button name="open_produce_product" attrs="{'invisible': ['|', '|', '|', ('state', '=', 'to_close'), ('is_locked', '=', False), ('reservation_state', '!=', 'assigned'), ('bom_has_operations', '!=', False)]}" string="Produce" type="object" class="oe_highlight"/>
+ <button name="open_produce_product" attrs="{'invisible': ['|', '|', '|', ('state', '=', 'to_close'), ('is_locked', '=', False), ('reservation_state', 'not in', ('confirmed', 'waiting')), ('bom_has_operations', '!=', False)]}" string="Produce" type="object"/>
<button name="post_inventory" string="Post Inventory" type="object" attrs="{'invisible': [('post_visible', '=', False)]}"/>
<button name="button_scrap" type="object" string="Scrap" attrs="{'invisible': ['|', ('state', 'in', ('cancel', 'draft')), ('is_locked', '=', False)]}"/>
<button name="button_unreserve" type="object" string="Unreserve" attrs="{'invisible': [('unreserve_visible', '=', False)]}"/>
@@ -129,20 +129,19 @@
</div>
<field name="bom_id"
context="{'default_product_tmpl_id': product_tmpl_id}" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
- <field name="routing_id" groups="mrp.group_mrp_routings"/>
</group>
<group>
<field name="date_deadline" attrs="{'readonly': [('state', 'in', ['done', 'cancel'])]}"/>
<div class="o_row o_td_label">
<label for="date_planned_start" string="Planned Date"
attrs="{'invisible': [
- ('routing_id', '!=', False),
+ ('bom_has_operations', '!=', False),
('state', 'in', ['draft', 'confirmed'])
]}"/>
<label for="date_planned_start" string="Plan from"
attrs="{'invisible': [
'|',
- ('routing_id', '=', False),
+ ('bom_has_operations', '=', False),
('state', 'not in', ['draft', 'confirmed'])
]}"/>
</div>
@@ -151,27 +150,27 @@
attrs="{'readonly': [
'|',
'&amp;',
- ('routing_id', '=', False),
+ ('bom_has_operations', '=', False),
('state', 'in', ['done', 'cancel']),
'&amp;',
- ('routing_id', '!=', False),
+ ('bom_has_operations', '!=', False),
('state', 'not in', ['draft', 'confirmed'])]}"/>
<label for="date_planned_finished" string="to" attrs="{'invisible': [
'|',
('id', '=', False),
'&amp;',
- ('routing_id', '!=', False),
+ ('bom_has_operations', '!=', False),
('state', 'in', ['draft', 'confirmed'])
]}"/>
<field name="date_planned_finished" required="1"
attrs="{'readonly': [
'|',
'&amp;',
- ('routing_id', '=', False),
+ ('bom_has_operations', '=', False),
('state', 'in', ['done', 'cancel']),
- ('routing_id', '!=', False)
+ ('bom_has_operations', '!=', False)
], 'invisible': [
- ('routing_id', '!=', False),
+ ('bom_has_operations', '!=', False),
('state', 'in', ['draft', 'confirmed'])
]}"/>
<field name="delay_alert_date" invisible="1"/>
@@ -273,8 +272,7 @@
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<calendar date_start="date_planned_start" date_stop="date_planned_finished"
- string="Manufacturing Orders" color="routing_id" event_limit="5" quick_add="False">
- <field name="routing_id" filters="1"/>
+ string="Manufacturing Orders" event_limit="5" quick_add="False">
<field name="user_id" avatar_field="image_128"/>
<field name="product_id"/>
<field name="product_qty"/>
@@ -286,7 +284,7 @@
<field name="name">mrp.production.gantt</field>
<field name="model">mrp.production</field>
<field name="arch" type="xml">
- <gantt date_stop="date_finished" date_start="date_start" string="Productions" default_group_by="routing_id" create="0">
+ <gantt date_stop="date_finished" date_start="date_start" string="Productions" create="0">
</gantt>
</field>
</record>
@@ -319,8 +317,7 @@
<field name="name" string="Manufacturing Order" filter_domain="['|', ('name', 'ilike', self), ('origin', 'ilike', self)]"/>
<field name="product_id"/>
<field name="move_raw_ids" string="Component" filter_domain="[('move_raw_ids.product_id', 'ilike', self)]"/>
- <field name="name" string="Work Center" filter_domain="[('routing_id.operation_ids.workcenter_id', 'ilike', self)]"/>
- <field name="routing_id" groups="mrp.group_mrp_routings"/>
+ <field name="name" string="Work Center" filter_domain="[('bom_id.operation_ids.workcenter_id', 'ilike', self)]"/>
<field name="origin"/>
<filter string="To Do" name="todo" domain="[('state', 'in', ('draft', 'confirmed', 'planned','progress', 'to_close'))]"
help="Manufacturing Orders which are in confirmed state."/>
@@ -352,7 +349,6 @@
domain="[('activity_exception_decoration', '!=', False)]"/>
<group expand="0" string="Group By...">
<filter string="Product" name="product" domain="[]" context="{'group_by': 'product_id'}"/>
- <filter string="Routing" name="routing" domain="[]" context="{'group_by': 'routing_id'}" groups="mrp.group_mrp_routings"/>
<filter string="Status" name="status" domain="[]" context="{'group_by': 'state'}"/>
<filter string="Material Availability" name="groupby_reservation_state" domain="[]" context="{'group_by': 'reservation_state'}"/>
<filter string="Scheduled Date" name="scheduled_date" domain="[]" context="{'group_by': 'date_planned_start'}" help="Scheduled Date by Month"/>
diff --git a/addons/mrp/views/mrp_routing_views.xml b/addons/mrp/views/mrp_routing_views.xml
index eeb55498856..38739db5e6d 100644
--- a/addons/mrp/views/mrp_routing_views.xml
+++ b/addons/mrp/views/mrp_routing_views.xml
@@ -21,7 +21,6 @@
<field name="arch" type="xml">
<form string="Routing Work Centers">
<sheet>
- <field name="routing_id" invisible="1" required="0"/>
<group>
<group name="description">
<field name="name"/>
@@ -62,118 +61,5 @@
</form>
</field>
</record>
-
- <!-- Routings -->
- <record id="mrp_routing_form_view" model="ir.ui.view">
- <field name="name">mrp.routing.form</field>
- <field name="model">mrp.routing</field>
- <field name="arch" type="xml">
- <form string="Routing">
- <sheet>
- <div class="oe_button_box" name="button_box">
- <button name="%(action_mrp_routing_time)d" type="action" class="oe_stat_button" icon="fa-clock-o">
- <div class="o_field_widget o_stat_info">
- <span class="o_stat_text">Time<br/> Analysis</span>
- </div>
- </button>
- </div>
- <widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
- <div class="oe_title">
- <h1>
- <field name="code"/>
- </h1>
- </div>
- <group>
- <group>
- <field name="name"/>
- <field name="active" invisible="1"/>
- </group>
- <group>
- <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
- </group>
- </group>
- <notebook>
- <page string="Work Center Operations" name="work_center_operations">
- <field name="operation_ids" context="{'default_routing_id': id}"/>
- </page>
- <page string="Notes" name="notes">
- <field name="note"/>
- </page>
- </notebook>
- </sheet>
- </form>
- </field>
- </record>
-
- <record id="mrp_routing_tree_view" model="ir.ui.view">
- <field name="name">mrp.routing.tree</field>
- <field name="model">mrp.routing</field>
- <field name="arch" type="xml">
- <tree string="Routing">
- <field name="code"/>
- <field name="name"/>
- <field name="active" invisible="1"/>
- <field name="company_id" groups="base.group_multi_company"/>
- </tree>
- </field>
- </record>
-
- <record id="mrp_routing_kanban_view" model="ir.ui.view">
- <field name="name">mrp.routing.kanban</field>
- <field name="model">mrp.routing</field>
- <field name="arch" type="xml">
- <kanban class="o_kanban_mobile">
- <field name="code"/>
- <field name="name"/>
- <templates>
- <t t-name="kanban-box">
- <div t-attf-class="oe_kanban_card oe_kanban_global_click">
- <div class="o_kanban_record_top">
- <div class="o_kanban_record_headings mt4">
- <strong class="o_kanban_record_title"><span><t t-esc="record.name.value"/></span></strong>
- </div>
- <span class="badge badge-pill"><field name="code"/></span>
- </div>
- </div>
- </t>
- </templates>
- </kanban>
- </field>
- </record>
-
- <record id="mrp_routing_search_view" model="ir.ui.view">
- <field name="name">mrp.routing.search</field>
- <field name="model">mrp.routing</field>
- <field name="arch" type="xml">
- <search string="Routing">
- <field name="name" string="Routing" filter_domain="['|', ('name', 'ilike', self), ('code', 'ilike', self)]"/>
- <filter name="inactive" string="Archived" domain="[('active', '=', False)]"/>
- </search>
- </field>
- </record>
-
- <record id="mrp_routing_action" model="ir.actions.act_window">
- <field name="name">Routings</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">mrp.routing</field>
- <field name="view_mode">tree,kanban,form</field>
- <field name="view_id" ref="mrp_routing_tree_view"/>
- <field name="search_view_id" ref="mrp_routing_search_view"/>
- <field name="help" type="html">
- <p class="o_view_nocontent_smiling_face">
- Create a new routing
- </p><p>
- Routings define the successive operations that need to be
- done to realize a Manufacturing Order. Each operation from
- a Routing is done at a specific Work Center and has a specific duration.
- </p>
- </field>
- </record>
-
- <menuitem id="menu_mrp_routing_action"
- action="mrp_routing_action"
- parent="menu_mrp_configuration"
- groups="group_mrp_routings"
- sequence="50"/>
</data>
</odoo>
diff --git a/addons/mrp/views/mrp_workcenter_views.xml b/addons/mrp/views/mrp_workcenter_views.xml
index 989805a1834..126f0ad6925 100644
--- a/addons/mrp/views/mrp_workcenter_views.xml
+++ b/addons/mrp/views/mrp_workcenter_views.xml
@@ -6,7 +6,7 @@
<field name="name">Manufacturing Orders</field>
<field name="res_model">mrp.production</field>
<field name="view_mode">tree,kanban,form,gantt</field>
- <field name="domain">[('routing_id', '!=', False),('routing_id.operation_ids.workcenter_id','=', active_id)]</field>
+ <field name="domain">[('bom_id', '!=', False), ('bom_id.operation_ids.workcenter_id', '=', active_id)]</field>
</record>
<record id="action_work_orders" model="ir.actions.act_window">
diff --git a/addons/mrp/views/mrp_workorder_views.xml b/addons/mrp/views/mrp_workorder_views.xml
index b70d7625e21..6b7e345945e 100644
--- a/addons/mrp/views/mrp_workorder_views.xml
+++ b/addons/mrp/views/mrp_workorder_views.xml
@@ -32,7 +32,7 @@
<field name="view_mode">graph,pivot,tree,form,gantt,calendar</field>
<field name="context">{'search_default_done': True}</field>
<field name="search_view_id" ref="view_mrp_production_work_order_search"/>
- <field name="domain">[('production_id.routing_id', '=', active_id), ('state', '=', 'done')]</field>
+ <field name="domain">[('operation_id.bom_id', '=', active_id), ('state', '=', 'done')]</field>
<field name="help" type="html">
<p class="o_view_nocontent_empty_folder">
No data to display
diff --git a/addons/mrp/views/res_config_settings_views.xml b/addons/mrp/views/res_config_settings_views.xml
index 229ff502fc6..115a1d14338 100644
--- a/addons/mrp/views/res_config_settings_views.xml
+++ b/addons/mrp/views/res_config_settings_views.xml
@@ -20,14 +20,11 @@
<label for="group_mrp_routings" string="Work Orders"/>
<a href="https://www.odoo.com/documentation/user/13.0/manufacturing/management/manufacturing_order.html#manage-manufacturing-with-routings-and-work-centers" title="Documentation" class="o_doc_link" target="_blank"></a>
<div class="text-muted">
- Process operations at specific work centers based on the routing
+ Process operations at specific work centers
</div>
<div class="content-group" attrs="{'invisible': [('group_mrp_routings','=',False)]}">
<div class="mt8">
<div>
- <button name="%(mrp.mrp_routing_action)d" icon="fa-arrow-right" type="action" string="Routings" class="btn-link"/>
- </div>
- <div>
<button name="%(mrp.mrp_workcenter_action)d" icon="fa-arrow-right" type="action" string="Work Centers" class="btn-link"/>
</div>
</div>
diff --git a/addons/mrp/views/stock_move_views.xml b/addons/mrp/views/stock_move_views.xml
index 7f0273418cd..06fc52fac22 100644
--- a/addons/mrp/views/stock_move_views.xml
+++ b/addons/mrp/views/stock_move_views.xml
@@ -31,8 +31,8 @@
<span> / </span>
<span><field name="reserved_availability" nolabel="1"/></span>
</div>
- <field name="operation_id" attrs="{'invisible': ['|', ('parent.routing_id', '=', False), ('id', '!=', False)]}" groups="mrp.group_mrp_routings"/>
- <field name="routing_id" invisible="1"/>
+ <field name="operation_id" attrs="{'invisible': [('id', '!=', False)]}" groups="mrp.group_mrp_routings"/>
+ <field name="allowed_operation_ids" invisible="1"/>
<field name="unit_factor" invisible="1"/>
<field name="additional" invisible="1"/>
<field name="company_id" invisible="1"/>
diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py
index 468681982bb..3ced315c703 100644
--- a/addons/mrp/wizard/change_production_qty.py
+++ b/addons/mrp/wizard/change_production_qty.py
@@ -80,7 +80,7 @@ class ChangeProductionQty(models.TransientModel):
production._log_manufacture_exception(documents)
operation_bom_qty = {}
for bom, bom_data in boms:
- for operation in bom.routing_id.operation_ids:
+ for operation in bom.operation_ids:
operation_bom_qty[operation.id] = bom_data['qty']
finished_moves_modification = self._update_finished_moves(production, production.product_qty - qty_produced, old_production_qty)
production._log_downside_manufactured_quantity(finished_moves_modification)
diff --git a/addons/mrp_account/models/product.py b/addons/mrp_account/models/product.py
index 61f067b020a..72f32617d02 100644
--- a/addons/mrp_account/models/product.py
+++ b/addons/mrp_account/models/product.py
@@ -47,7 +47,7 @@ class ProductProduct(models.Model):
if not boms_to_recompute:
boms_to_recompute = []
total = 0
- for opt in bom.routing_id.operation_ids:
+ for opt in bom.operation_ids:
duration_expected = (
opt.workcenter_id.time_start +
opt.workcenter_id.time_stop +
diff --git a/addons/mrp_account/tests/test_bom_price.py b/addons/mrp_account/tests/test_bom_price.py
index b015080a883..813a8c2b6a8 100644
--- a/addons/mrp_account/tests/test_bom_price.py
+++ b/addons/mrp_account/tests/test_bom_price.py
@@ -19,7 +19,7 @@ class TestBom(common.TransactionCase):
super(TestBom, self).setUp()
self.Product = self.env['product.product']
self.Bom = self.env['mrp.bom']
- self.Routing = self.env['mrp.routing']
+ #self.Routing = self.env['mrp.routing']
self.operation = self.env['mrp.routing.workcenter']
# Products.
@@ -117,37 +117,63 @@ class TestBom(common.TransactionCase):
workcenter_from1.costs_hour = 100
workcenter_1 = workcenter_from1.save()
- routing_form1 = Form(self.Routing)
- routing_form1.name = 'Assembly Furniture'
- routing_1 = routing_form1.save()
-
- operation_1 = self.operation.create({
- 'name': 'Cutting',
- 'workcenter_id': workcenter_1.id,
- 'routing_id': routing_1.id,
- 'time_mode': 'manual',
- 'time_cycle_manual': 20,
- 'batch': 'no',
- 'sequence': 1,
- })
- operation_2 = self.operation.create({
- 'name': 'Drilling',
- 'workcenter_id': workcenter_1.id,
- 'routing_id': routing_1.id,
- 'time_mode': 'manual',
- 'time_cycle_manual': 25,
- 'batch': 'no',
- 'sequence': 2,
- })
- operation_3 = self.operation.create({
- 'name': 'Fitting',
- 'workcenter_id': workcenter_1.id,
- 'routing_id': routing_1.id,
- 'time_mode': 'manual',
- 'time_cycle_manual': 30,
- 'batch': 'no',
- 'sequence': 3,
- })
+ self.bom_1.write({
+ 'operation_ids': [
+ (0, 0, {
+ 'name': 'Cutting',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 20,
+ 'batch': 'no',
+ 'sequence': 1,
+ }),
+ (0, 0, {
+ 'name': 'Drilling',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 25,
+ 'batch': 'no',
+ 'sequence': 2,
+ }),
+ (0, 0, {
+ 'name': 'Fitting',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 30,
+ 'batch': 'no',
+ 'sequence': 3,
+ }),
+ ],
+ }),
+ self.bom_2.write({
+ 'operation_ids': [
+ (0, 0, {
+ 'name': 'Cutting',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 20,
+ 'batch': 'no',
+ 'sequence': 1,
+ }),
+ (0, 0, {
+ 'name': 'Drilling',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 25,
+ 'batch': 'no',
+ 'sequence': 2,
+ }),
+ (0, 0, {
+ 'name': 'Fitting',
+ 'workcenter_id': workcenter_1.id,
+ 'time_mode': 'manual',
+ 'time_cycle_manual': 30,
+ 'batch': 'no',
+ 'sequence': 3,
+ }),
+ ],
+ }),
+
# -----------------------------------------------------------------
# Dinning Table Operation Cost(1 Unit)
@@ -160,7 +186,6 @@ class TestBom(common.TransactionCase):
# Operation Cost 1 unit = 125
# -----------------------------------------------------------------
- self.bom_1.routing_id = routing_1.id
# --------------------------------------------------------------------------
# Table Head Operation Cost (1 Dozen)
@@ -173,7 +198,6 @@ class TestBom(common.TransactionCase):
# Operation Cost 1 dozen (125 per dozen) and 10.42 for 1 Unit
# --------------------------------------------------------------------------
- self.bom_2.routing_id = routing_1.id
self.assertEqual(self.dining_table.standard_price, 1000, "Initial price of the Product should be 1000")
self.dining_table.button_bom_cost()
diff --git a/addons/mrp_account/tests/test_mrp_account.py b/addons/mrp_account/tests/test_mrp_account.py
index 8bb590c6986..7f1660f3170 100644
--- a/addons/mrp_account/tests/test_mrp_account.py
+++ b/addons/mrp_account/tests/test_mrp_account.py
@@ -66,7 +66,6 @@ class TestMrpAccount(TestWorkOrderProcessCommon):
})
inventory.action_validate
bom = self.mrp_bom_desk.copy()
- bom.routing_id = False # TODO: extend the test later with the necessary operations
production_table_form = Form(self.env['mrp.production'])
production_table_form.product_id = self.dining_table
production_table_form.bom_id = bom
diff --git a/addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py b/addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py
index 97e9863b029..134ba08f893 100644
--- a/addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py
+++ b/addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py
@@ -37,16 +37,12 @@ class TestStockLandedCostsMrp(StockAccountTestCommon):
'type': 'product',
'categ_id': cls.categ_all.id
})
- cls.routing_1 = cls.env['mrp.routing'].create({
- 'name': 'Simple Line',
- })
cls.uom_unit = cls.env.ref('uom.product_uom_unit')
cls.bom_refri = cls.env['mrp.bom'].create({
'product_id': cls.product_refrigerator.id,
'product_tmpl_id': cls.product_refrigerator.product_tmpl_id.id,
'product_uom_id': cls.uom_unit.id,
'product_qty': 1.0,
- 'routing_id': cls.routing_1.id,
'type': 'normal',
})
cls.bom_refri_line1 = cls.env['mrp.bom.line'].create({
diff --git a/addons/mrp_product_expiry/tests/test_mrp_product_expiry.py b/addons/mrp_product_expiry/tests/test_mrp_product_expiry.py
index 0633317f6b1..e5dda34c14d 100644
--- a/addons/mrp_product_expiry/tests/test_mrp_product_expiry.py
+++ b/addons/mrp_product_expiry/tests/test_mrp_product_expiry.py
@@ -64,14 +64,6 @@ class TestStockProductionLot(TestStockCommon):
'time_stop': 5,
'time_efficiency': 80,
})
- cls.routing = cls.env['mrp.routing'].create({'name': 'COOK'})
- cls.operation = cls.env['mrp.routing.workcenter'].create({
- 'name': 'Bake in the oven',
- 'workcenter_id': cls.workcenter.id,
- 'routing_id': cls.routing.id,
- 'time_cycle': 15,
- 'sequence': 1,
- })
def test_01_product_produce(self):
""" Checks user doesn't get a confirmation wizard when they produces with
@@ -122,7 +114,14 @@ class TestStockProductionLot(TestStockCommon):
""" Checks user doesn't get a confirmation wizard when they makes a
workorder without expired components. """
# Set a routing on the BOM.
- self.bom_apple_pie.routing_id = self.routing
+ self.bom_apple_pie.write({
+ 'operation_ids': [(0, 0, {
+ 'name': 'Bake in the oven',
+ 'workcenter_id': self.workcenter.id,
+ 'time_cycle': 15,
+ 'sequence': 1,
+ })],
+ })
# Creates the MO, starts it and plans the Work Order.
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product_apple_pie
@@ -149,7 +148,14 @@ class TestStockProductionLot(TestStockCommon):
""" Checks user doesn't get a confirmation wizard when they makes a
workorder without expired components. """
# Set a routing on the BOM.
- self.bom_apple_pie.routing_id = self.routing
+ self.bom_apple_pie.write({
+ 'operation_ids': [(0, 0, {
+ 'name': 'Bake in the oven',
+ 'workcenter_id': self.workcenter.id,
+ 'time_cycle': 15,
+ 'sequence': 1,
+ })],
+ })
# Creates the MO, starts it and plans the Work Order.
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product_apple_pie