diff options
| author | Simon Lejeune <sle@openerp.com> | 2020-04-21 10:10:45 +0200 |
|---|---|---|
| committer | Simon Lejeune <sle@openerp.com> | 2020-06-15 16:59:40 +0200 |
| commit | c660770ebd84c2c0d9e56ca5897effc6c138b827 (patch) | |
| tree | 124f67a64758d2ff0899e29260a80b251f56f02a /addons | |
| parent | beccf82e09d536255d9d9cb9bfe58ebae2559843 (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')
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 & 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': [ '|', '&', - ('routing_id', '=', False), + ('bom_has_operations', '=', False), ('state', 'in', ['done', 'cancel']), '&', - ('routing_id', '!=', False), + ('bom_has_operations', '!=', False), ('state', 'not in', ['draft', 'confirmed'])]}"/> <label for="date_planned_finished" string="to" attrs="{'invisible': [ '|', ('id', '=', False), '&', - ('routing_id', '!=', False), + ('bom_has_operations', '!=', False), ('state', 'in', ['draft', 'confirmed']) ]}"/> <field name="date_planned_finished" required="1" attrs="{'readonly': [ '|', '&', - ('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 |
