781 lines
36 KiB
Python
781 lines
36 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import Command
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.tests import Form
|
|
from odoo.tests.common import new_test_user
|
|
|
|
from .test_common import TestPlmCommon
|
|
|
|
|
|
class TestMrpPlm(TestPlmCommon):
|
|
|
|
def test_create_eco_from_production_using_uom(self):
|
|
""" Creates an ECO from a Manufacturing Order (using different UoM than its BoM) and checks
|
|
the modifications done in the MO are in the revised BoM."""
|
|
self.env.user.group_ids += self.env.ref('uom.group_uom')
|
|
uom_unit = self.env.ref('uom.product_uom_unit')
|
|
uom_dozen = self.env.ref('uom.product_uom_dozen')
|
|
# Creates a BoM.
|
|
common_vals = {'is_storable': True}
|
|
finished_product = self.env['product.product'].create(dict(common_vals, name="Clover"))
|
|
component_1 = self.env['product.product'].create(dict(common_vals, name="Clover's stem"))
|
|
component_2 = self.env['product.product'].create(dict(common_vals, name="Clover's leaf"))
|
|
bom = self.env['mrp.bom'].create({
|
|
'product_tmpl_id': finished_product.product_tmpl_id.id,
|
|
'product_qty': 1.0,
|
|
'bom_line_ids': [
|
|
Command.create({'product_id': component_1.id, 'product_qty': 1}),
|
|
Command.create({'product_id': component_2.id, 'product_qty': 3}),
|
|
]
|
|
})
|
|
# Creates a new MO, modifies some fields and confirms it.
|
|
mo_form = Form(self.env['mrp.production'])
|
|
mo_form.bom_id = bom
|
|
mo_form.product_qty = 3
|
|
mo_form.product_uom_id = uom_dozen
|
|
with mo_form.move_raw_ids.edit(1) as clover_leaves_raw_move:
|
|
clover_leaves_raw_move.product_uom_qty = 144
|
|
mo = mo_form.save()
|
|
mo_form = Form(mo)
|
|
mo = mo_form.save()
|
|
mo.action_confirm()
|
|
# Generates an ECO from the MO and starts the revision.
|
|
action = mo.action_create_eco()
|
|
new_eco_form = Form(self.env['mrp.eco'].with_context(action['context']))
|
|
new_eco = new_eco_form.save()
|
|
new_eco.will_update_version = False
|
|
new_eco.action_new_revision()
|
|
# Checks the ECO's revision BoM has the expected changes.
|
|
new_bom = new_eco.new_bom_id
|
|
self.assertEqual(new_bom.version, bom.version, "Update version was unchecked in eco, so new bom version should equal original bom's")
|
|
self.assertRecordValues(new_bom.bom_line_ids, [
|
|
{'product_id': component_1.id, 'product_qty': 1, 'product_uom_id': uom_unit.id},
|
|
{'product_id': component_2.id, 'product_qty': 4, 'product_uom_id': uom_unit.id},
|
|
])
|
|
|
|
# Does the same test but switches the BoM and MO's UoM, and use an UoM
|
|
# from another UoM's category for one of the component.
|
|
uom_cm = self.env.ref('uom.product_uom_cm')
|
|
comp_cm_vals = {'name': "Clover's stem", 'uom_id': uom_cm.id}
|
|
component_1_in_cm = self.env['product.product'].create(dict(common_vals, **comp_cm_vals))
|
|
bom_dozen = self.env['mrp.bom'].create({
|
|
'product_tmpl_id': finished_product.product_tmpl_id.id,
|
|
'product_qty': 1.0,
|
|
'product_uom_id': uom_dozen.id,
|
|
'bom_line_ids': [
|
|
Command.create({
|
|
'product_id': component_1_in_cm.id,
|
|
'product_qty': 84,
|
|
'product_uom_id': uom_cm.id
|
|
}),
|
|
Command.create({
|
|
'product_id': component_2.id,
|
|
'product_qty': 3,
|
|
'product_uom_id': uom_dozen.id
|
|
}),
|
|
]
|
|
})
|
|
# Creates a new MO, modifies some fields and confirms it.
|
|
mo_form = Form(self.env['mrp.production'])
|
|
mo_form.bom_id = bom_dozen
|
|
mo_form.product_qty = 3
|
|
mo_form.product_uom_id = uom_unit
|
|
mo = mo_form.save()
|
|
with mo_form.move_raw_ids.edit(0) as clover_stem_raw_move:
|
|
clover_stem_raw_move.product_uom_qty = 15
|
|
with mo_form.move_raw_ids.edit(1) as clover_leaves_raw_move:
|
|
clover_leaves_raw_move.product_uom_qty = 12
|
|
clover_leaves_raw_move.product_uom = uom_unit
|
|
mo = mo_form.save()
|
|
mo_form = Form(mo)
|
|
mo = mo_form.save()
|
|
mo.action_confirm()
|
|
# Generates an ECO from the MO and starts the revision.
|
|
action = mo.action_create_eco()
|
|
new_eco_form = Form(self.env['mrp.eco'].with_context(action['context']))
|
|
new_eco = new_eco_form.save()
|
|
new_eco.action_new_revision()
|
|
# Checks the ECO's revision BoM has the expected changes.
|
|
new_bom = new_eco.new_bom_id
|
|
self.assertEqual(new_bom.version, bom.version + 1, "By default, new bom version should be original bom's + 1")
|
|
self.assertRecordValues(new_bom.bom_line_ids, [
|
|
{'product_id': component_1_in_cm.id, 'product_qty': 60, 'product_uom_id': uom_cm.id},
|
|
{'product_id': component_2.id, 'product_qty': 4, 'product_uom_id': uom_dozen.id},
|
|
])
|
|
|
|
def test_rebase_with_old_bom_change(self):
|
|
"Test eco rebase with old bom changes."
|
|
|
|
# Create eco for bill of material.
|
|
version_num = self.bom_table.version
|
|
self.eco1 = self._create_eco('ECO1', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
# Start new revision of eco1.
|
|
self.eco1.action_new_revision()
|
|
|
|
# Eco should be in progress and new revision of BoM should be created.
|
|
self.assertTrue(self.eco1.new_bom_id, "New revision of bill of material should be created.")
|
|
self.assertEqual(self.eco1.state, 'progress', "Wrong state on eco")
|
|
|
|
# Change old bom lines
|
|
old_bom_leg = self.bom_table.bom_line_ids.filtered(lambda x: x.product_id == self.table_leg)
|
|
new_bom_leg = self.eco1.new_bom_id.bom_line_ids.filtered(lambda x: x.product_id == self.table_leg)
|
|
|
|
# Update quantity current bill of materials.
|
|
old_bom_leg.product_qty = 8
|
|
|
|
# Check status of eco
|
|
self.assertEqual(self.eco1.state, 'rebase', "Wrong state on eco.")
|
|
self.assertEqual(new_bom_leg.product_qty, 3, "Wrong table leg quantity on new revision of BoM.")
|
|
|
|
# Rebase eco1 with current BoM changes ( 3 + 5 ( New added product )).
|
|
self.eco1.apply_rebase()
|
|
self.assertEqual(self.eco1.new_bom_revision, version_num + 1, "By default, new bom version should match original bom's + 1")
|
|
|
|
# Check quantity of table lag on new revision of BoM.
|
|
self.assertEqual(new_bom_leg.product_qty, 8, "Wrong table leg quantity on new revision of bom.")
|
|
|
|
# Add new bom line with product bolt in old BoM.
|
|
self.env['mrp.bom.line'].create({'product_id': self.table_bolt.id, 'bom_id': self.bom_table.id, 'product_qty': 3})
|
|
|
|
# Check status of eco and rebase line after adding new product on current BoM.
|
|
self.assertEqual(self.eco1.state, 'rebase', "Wrong state on eco.")
|
|
self.assertEqual(len(self.eco1.bom_rebase_ids), 1, "Wrong rebase line on eco.")
|
|
self.assertEqual(self.eco1.bom_rebase_ids.change_type, 'add', "Wrong type on rebase line.")
|
|
self.eco1.will_update_version = False
|
|
self.assertEqual(self.eco1.new_bom_revision, version_num, "Update version was unchecked in eco, so new bom version should equal original bom's")
|
|
|
|
# Rebase eco1 with BoM changes.
|
|
self.eco1.apply_rebase()
|
|
self.assertEqual(self.eco1.new_bom_revision, version_num, "Update version was unchecked in eco, rebasing should leave new bom version as equal to original bom's")
|
|
|
|
new_bom_bolt = self.eco1.new_bom_id.bom_line_ids.filtered(lambda x: x.product_id == self.table_bolt)
|
|
|
|
# Check eco status and bom line should be added on new bom revision.
|
|
self.assertTrue(new_bom_bolt, "BoM line should be added for bolt on new revision of BoM.")
|
|
self.assertEqual(self.eco1.state, 'progress', "Wrong state on eco.")
|
|
|
|
# Remove line form current BoM
|
|
self.eco1.bom_id.bom_line_ids.filtered(lambda x: x.product_id == self.table_bolt).unlink()
|
|
|
|
# Check status of eco with rebase lines.
|
|
self.assertEqual(self.eco1.state, 'rebase', "Wrong state on eco.")
|
|
self.assertEqual(len(self.eco1.bom_rebase_ids), 1, "Wrong BoM rebase line on eco.")
|
|
self.assertEqual(self.eco1.bom_rebase_ids.change_type, 'update', "Wrong type on rebase line.")
|
|
self.assertEqual(self.eco1.bom_rebase_ids.upd_product_qty, -3, "Wrong quantity on rebase line.")
|
|
|
|
# Rebase eco
|
|
self.eco1.apply_rebase()
|
|
self.assertFalse(self.eco1.new_bom_id.bom_line_ids.filtered(lambda x: x.product_id == self.table_bolt), "BoM line should be unlink from new revision of BoM.")
|
|
|
|
# Change old BoM leg and new revision BoM leg quantity.
|
|
old_bom_leg.product_qty = 10
|
|
new_bom_leg.product_qty = 12
|
|
self.assertEqual(self.eco1.bom_rebase_ids.change_type, 'update', "Wrong type on rebase line.")
|
|
self.assertEqual(self.eco1.bom_rebase_ids.upd_product_qty, 2, "Wrong quantity on rebase line.")
|
|
|
|
# Rebase ecos with changes of old bill of material.
|
|
self.eco1.apply_rebase()
|
|
self.assertEqual(self.eco1.state, 'conflict', "Wrong state on eco.")
|
|
|
|
# Manually resolve conflict.
|
|
self.eco1.conflict_resolve()
|
|
self.assertEqual(self.eco1.state, 'progress', "Wrong state on eco.")
|
|
|
|
def test_rebase_with_previous_eco_change(self):
|
|
"Test eco rebase with previous eco changes."
|
|
|
|
# --------------------------------
|
|
# Create ecos for bill of material.
|
|
# ---------------------------------
|
|
|
|
eco1 = self._create_eco('ECO1', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
eco2 = self._create_eco('ECO2', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
eco3 = self._create_eco('ECO3', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
version_num = self.bom_table.version
|
|
|
|
# Start new revision of eco1, eco2, eco3
|
|
eco1.action_new_revision()
|
|
eco2.action_new_revision()
|
|
eco3.action_new_revision()
|
|
|
|
# -----------------------------------------
|
|
# Check eco status after start new revision.
|
|
# ------------------------------------------
|
|
|
|
self.assertEqual(eco1.state, 'progress', "Wrong state on eco1.")
|
|
self.assertEqual(eco2.state, 'progress', "Wrong state on eco2.")
|
|
self.assertEqual(eco3.state, 'progress', "Wrong state on eco2.")
|
|
|
|
# ---------------------------------------------------------------
|
|
# ECO 1 : Update Table Leg quantity in new BoM revision.
|
|
# ---------------------------------------------------------------
|
|
|
|
eco1_new_table_leg = eco1.new_bom_id.bom_line_ids.filtered(lambda x: x.product_id == self.table_leg)
|
|
eco1_new_table_leg.product_qty = 6
|
|
|
|
# -------------------------------------------------------------------------------
|
|
# ECO 1 : Check status of ecos after apply changes and activate new bom revision.
|
|
# -------------------------------------------------------------------------------
|
|
|
|
eco1.action_apply()
|
|
self.assertEqual(eco1.new_bom_revision, version_num + 1, "By default, new bom version should match original bom's + 1")
|
|
self.assertFalse(eco1.bom_id.active, "Old BoM of eco1 should be deactivated.")
|
|
self.assertTrue(eco1.new_bom_id.active, "New BoM revision of ECO 1 should be activated.")
|
|
# Check eco status after activate new bom revision of eco.
|
|
self.assertEqual(eco1.state, 'done', "Wrong state on eco1.")
|
|
self.assertEqual(eco2.state, 'rebase', "Wrong state on eco2.")
|
|
self.assertEqual(eco3.state, 'rebase', "Wrong state on eco3.")
|
|
|
|
# ------------------------------
|
|
# ECO 2 : Rebase with ECO 1 changes.
|
|
# ------------------------------
|
|
|
|
eco2.will_update_version = False
|
|
eco2.apply_rebase()
|
|
self.assertEqual(eco2.state, 'progress', "Wrong state on eco2.")
|
|
self.assertEqual(eco1.new_bom_id.id, eco2.bom_id.id, "Eco2 BoM should replace with new activated BoM revision of Eco1.")
|
|
self.assertEqual(eco2.new_bom_revision, eco1.new_bom_id.version, "Update version was unchecked in eco, rebasing should leave new bom version as BoM revision of Eco1")
|
|
|
|
# ----------------------------------------------------------------------
|
|
# ECO 2 : Add new product 'Table Bolt'
|
|
# ----------------------------------------------------------------------
|
|
|
|
eco2.new_bom_id.bom_line_ids.create({'product_id': self.table_bolt.id, 'bom_id': eco2.new_bom_id.id, 'product_qty': 3})
|
|
self.assertTrue(eco2.bom_change_ids, "Eco 2 should have BoM change lines.")
|
|
|
|
# -------------------------------------------------------------------------------
|
|
# ECO 2 : Check status of after apply changes and activate new bom revision.
|
|
# -------------------------------------------------------------------------------
|
|
|
|
eco2.action_apply()
|
|
|
|
self.assertFalse(eco1.bom_id.active, "BoM of ECO 1 should be deactivated")
|
|
self.assertFalse(eco1.new_bom_id.active, "BoM revision of ECO 1 should be deactivated")
|
|
self.assertTrue(eco2.new_bom_id.active, "BoM revision of ECO 2 should be activated")
|
|
self.assertEqual(eco2.new_bom_revision, eco1.new_bom_id.version, "Update version was unchecked in eco, new bom version should still match BoM revision of Eco1")
|
|
|
|
# -----------------------------------------------------
|
|
# ECO3 : Change same line in eco3 as changes in eco1.
|
|
# ----------------------------------------------------
|
|
|
|
eco3_new_table_leg = eco3.new_bom_id.bom_line_ids.filtered(lambda x: x.product_id == self.table_leg)
|
|
eco3_new_table_leg.product_qty = 4
|
|
|
|
# -----------------------------------
|
|
# Rebase eco3 with eco1 BoM changes.
|
|
# -----------------------------------
|
|
|
|
eco3.apply_rebase()
|
|
|
|
# Check status of eco3 after rebase.
|
|
self.assertEqual(eco3.state, 'conflict', "Wrong state on eco.")
|
|
|
|
# Resolve conflict manually.
|
|
self.assertTrue(eco3.previous_change_ids.ids, "Wrong previous bom change on bom lines.")
|
|
eco3.conflict_resolve()
|
|
self.assertEqual(eco3.state, 'progress', "Wrong state on eco.")
|
|
eco3.action_apply()
|
|
self.assertFalse(eco2.new_bom_id.active, "BoM revision of ECO 2 should be deactivated")
|
|
self.assertTrue(eco3.new_bom_id.active, "BoM revision of ECO 3 should be activated")
|
|
self.assertEqual(eco3.new_bom_revision, eco2.new_bom_id.version + 1, "By default, new bom version should match eco2 bom's + 1")
|
|
self.assertFalse(eco3.previous_change_ids.ids)
|
|
self.assertFalse(eco3.bom_rebase_ids.ids)
|
|
|
|
def test_operation_change(self):
|
|
"Test eco with bom operation changes."
|
|
# --------------------------------
|
|
# Create ecos for bill of material.
|
|
# ---------------------------------
|
|
|
|
eco1 = self._create_eco('ECO1', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
|
|
# Start new revision of eco1
|
|
eco1.action_new_revision()
|
|
|
|
# -----------------------------------------
|
|
# Check eco status after start new revision.
|
|
# ------------------------------------------
|
|
|
|
self.assertEqual(eco1.state, 'progress', "Wrong state on eco1.")
|
|
|
|
# ---------------------------------------------------------------
|
|
# ECO 1 : Update duration on operation1
|
|
# ---------------------------------------------------------------
|
|
|
|
op1 = eco1.new_bom_id.operation_ids.filtered(lambda x: x.workcenter_id == self.workcenter_1)
|
|
op1.time_cycle_manual = 20
|
|
|
|
# Check correctness
|
|
op1_change = eco1.routing_change_ids.filtered(lambda x: x.workcenter_id == self.workcenter_1)
|
|
self.assertEqual(op1_change[0].change_type, 'update', "Wrong type on operation change line.")
|
|
self.assertEqual(op1_change[0].upd_time_cycle_manual, 10.0, "Wrong duration change.")
|
|
|
|
# ---------------------------------------------------------------
|
|
# ECO 1 : Remove operation2
|
|
# ---------------------------------------------------------------
|
|
|
|
op2 = eco1.new_bom_id.operation_ids.filtered(lambda x: x.workcenter_id == self.workcenter_2)
|
|
op2.unlink()
|
|
|
|
op2_change = eco1.routing_change_ids.filtered(lambda x: x.workcenter_id == self.workcenter_2)
|
|
self.assertEqual(op2_change.change_type, 'remove', "Wrong type on operation change line.")
|
|
|
|
# ---------------------------------------------------------------
|
|
# ECO 1 : Add operation3
|
|
# ---------------------------------------------------------------
|
|
|
|
eco1.new_bom_id.operation_ids.create({
|
|
'name': 'op3',
|
|
'bom_id': eco1.new_bom_id.id,
|
|
'workcenter_id': self.workcenter_3.id,
|
|
'time_cycle_manual': 10,
|
|
'sequence': 2,
|
|
})
|
|
|
|
op3_change = eco1.routing_change_ids.filtered(lambda x: x.workcenter_id == self.workcenter_3)
|
|
self.assertEqual(op3_change.change_type, 'add', "Wrong type on operation change line.")
|
|
self.assertEqual(op3_change.upd_time_cycle_manual, 10.0, "Wrong duration change.")
|
|
|
|
def test_operation_eco_counting(self):
|
|
""" Test when count ECOs for a bom, all ECOs, including the ones for previous
|
|
version boms, are counted.
|
|
"""
|
|
eco1 = self._create_eco('ECO1', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
eco1.action_new_revision()
|
|
eco1.action_apply()
|
|
self.assertEqual(eco1.stage_id, self.eco_stage_folded, "Wrong stage.")
|
|
|
|
eco2 = self._create_eco('ECO2', eco1.new_bom_id, self.eco_type.id, self.eco_stage.id)
|
|
eco2.action_new_revision()
|
|
self.assertEqual(eco2.stage_id, self.eco_stage, "Wrong stage.")
|
|
|
|
self.assertEqual(eco1.new_bom_id.eco_count, 2)
|
|
|
|
def test_do_not_merge_bom_lines(self):
|
|
"""
|
|
Test that when applying a mrp.eco on a BoM for which the same product is present twice in the bom lines
|
|
(same product, multiple operations), the BoM changes are correctly computed
|
|
"""
|
|
workcenter = self.env['mrp.workcenter'].create({'name': 'A center'})
|
|
product_a = self.env['product.product'].create({'name': 'a_product'})
|
|
product_b = self.env['product.product'].create({'name': 'b_product'})
|
|
bom = self.env['mrp.bom'].create({
|
|
'product_id': product_a.id,
|
|
'product_tmpl_id': product_a.product_tmpl_id.id,
|
|
'product_qty': 1,
|
|
'type': 'normal',
|
|
'bom_line_ids': [
|
|
(0, 0, {'product_id': product_b.id, 'product_qty': 2}),
|
|
(0, 0, {'product_id': product_b.id, 'product_qty': 3}),
|
|
]
|
|
})
|
|
operation_a = self.env['mrp.routing.workcenter'].create({
|
|
'name': 'Some operation',
|
|
'workcenter_id': workcenter.id,
|
|
'bom_id': bom.id
|
|
})
|
|
operation_b = self.env['mrp.routing.workcenter'].create({
|
|
'name': 'Other operation',
|
|
'workcenter_id': workcenter.id,
|
|
'bom_id': bom.id
|
|
})
|
|
bom.bom_line_ids[0].operation_id = operation_a.id
|
|
bom.bom_line_ids[1].operation_id = operation_b.id
|
|
type_id = self.env['mrp.eco.type'].search([], limit=1).id
|
|
mrp_eco = self.env['mrp.eco'].create({
|
|
'name': 'a plm',
|
|
'bom_id': bom.id,
|
|
'product_tmpl_id': bom.product_tmpl_id.id,
|
|
'type_id': type_id,
|
|
'type': 'bom'
|
|
})
|
|
mrp_eco.action_new_revision()
|
|
self.assertEqual(len(mrp_eco.bom_change_ids), 0)
|
|
mrp_eco.new_bom_id.bom_line_ids[0].product_qty = 13 # Change from 2 to 13
|
|
self.assertRecordValues(mrp_eco.bom_change_ids, [
|
|
{'change_type': 'update', 'upd_product_qty': 11},
|
|
])
|
|
|
|
def test_bom_changes(self):
|
|
"""
|
|
Test that when creating a `mrp.eco` for a BOM with operations and components consumed in the operations,
|
|
the difference lines between the old and the new BOM is done correctly
|
|
"""
|
|
workcenter = self.env['mrp.workcenter'].create({'name': 'wc 1'})
|
|
operation_1 = self.env['mrp.routing.workcenter'].create({
|
|
'name': 'op1',
|
|
'workcenter_id': workcenter.id,
|
|
'bom_id': self.bom_table.id
|
|
})
|
|
operation_2 = self.env['mrp.routing.workcenter'].create({
|
|
'name': 'op2',
|
|
'workcenter_id': workcenter.id,
|
|
'bom_id': self.bom_table.id
|
|
})
|
|
self.bom_table.operation_ids = [(6, 0, (operation_1 + operation_2).ids)]
|
|
# Consume the first component in the first operation
|
|
self.bom_table.bom_line_ids[0].operation_id = self.bom_table.operation_ids[0]
|
|
# Create eco for bill of material.
|
|
eco1 = self._create_eco('ECO1', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
# Start new revision of eco1.
|
|
eco1.action_new_revision()
|
|
self.assertEqual(eco1.state, 'progress', "Wrong state on eco1.")
|
|
# Make sure there is no change between the two BOMs
|
|
self.assertEqual(len(eco1.bom_change_ids), 0)
|
|
# Modify the new BOM to consume the first component in the second operation
|
|
eco1.new_bom_id.bom_line_ids[0].operation_id = eco1.new_bom_id.operation_ids[1]
|
|
# A bom changes must be created
|
|
self.assertRecordValues(eco1.bom_change_ids, [
|
|
{'change_type': 'add', 'operation_change': 'op2'},
|
|
{'change_type': 'remove', 'operation_change': 'op1'},
|
|
])
|
|
|
|
def test_product_version(self):
|
|
"""Test product version number increases or doesn't increase after a product ECO is done
|
|
depending on setting.
|
|
"""
|
|
version_num = self.table.product_tmpl_id.version
|
|
mrp_eco = self.env['mrp.eco'].create({
|
|
'name': 'a plm',
|
|
'product_tmpl_id': self.table.product_tmpl_id.id,
|
|
'stage_id': self.eco_stage.id,
|
|
'type_id': self.eco_type.id,
|
|
'type': 'product',
|
|
})
|
|
mrp_eco.action_new_revision()
|
|
mrp_eco.action_apply()
|
|
self.assertEqual(mrp_eco.state, 'done')
|
|
self.assertEqual(self.table.product_tmpl_id.version, version_num + 1, "By default, product's version should increment when eco applied")
|
|
|
|
version_num = self.table.product_tmpl_id.version
|
|
mrp_eco_no_change = self.env['mrp.eco'].create({
|
|
'name': 'plm no version change',
|
|
'product_tmpl_id': self.table.product_tmpl_id.id,
|
|
'stage_id': self.eco_stage.id,
|
|
'type_id': self.eco_type.id,
|
|
'type': 'product',
|
|
'will_update_version': False
|
|
})
|
|
mrp_eco_no_change.action_new_revision()
|
|
mrp_eco_no_change.action_apply()
|
|
self.assertEqual(mrp_eco_no_change.state, 'done')
|
|
self.assertEqual(self.table.product_tmpl_id.version, version_num, "Update version was unchecked in eco, so product's version shouldn't increment when eco applied")
|
|
|
|
def test_new_bom_version(self):
|
|
"""Test new bom version number increases or doesn't increase depending on state + setting
|
|
We force the state values because it's assumed there are other tests checking that the state matches
|
|
"""
|
|
# updates version = True by default
|
|
version_num = self.bom_table.version
|
|
bom_eco = self._create_eco('bom_eco', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
bom_eco.action_new_revision()
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
# unchecking updates version => the new bom version auto-increments down even when not done
|
|
bom_eco.will_update_version = False
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num)
|
|
# rechecking updates version => the new bom version auto-increments up when not done
|
|
bom_eco.will_update_version = True
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
|
|
# check that version updates occur as long as eco != Done
|
|
# note that test for rebasing increments version as expected is done
|
|
# in another test due to conditions needed for rebasing to work
|
|
bom_eco.state = 'rebase'
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
bom_eco.will_update_version = False
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num)
|
|
bom_eco.will_update_version = True
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
|
|
bom_eco.state = 'conflict'
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
bom_eco.will_update_version = False
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num)
|
|
bom_eco.will_update_version = True
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
bom_eco.conflict_resolve()
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
bom_eco.state = 'conflict'
|
|
bom_eco.will_update_version = False
|
|
bom_eco.conflict_resolve()
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num)
|
|
bom_eco.will_update_version = True
|
|
|
|
bom_eco.state = 'progress'
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
bom_eco.action_apply()
|
|
self.assertEqual(bom_eco.state, 'done')
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
|
|
# in theory the field shouldn't be update-able when eco is done, but let's check anyways
|
|
bom_eco.will_update_version = False
|
|
self.assertEqual(bom_eco.new_bom_revision, version_num + 1)
|
|
|
|
def test_mrp_user_without_plm_permission_can_create_bom(self):
|
|
mrp_manager = new_test_user(
|
|
self.env, 'temp_stock_manager', 'mrp.group_mrp_manager',
|
|
)
|
|
bom = self.env['mrp.bom'].with_user(mrp_manager).create({
|
|
'product_id': self.table.id,
|
|
'product_tmpl_id': self.table.product_tmpl_id.id,
|
|
'bom_line_ids': [
|
|
(0, 0, {'product_id': self.table_sheet.id, 'product_qty': 1}),
|
|
],
|
|
})
|
|
|
|
# Admin creates an ECO
|
|
self.env['mrp.eco'].sudo().create({
|
|
'name': 'a plm',
|
|
'bom_id': bom.id,
|
|
'product_tmpl_id': bom.product_tmpl_id.id,
|
|
'type_id': self.env['mrp.eco.type'].search([], limit=1).id,
|
|
'type': 'bom',
|
|
# Status 'progress' or 'rebase' is required to trigger rebase lines
|
|
'state': 'progress',
|
|
})
|
|
|
|
# MRP manager should still be able to edit the original object
|
|
bom.bom_line_ids[0].product_qty = 2
|
|
|
|
def create_eco(self, eco_type):
|
|
eco_form = Form(self.env['mrp.eco'])
|
|
eco_form.type = eco_type
|
|
eco_form.type_id = self.eco_type
|
|
eco_form.name = 'test_plm_bom_document'
|
|
eco_form.product_tmpl_id = self.table_bolt.product_tmpl_id
|
|
eco = eco_form.save()
|
|
eco.action_new_revision()
|
|
return eco
|
|
|
|
def test_plm_bom_document(self):
|
|
|
|
doc_product = self.env['product.document'].create({
|
|
'name': 'doc_product_mo',
|
|
'attached_on_mrp': 'hidden',
|
|
'res_id': self.table_bolt.id,
|
|
'res_model': 'product.product',
|
|
})
|
|
doc_template = self.env['product.document'].create({
|
|
'name': 'doc_product_mo',
|
|
'attached_on_mrp': 'hidden',
|
|
'res_id': self.table_bolt.product_tmpl_id.id,
|
|
'res_model': 'product.template',
|
|
})
|
|
self.env['mrp.bom'].create({
|
|
'product_tmpl_id': self.table_bolt.product_tmpl_id.id,
|
|
'product_uom_id': self.table_bolt.uom_id.id,
|
|
'product_qty': 1.0,
|
|
'type': 'normal',
|
|
})
|
|
|
|
eco = self.create_eco('bom')
|
|
self.assertEqual(eco.document_count, 0, 'Document count should be 0, no docs are visible on bom')
|
|
|
|
doc_template.attached_on_mrp = 'bom'
|
|
doc_product.attached_on_mrp = 'bom'
|
|
eco = self.create_eco('bom')
|
|
self.assertEqual(eco.document_count, 2, 'Document count should be 2, docs related to the template and the product have to be copied.')
|
|
docs = self.env['product.document'].search([('id', 'in', [doc_product.id, doc_template.id])])
|
|
self.assertNotEqual(eco.document_ids, docs, 'The documents have to be differents')
|
|
|
|
def test_plm_product_document(self):
|
|
|
|
self.env['product.document'].create({
|
|
'name': 'doc_product_mo',
|
|
'attached_on_mrp': 'hidden',
|
|
'res_id': self.table_bolt.id,
|
|
'res_model': 'product.product',
|
|
})
|
|
self.env['product.document'].create({
|
|
'name': 'doc_product_mo',
|
|
'attached_on_mrp': 'hidden',
|
|
'res_id': self.table_bolt.product_tmpl_id.id,
|
|
'res_model': 'product.template',
|
|
})
|
|
eco = self.create_eco('product')
|
|
self.assertEqual(eco.document_count, 2, 'Document count should be 2, all docs have to be copied.')
|
|
|
|
def test_duplicate_eco_template_with_same_user_and_stage(self):
|
|
"""Test that two ECO approval templates cannot be created
|
|
with the same user for the same stage."""
|
|
self.env['mrp.eco.approval.template'].create({
|
|
'name': 'eco approval template',
|
|
'approval_type': 'mandatory',
|
|
'user_ids': [Command.set(self.env.ref('base.user_admin').ids)],
|
|
'stage_id': self.eco_stage.id,
|
|
})
|
|
with self.assertRaises(ValidationError):
|
|
self.env['mrp.eco.approval.template'].create({
|
|
'name': 'eco approval template',
|
|
'approval_type': 'mandatory',
|
|
'user_ids': [Command.set(self.env.ref('base.user_admin').ids)],
|
|
'stage_id': self.eco_stage.id,
|
|
})
|
|
|
|
def test_eco_count(self):
|
|
"""Test that the ECO count is correctly updated for both
|
|
the product and the BOM when creating a new revision.
|
|
"""
|
|
self.assertEqual(self.table.eco_count, 0)
|
|
self.assertEqual(self.bom_table.eco_count, 0)
|
|
# Create an ECO for the BOM and verify the count for both the BOM and the product
|
|
eco = self.env['mrp.eco'].create({
|
|
'name': 'eco',
|
|
'product_tmpl_id': self.table.product_tmpl_id.id,
|
|
'type_id': self.eco_type.id,
|
|
'type': 'bom',
|
|
'bom_id': self.bom_table.id,
|
|
})
|
|
self.table.invalidate_recordset(['eco_count'])
|
|
self.table.product_tmpl_id.invalidate_recordset(['eco_count'])
|
|
self.bom_table.invalidate_recordset(['eco_count'])
|
|
self.assertEqual(self.table.eco_count, 0)
|
|
self.assertEqual(self.bom_table.eco_count, 1)
|
|
# Update the ECO type to "Product" and verify that
|
|
# the bom_id field is cleared and the count is updated
|
|
eco.type = 'product'
|
|
self.assertFalse(eco.bom_id)
|
|
self.table.invalidate_recordset(['eco_count'])
|
|
self.table.product_tmpl_id.invalidate_recordset(['eco_count'])
|
|
self.bom_table.invalidate_recordset(['eco_count'])
|
|
self.assertEqual(self.table.eco_count, 1)
|
|
self.assertEqual(self.bom_table.eco_count, 0)
|
|
|
|
def _create_eco_approval_workflow(self):
|
|
"""Create an ECO type with an approval workflow for testing purposes."""
|
|
self.random_approver = new_test_user(
|
|
self.env, "random_approver", "mrp_plm.group_plm_manager"
|
|
)
|
|
self.mrp_admin = new_test_user(
|
|
self.env, "mrp_admin", "mrp_plm.group_plm_manager"
|
|
)
|
|
self.approval_eco_type = self.env["mrp.eco.type"].create(
|
|
{
|
|
"name": "ECO Type with approval",
|
|
}
|
|
)
|
|
self.approval_new_stage = self.env["mrp.eco.stage"].create(
|
|
{
|
|
"name": "New",
|
|
"sequence": 0,
|
|
"type_ids": [Command.link(self.approval_eco_type.id)],
|
|
}
|
|
)
|
|
self.approval_first_stage = self.env["mrp.eco.stage"].create(
|
|
{
|
|
"name": "First approval",
|
|
"type_ids": [Command.link(self.approval_eco_type.id)],
|
|
"sequence": 10,
|
|
"approval_template_ids": [
|
|
Command.create(
|
|
{
|
|
"name": "First Approval",
|
|
"user_ids": [Command.set(self.mrp_admin.ids)],
|
|
"approval_type": "optional",
|
|
}
|
|
),
|
|
Command.create(
|
|
{
|
|
"name": "Second Approval",
|
|
"user_ids": [Command.set(self.random_approver.ids)],
|
|
"approval_type": "mandatory",
|
|
}
|
|
),
|
|
],
|
|
}
|
|
)
|
|
self.approval_done_stage = self.env["mrp.eco.stage"].create(
|
|
{
|
|
"name": "Done",
|
|
"type_ids": [Command.link(self.approval_eco_type.id)],
|
|
"final_stage": True,
|
|
"sequence": 30,
|
|
}
|
|
)
|
|
|
|
def test_dashboard_waiting_for_me(self):
|
|
"""Test that the dashboard computes the ECOs waiting for my approval properly."""
|
|
self._create_eco_approval_workflow()
|
|
eco = self._create_eco(
|
|
"ECO1",
|
|
self.bom_table,
|
|
self.approval_eco_type.id,
|
|
self.approval_new_stage.id,
|
|
)
|
|
eco.action_new_revision()
|
|
eco.stage_id = self.approval_first_stage
|
|
# Just landed on first stage, both users need to approve
|
|
self.approval_eco_type.invalidate_recordset(["nb_approvals_my"])
|
|
self.assertEqual(
|
|
self.approval_eco_type.with_user(self.random_approver).nb_approvals_my,
|
|
1,
|
|
"Random approver should have 1 approval waiting",
|
|
)
|
|
self.approval_eco_type.invalidate_recordset(["nb_approvals_my"])
|
|
self.assertEqual(
|
|
self.approval_eco_type.with_user(self.mrp_admin).nb_approvals_my,
|
|
1,
|
|
"Admin should have 1 approval waiting",
|
|
)
|
|
# Admin approves
|
|
eco.with_user(self.mrp_admin).approve()
|
|
# Check that counter is correct
|
|
self.approval_eco_type.invalidate_recordset(["nb_approvals_my"])
|
|
self.assertEqual(
|
|
self.approval_eco_type.with_user(self.random_approver).nb_approvals_my,
|
|
1,
|
|
"Random approver should have 1 approval waiting",
|
|
)
|
|
self.approval_eco_type.invalidate_recordset(["nb_approvals_my"])
|
|
self.assertEqual(
|
|
self.approval_eco_type.with_user(self.mrp_admin).nb_approvals_my,
|
|
0,
|
|
"Admin should have 0 approval waiting",
|
|
)
|
|
|
|
def test_compute_component_upd_product_qty(self):
|
|
"""Test the correct computation of BoM component quantity changes when
|
|
using high-precision units of measure. We ensure that the updated
|
|
quantity respects the UoM decimal digits instead of the default precision.
|
|
"""
|
|
self.env['decimal.precision'].search([('name', '=', 'Product Unit')]).digits = 4
|
|
self.bom_table.product_uom_id.rounding = 0.0001
|
|
bom_eco = self._create_eco('bom_eco', self.bom_table, self.eco_type.id, self.eco_stage.id)
|
|
bom_eco.action_new_revision()
|
|
self.assertFalse(bom_eco.bom_change_ids)
|
|
self.assertEqual(bom_eco.bom_id.bom_line_ids[0].product_qty, 1.0)
|
|
bom_eco.new_bom_id.bom_line_ids[0].product_qty = 1.0003
|
|
self.assertEqual(bom_eco.new_bom_id.bom_line_ids[0].product_qty, 1.0003)
|
|
self.assertEqual(bom_eco.bom_change_ids.upd_product_qty, 0.0003)
|
|
|
|
def test_eco_attachments(self):
|
|
"""Test that applying an ECO updates the origin_attachment_id for product.document"""
|
|
tmpl = self.env["product.template"].create({"name": "Test"})
|
|
eco_form = Form(self.env["mrp.eco"])
|
|
eco_form.name = "ELCT ECO"
|
|
eco_form.type_id = self.eco_type
|
|
eco_form.type = "product"
|
|
eco_form.product_tmpl_id = tmpl
|
|
eco = eco_form.save()
|
|
eco.stage_id = self.eco_stage
|
|
eco.action_new_revision()
|
|
eco.document_ids = [
|
|
(
|
|
0,
|
|
0,
|
|
{
|
|
"name": "test.pdf",
|
|
"type": "binary",
|
|
"raw": b"Original content",
|
|
"res_id": eco.id,
|
|
"res_model": "mrp.eco",
|
|
},
|
|
)
|
|
]
|
|
doc = eco.document_ids[0]
|
|
old_origin_id = doc.origin_attachment_id
|
|
eco.action_apply()
|
|
self.assertNotEqual(doc.origin_attachment_id, old_origin_id)
|