1 parent
29e3785f8d
commit
0b078acacd
6 files changed
+44
-18
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Add support for disassembly production
|
||||
|
||||
Version 7.6.0 - 2025-04-28
|
||||
--------------------------
|
||||
|
||||
+15
-12
@@ -22,6 +22,8 @@ class BOM(DeactivableMixin, ModelSQL, ModelView):
|
||||
code_readonly = fields.Function(
|
||||
fields.Boolean("Code Readonly"), 'get_code_readonly')
|
||||
inputs = fields.One2Many('production.bom.input', 'bom', "Input Materials")
|
||||
input_products = fields.Many2Many(
|
||||
'production.bom.input', 'bom', 'product', "Input Products")
|
||||
outputs = fields.One2Many(
|
||||
'production.bom.output', 'bom', "Output Materials")
|
||||
output_products = fields.Many2Many('production.bom.output',
|
||||
@@ -71,18 +73,18 @@ class BOM(DeactivableMixin, ModelSQL, ModelView):
|
||||
('code', operator, code_value, *extra),
|
||||
]
|
||||
|
||||
def compute_factor(self, product, quantity, unit):
|
||||
'''
|
||||
Compute factor for an output product
|
||||
'''
|
||||
Uom = Pool().get('product.uom')
|
||||
output_quantity = 0
|
||||
for output in self.outputs:
|
||||
if output.product == product:
|
||||
output_quantity += Uom.compute_qty(
|
||||
output.unit, output.quantity, unit, round=False)
|
||||
if output_quantity:
|
||||
return quantity / output_quantity
|
||||
def compute_factor(self, product, quantity, unit, type='outputs'):
|
||||
"Compute factor for a product from the type"
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
assert type in {'inputs', 'outputs'}, f"Invalid {type}"
|
||||
total = 0
|
||||
for line in getattr(self, type):
|
||||
if line.product == product:
|
||||
total += Uom.compute_qty(
|
||||
line.unit, line.quantity, unit, round=False)
|
||||
if total:
|
||||
return quantity / total
|
||||
else:
|
||||
return 0
|
||||
|
||||
@@ -108,6 +110,7 @@ class BOM(DeactivableMixin, ModelSQL, ModelView):
|
||||
else:
|
||||
default = default.copy()
|
||||
default.setdefault('code', None)
|
||||
default.setdefault('input_products', None)
|
||||
default.setdefault('output_products', None)
|
||||
return super().copy(records, default=default)
|
||||
|
||||
|
||||
@@ -70,9 +70,18 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
'readonly': (~Eval('state').in_(['request', 'draft'])
|
||||
| Eval('inputs', [-1]) | Eval('outputs', [-1])),
|
||||
})
|
||||
type = fields.Selection([
|
||||
('assembly', "Assembly"),
|
||||
('disassembly', "Disassembly"),
|
||||
], "Type", required=True,
|
||||
states={
|
||||
'readonly': ~Eval('state').in_(['request', 'draft']),
|
||||
})
|
||||
product = fields.Many2One('product.product', 'Product',
|
||||
domain=[
|
||||
('producible', '=', True),
|
||||
If(Eval('type') == 'assembly',
|
||||
('producible', '=', True),
|
||||
()),
|
||||
],
|
||||
states={
|
||||
'readonly': ~Eval('state').in_(['request', 'draft']),
|
||||
@@ -83,7 +92,10 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
depends={'company'})
|
||||
bom = fields.Many2One('production.bom', 'BOM',
|
||||
domain=[
|
||||
('output_products', '=', Eval('product', 0)),
|
||||
If(Eval('type') == 'disassembly',
|
||||
('input_products', '=', Eval('product', -1)),
|
||||
('output_products', '=', Eval('product', -1)),
|
||||
),
|
||||
],
|
||||
states={
|
||||
'readonly': (~Eval('state').in_(['request', 'draft'])
|
||||
@@ -305,6 +317,10 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
warehouse = Location(warehouse_id)
|
||||
return warehouse.production_location.id
|
||||
|
||||
@classmethod
|
||||
def default_type(cls):
|
||||
return 'assembly'
|
||||
|
||||
@staticmethod
|
||||
def default_company():
|
||||
return Transaction().context.get('company')
|
||||
@@ -312,7 +328,7 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
@fields.depends('product', 'bom')
|
||||
def compute_lead_time(self, pattern=None):
|
||||
pattern = pattern.copy() if pattern is not None else {}
|
||||
if self.product:
|
||||
if self.product and self.product.producible:
|
||||
pattern.setdefault('bom', self.bom.id if self.bom else None)
|
||||
for line in self.product.production_lead_times:
|
||||
if line.match(pattern):
|
||||
@@ -384,14 +400,15 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
return move
|
||||
|
||||
@fields.depends(
|
||||
'bom', 'product', 'unit', 'quantity', 'inputs', 'outputs',
|
||||
'type', 'bom', 'product', 'unit', 'quantity', 'inputs', 'outputs',
|
||||
methods=['_move'])
|
||||
def explode_bom(self):
|
||||
if not (self.bom and self.product and self.unit):
|
||||
return
|
||||
|
||||
factor = self.bom.compute_factor(
|
||||
self.product, self.quantity or 0, self.unit)
|
||||
self.product, self.quantity or 0, self.unit,
|
||||
type='inputs' if self.type == 'disassembly' else 'outputs')
|
||||
inputs = []
|
||||
for input_ in self.bom.inputs:
|
||||
quantity = input_.compute_quantity(factor)
|
||||
|
||||
@@ -20,7 +20,7 @@ class ProductionTestCase(CompanyTestMixin, ModuleTestCase):
|
||||
LeadTime = pool.get('production.lead_time')
|
||||
|
||||
date = datetime.date(2016, 11, 26)
|
||||
product = Product()
|
||||
product = Product(producible=True)
|
||||
product.production_lead_times = []
|
||||
production = Production()
|
||||
production.planned_date = date
|
||||
|
||||
@@ -22,6 +22,10 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<label name="warehouse"/>
|
||||
<field name="warehouse"/>
|
||||
|
||||
<label name="type"/>
|
||||
<field name="type"/>
|
||||
<newline/>
|
||||
|
||||
<label name="product"/>
|
||||
<field name="product"/>
|
||||
<label name="bom"/>
|
||||
|
||||
@@ -5,6 +5,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="company" expand="1" optional="1"/>
|
||||
<field name="number" expand="1"/>
|
||||
<field name="reference" expand="1" optional="1"/>
|
||||
<field name="type" optional="1"/>
|
||||
<field name="product" expand="2"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<field name="planned_start_date" optional="0"/>
|
||||
|
||||
Reference in new issue
Block a user