diff --git a/addons/mrp/models/mrp_routing.py b/addons/mrp/models/mrp_routing.py
index 6a47dad4ef0..2b37e2cb17a 100644
--- a/addons/mrp/models/mrp_routing.py
+++ b/addons/mrp/models/mrp_routing.py
@@ -87,7 +87,8 @@ class MrpRoutingWorkcenter(models.Model):
cycle_number = 0 # Never 0 unless infinite item['workcenter_id'].capacity
for item in data:
total_duration += item['duration']
- cycle_number += tools.float_round((item['qty_produced'] / item['workcenter_id'].capacity or 1.0), precision_digits=0, rounding_method='UP')
+ capacity = item['workcenter_id']._get_capacity(item.product_id)
+ cycle_number += tools.float_round((item['qty_produced'] / capacity or 1.0), precision_digits=0, rounding_method='UP')
if cycle_number:
operation.time_cycle = total_duration / cycle_number
else:
diff --git a/addons/mrp/models/mrp_workcenter.py b/addons/mrp/models/mrp_workcenter.py
index 3a9bb5e5ded..2556ef42103 100644
--- a/addons/mrp/models/mrp_workcenter.py
+++ b/addons/mrp/models/mrp_workcenter.py
@@ -30,9 +30,9 @@ class MrpWorkcenter(models.Model):
note = fields.Html(
'Description',
help="Description of the Work Center.")
- capacity = fields.Float(
+ default_capacity = fields.Float(
'Capacity', default=1.0,
- help="Number of pieces that can be produced in parallel. In case the work center has a capacity of 5 and you have to produce 10 units on your work order, the usual operation time will be multiplied by 2.")
+ help="Default number of pieces that can be produced in parallel. In case the work center has a capacity of 5 and you have to produce 10 units on your work order, the usual operation time will be multiplied by 2.")
sequence = fields.Integer(
'Sequence', default=1, required=True,
help="Gives the sequence order when displaying a list of work centers.")
@@ -73,6 +73,8 @@ class MrpWorkcenter(models.Model):
help="Alternative workcenters that can be substituted to this one in order to dispatch production"
)
tag_ids = fields.Many2many('mrp.workcenter.tag')
+ capacity_ids = fields.One2many('mrp.workcenter.capacity', 'workcenter_id', string='Product Capacities',
+ help="Specific number of pieces that can be produced in parallel per product.", copy=True)
@api.constrains('alternative_workcenter_ids')
def _check_alternative_workcenter(self):
@@ -172,9 +174,9 @@ class MrpWorkcenter(models.Model):
else:
workcenter.performance = 0.0
- @api.constrains('capacity')
+ @api.constrains('default_capacity')
def _check_capacity(self):
- if any(workcenter.capacity <= 0.0 for workcenter in self):
+ if any(workcenter.default_capacity <= 0.0 for workcenter in self):
raise exceptions.UserError(_('The capacity must be strictly positive.'))
def unblock(self):
@@ -287,6 +289,10 @@ class MrpWorkcenter(models.Model):
}
return res
+ def _get_capacity(self, product):
+ product_capacity = self.capacity_ids.filtered(lambda capacity: capacity.product_id == product)
+ return product_capacity.capacity if product_capacity else self.default_capacity
+
class WorkcenterTag(models.Model):
_name = 'mrp.workcenter.tag'
@@ -405,3 +411,19 @@ class MrpWorkcenterProductivity(models.Model):
def button_block(self):
self.ensure_one()
self.workcenter_id.order_ids.end_all()
+
+
+class MrpWorkCenterCapacity(models.Model):
+ _name = 'mrp.workcenter.capacity'
+ _description = 'Work Center Capacity'
+ _check_company_auto = True
+
+ 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.")
+
+ _sql_constraints = [
+ ('positive_capacity', 'CHECK(capacity > 0)', 'Capacity should be a positive number.'),
+ ('unique_product', 'UNIQUE(workcenter_id, product_id)', 'Product capacity should be unique for each workcenter.'),
+ ]
diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py
index 280bf355395..29fc458c4e0 100644
--- a/addons/mrp/models/mrp_workorder.py
+++ b/addons/mrp/models/mrp_workorder.py
@@ -736,13 +736,15 @@ class MrpWorkorder(models.Model):
duration_expected_working = 0
return self.workcenter_id.time_start + self.workcenter_id.time_stop + duration_expected_working * ratio * 100.0 / self.workcenter_id.time_efficiency
qty_production = self.production_id.product_uom_id._compute_quantity(self.qty_production, self.production_id.product_id.uom_id)
- cycle_number = float_round(qty_production / self.workcenter_id.capacity, precision_digits=0, rounding_method='UP')
+ capacity = self.workcenter_id._get_capacity(self.product_id)
+ cycle_number = float_round(qty_production / capacity, precision_digits=0, rounding_method='UP')
if alternative_workcenter:
# TODO : find a better alternative : the settings of workcenter can change
duration_expected_working = (self.duration_expected - self.workcenter_id.time_start - self.workcenter_id.time_stop) * self.workcenter_id.time_efficiency / (100.0 * cycle_number)
if duration_expected_working < 0:
duration_expected_working = 0
- alternative_wc_cycle_nb = float_round(qty_production / alternative_workcenter.capacity, precision_digits=0, rounding_method='UP')
+ capacity = alternative_workcenter._get_capacity(self.product_id)
+ alternative_wc_cycle_nb = float_round(qty_production / capacity, precision_digits=0, rounding_method='UP')
return alternative_workcenter.time_start + alternative_workcenter.time_stop + alternative_wc_cycle_nb * duration_expected_working * 100.0 / alternative_workcenter.time_efficiency
time_cycle = self.operation_id.time_cycle
return self.workcenter_id.time_start + self.workcenter_id.time_stop + cycle_number * time_cycle * 100.0 / self.workcenter_id.time_efficiency
diff --git a/addons/mrp/populate/mrp.py b/addons/mrp/populate/mrp.py
index 97032e8f640..0472ab701c6 100644
--- a/addons/mrp/populate/mrp.py
+++ b/addons/mrp/populate/mrp.py
@@ -175,7 +175,7 @@ class MrpWorkcenter(models.Model):
('resource_calendar_id', populate.compute(get_resource_calendar_id)),
('active', populate.iterate([True, False], [0.9, 0.1])),
('code', populate.constant("W/{counter}")),
- ('capacity', populate.iterate([0.5, 1.0, 2.0, 5.0], [0.2, 0.4, 0.2, 0.2])),
+ ('default_capacity', populate.iterate([0.5, 1.0, 2.0, 5.0], [0.2, 0.4, 0.2, 0.2])),
('sequence', populate.randint(1, 1000)),
('color', populate.randint(1, 12)),
('costs_hour', populate.randint(5, 25)),
diff --git a/addons/mrp/report/mrp_report_bom_structure.py b/addons/mrp/report/mrp_report_bom_structure.py
index b61988b007d..03a60d75620 100644
--- a/addons/mrp/report/mrp_report_bom_structure.py
+++ b/addons/mrp/report/mrp_report_bom_structure.py
@@ -220,7 +220,8 @@ class ReportBomStructure(models.AbstractModel):
for operation in bom.operation_ids:
if operation._skip_operation_line(product):
continue
- operation_cycle = float_round(qty / operation.workcenter_id.capacity, precision_rounding=1, rounding_method='UP')
+ capacity = operation.workcenter_id._get_capacity(product)
+ operation_cycle = float_round(qty / capacity, precision_rounding=1, rounding_method='UP')
duration_expected = (operation_cycle * operation.time_cycle * 100.0 / operation.workcenter_id.time_efficiency) + (operation.workcenter_id.time_stop + operation.workcenter_id.time_start)
total = ((duration_expected / 60.0) * operation.workcenter_id.costs_hour)
operations.append({
diff --git a/addons/mrp/security/ir.model.access.csv b/addons/mrp/security/ir.model.access.csv
index ae83136a1d6..c50c3f47378 100644
--- a/addons/mrp/security/ir.model.access.csv
+++ b/addons/mrp/security/ir.model.access.csv
@@ -61,3 +61,4 @@ access_mrp_workcenter_tag_manager,access.mrp.workcenter.tag,model_mrp_workcenter
access_mrp_production_split_multi,access.mrp.production.split.multi,model_mrp_production_split_multi,mrp.group_mrp_user,1,1,1,0
access_mrp_production_split,access.mrp.production.split,model_mrp_production_split,mrp.group_mrp_user,1,1,1,0
access_mrp_production_split_line,access.mrp.production.split.line,model_mrp_production_split_line,mrp.group_mrp_user,1,1,1,1
+access_mrp_workcenter_capacity_manager,mrp.workcenter.capacity.manager,model_mrp_workcenter_capacity,mrp.group_mrp_manager,1,1,1,1
diff --git a/addons/mrp/tests/common.py b/addons/mrp/tests/common.py
index a6937e6f8f1..3a837202bca 100644
--- a/addons/mrp/tests/common.py
+++ b/addons/mrp/tests/common.py
@@ -79,21 +79,21 @@ class TestMrpCommon(common2.TestStockCommon):
cls.workcenter_1 = cls.env['mrp.workcenter'].create({
'name': 'Nuclear Workcenter',
- 'capacity': 2,
+ 'default_capacity': 2,
'time_start': 10,
'time_stop': 5,
'time_efficiency': 80,
})
cls.workcenter_2 = cls.env['mrp.workcenter'].create({
'name': 'Simple Workcenter',
- 'capacity': 1,
+ 'default_capacity': 1,
'time_start': 0,
'time_stop': 0,
'time_efficiency': 100,
})
cls.workcenter_3 = cls.env['mrp.workcenter'].create({
'name': 'Double Workcenter',
- 'capacity': 2,
+ 'default_capacity': 2,
'time_start': 0,
'time_stop': 0,
'time_efficiency': 100,
diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py
index df3efbb55ff..c9f9160b658 100644
--- a/addons/mrp/tests/test_order.py
+++ b/addons/mrp/tests/test_order.py
@@ -2673,7 +2673,7 @@ class TestMrpOrder(TestMrpCommon):
"""
workcenter_1 = self.env['mrp.workcenter'].create({
'name': 'wc1',
- 'capacity': 2,
+ 'default_capacity': 2,
'time_start': 1,
'time_stop': 1,
'time_efficiency': 100,
@@ -2681,7 +2681,7 @@ class TestMrpOrder(TestMrpCommon):
workcenter_2 = self.env['mrp.workcenter'].create({
'name': 'wc2',
- 'capacity': 2,
+ 'default_capacity': 2,
'time_start': 10,
'time_stop': 5,
'time_efficiency': 100,
@@ -2725,7 +2725,7 @@ class TestMrpOrder(TestMrpCommon):
mo.button_unplan()
# Update the production capcity
- workcenter_2.capacity = 4
+ workcenter_2.default_capacity = 4
#MO_2
mo_form = Form(self.env['mrp.production'])
diff --git a/addons/mrp/views/mrp_workcenter_views.xml b/addons/mrp/views/mrp_workcenter_views.xml
index 68bcba3a743..21ff75d7e90 100644
--- a/addons/mrp/views/mrp_workcenter_views.xml
+++ b/addons/mrp/views/mrp_workcenter_views.xml
@@ -40,7 +40,7 @@