[FIX] mrp: replace workcenter times when specific capacity is defined
Currently, when a specific capacity is defined for a product in a workcenter, if a setup / cleanup time is given, it will be added to the existing setup / cleanup time of the workcenter. What we want instead is to be able to fully define what the setup/cleanup time is for this specific product. So the given times in the specific capacity override the times defined in the workcenter for that specific product. To help with that, we set the workcenter's setup/cleanup time as a default for their related specific capacities, since if not changed, they will follow the workcenter times. Updated the tests so their specified capacities match the new behaviour. closes odoo/odoo#119212 Related: odoo/upgrade#4576 Signed-off-by: Tiffany Chang <tic@odoo.com>
This commit is contained in:
@@ -299,12 +299,10 @@ class MrpWorkcenter(models.Model):
|
||||
|
||||
def _get_expected_duration(self, product_id):
|
||||
"""Compute the expected duration when using this work-center
|
||||
Always include workcenter startup time and clean-up time.
|
||||
In case there are specific capacities defined in the workcenter
|
||||
that matches the product we are producing. Add the extra-time.
|
||||
Always use the startup / clean-up time from specific capacity if defined.
|
||||
"""
|
||||
capacity = self.capacity_ids.filtered(lambda p: p.product_id == product_id)
|
||||
return self.time_start + self.time_stop + (capacity.time_start + capacity.time_stop if capacity else 0.0)
|
||||
return capacity.time_start + capacity.time_stop if capacity else self.time_start + self.time_stop
|
||||
|
||||
|
||||
class WorkcenterTag(models.Model):
|
||||
@@ -478,12 +476,20 @@ class MrpWorkCenterCapacity(models.Model):
|
||||
_description = 'Work Center Capacity'
|
||||
_check_company_auto = True
|
||||
|
||||
def _default_time_start(self):
|
||||
workcenter_id = self.workcenter_id.id or self.env.context.get('default_workcenter_id')
|
||||
return self.env['mrp.workcenter'].browse(workcenter_id).time_start if workcenter_id else 0.0
|
||||
|
||||
def _default_time_stop(self):
|
||||
workcenter_id = self.workcenter_id.id or self.env.context.get('default_workcenter_id')
|
||||
return self.env['mrp.workcenter'].browse(workcenter_id).time_stop if workcenter_id else 0.0
|
||||
|
||||
workcenter_id = fields.Many2one('mrp.workcenter', string='Work Center', required=True)
|
||||
product_id = fields.Many2one('product.product', string='Product', required=True)
|
||||
product_uom_id = fields.Many2one('uom.uom', string='Product UoM', related='product_id.uom_id')
|
||||
capacity = fields.Float('Capacity', default=1.0, help="Number of pieces that can be produced in parallel for this product.")
|
||||
time_start = fields.Float('Setup Time (minutes)', help="Time in minutes for the setup.")
|
||||
time_stop = fields.Float('Cleanup Time (minutes)', help="Time in minutes for the cleaning.")
|
||||
time_start = fields.Float('Setup Time (minutes)', default=_default_time_start, help="Time in minutes for the setup.")
|
||||
time_stop = fields.Float('Cleanup Time (minutes)', default=_default_time_stop, help="Time in minutes for the cleaning.")
|
||||
|
||||
_sql_constraints = [
|
||||
('positive_capacity', 'CHECK(capacity > 0)', 'Capacity should be a positive number.'),
|
||||
|
||||
@@ -602,8 +602,8 @@ class TestBoM(TestMrpCommon):
|
||||
self.env['mrp.workcenter.capacity'].create({
|
||||
'product_id': cheese_cake.id,
|
||||
'workcenter_id': workcenter_2.id,
|
||||
'time_start': 2,
|
||||
'time_stop': 1,
|
||||
'time_start': 12,
|
||||
'time_stop': 16,
|
||||
})
|
||||
|
||||
with Form(bom_cheese_cake) as bom:
|
||||
@@ -628,7 +628,7 @@ class TestBoM(TestMrpCommon):
|
||||
|
||||
# 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)
|
||||
#Operation time = 15 min * 60 + time_start + time_stop + capacity_time_start + capacity_time_stop= 928
|
||||
# Operation time = 15 min * 60 + capacity_time_start + capacity_time_stop = 928
|
||||
self.assertEqual(report_values['lines']['operations_time'], 928.0, 'Operation time should be the same for 1 unit or for the batch')
|
||||
# Operation cost is the sum of operation line : (60 * 10)/60 * 10€ + (10 + 15 + 60 * 5)/60 * 20€ + (1 + 2)/60 * 20€ = 209,33€
|
||||
self.assertEqual(float_compare(report_values['lines']['operations_cost'], 209.33, precision_digits=2), 0)
|
||||
|
||||
@@ -3239,3 +3239,41 @@ class TestMrpOrder(TestMrpCommon):
|
||||
mo_form.picking_type_id = warehouse02.manu_type_id
|
||||
mo_form.bom_id = bom_wh02
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be adapted because of the default value')
|
||||
|
||||
def test_workcenter_specific_capacities(self):
|
||||
""" Test that the duraction expected is correctly computed when specific capacities are defined on the workcenter.
|
||||
"""
|
||||
# Required for `workorder_ids` to be visible in the view
|
||||
self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings')
|
||||
self.workcenter_2.update({
|
||||
'time_start': 10,
|
||||
'time_stop': 20,
|
||||
})
|
||||
self.env['mrp.workcenter.capacity'].create({
|
||||
'product_id': self.product_4.id,
|
||||
'workcenter_id': self.workcenter_2.id,
|
||||
'time_start': 5,
|
||||
'time_stop': 10,
|
||||
})
|
||||
|
||||
production_form = Form(self.env['mrp.production'])
|
||||
production_form.product_id = self.product_5
|
||||
production = production_form.save()
|
||||
|
||||
with Form(production) as mo_form:
|
||||
with mo_form.workorder_ids.new() as wo:
|
||||
wo.name = "OP1"
|
||||
wo.workcenter_id = self.workcenter_2
|
||||
|
||||
# Since no duration was given, only duration from the workcenter setup/cleanup time should be added in : 10 + 20 = 30
|
||||
self.assertEqual(production.workorder_ids[0].duration_expected, 30.0, "Workcenter setup time (10) + workcenter cleanup time (20)")
|
||||
|
||||
# Change the product so that it uses a specific capacity of that workcenter
|
||||
with Form(production) as mo_form:
|
||||
mo_form.product_id = self.product_4
|
||||
with mo_form.workorder_ids.new() as wo:
|
||||
wo.name = "OP1"
|
||||
wo.workcenter_id = self.workcenter_2
|
||||
|
||||
# Only duration from the workcenter specific capacity setup/cleanup times since there is one defined for this product.
|
||||
self.assertEqual(production.workorder_ids[0].duration_expected, 15.0, "Capacity setup time (5) + capacity cleanup time (10)")
|
||||
|
||||
@@ -392,8 +392,8 @@
|
||||
<field name="product_id"/>
|
||||
<field name="product_uom_id" groups="uom.group_uom"/>
|
||||
<field name="capacity"/>
|
||||
<field name="time_start" optional="hide"/>
|
||||
<field name="time_stop" optional="hide"/>
|
||||
<field name="time_start" widget="float_time"/>
|
||||
<field name="time_stop" widget="float_time"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
@@ -124,8 +124,8 @@ class TestBomPrice(TestBomPriceCommon):
|
||||
self.env['mrp.workcenter.capacity'].create({
|
||||
'product_id': self.dining_table.id,
|
||||
'workcenter_id': workcenter_1.id,
|
||||
'time_start': 2,
|
||||
'time_stop': 1,
|
||||
'time_start': 17,
|
||||
'time_stop': 16,
|
||||
})
|
||||
|
||||
self.bom_1.write({
|
||||
|
||||
Reference in New Issue
Block a user