Make module work correctly with production_route changes including time and quantity.
This commit is contained in:
parent
08f9a29729
commit
1ede88a424
127
plan.py
127
plan.py
|
@ -1,47 +1,77 @@
|
|||
from decimal import Decimal
|
||||
from trytond.model import ModelSQL, ModelView, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval, Id
|
||||
from trytond.pyson import Eval, Id, If, Bool
|
||||
|
||||
__all__ = ['PlanOperationLine', 'Plan']
|
||||
__metaclass__ = PoolMeta
|
||||
|
||||
_ZERO = Decimal('0.0')
|
||||
|
||||
|
||||
class PlanOperationLine(ModelSQL, ModelView):
|
||||
'Product Cost Plan Operation Line'
|
||||
__name__ = 'product.cost.plan.operation_line'
|
||||
|
||||
plan = fields.Many2One('product.cost.plan', 'Plan', required=True)
|
||||
sequence = fields.Integer('Sequence')
|
||||
work_center = fields.Many2One('production.work_center', 'Work Center')
|
||||
work_center_category = fields.Many2One('production.work_center.category',
|
||||
'Work Center Category')
|
||||
route_operation = fields.Many2One('production.route.operation',
|
||||
'Route Operation', on_change=['route_operation'])
|
||||
operation_type = fields.Many2One('production.operation.type',
|
||||
'Operation Type')
|
||||
time = fields.Float('Quantity', required=True,
|
||||
digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
|
||||
digits=(16, Eval('time_uom_digits', 2)), depends=['time_uom_digits'])
|
||||
time_uom = fields.Many2One('product.uom', 'Uom', required=True, domain=[
|
||||
('category', '=', Id('product', 'uom_cat_time')),
|
||||
], on_change_with=['work_center', 'work_center_category'])
|
||||
time_uom_digits = fields.Function(fields.Integer('Time UOM Digits',
|
||||
on_change_with=['uom']), 'on_change_with_time_uom_digits')
|
||||
cost = fields.Function(fields.Numeric('Cost', on_change_with=['time',
|
||||
'cost_price', 'time_uom', 'work_center',
|
||||
'work_center_category']), 'on_change_with_cost')
|
||||
quantity = fields.Float('Quantity', states={
|
||||
'required': Eval('calculation') == 'standard',
|
||||
'invisible': Eval('calculation') != 'standard',
|
||||
},
|
||||
digits=(16, Eval('quantity_uom_digits', 2)),
|
||||
depends=['quantity_uom_digits', 'calculation'],
|
||||
help='Quantity of the production product processed by the specified '
|
||||
'time.' )
|
||||
quantity_uom = fields.Many2One('product.uom', 'Quantity UOM', states={
|
||||
'required': Eval('calculation') == 'standard',
|
||||
'invisible': Eval('calculation') != 'standard',
|
||||
}, domain=[
|
||||
If(Bool(Eval('quantity_uom_category', 0)),
|
||||
('category', '=', Eval('quantity_uom_category')),
|
||||
(),
|
||||
)], depends=['quantity_uom_category'])
|
||||
calculation = fields.Selection([
|
||||
('standard', 'Standard'),
|
||||
('fixed', 'Fixed'),
|
||||
], 'Calculation', required=True, help='Use Standard to multiply '
|
||||
'the amount of time by the number of units produced. Use Fixed to use '
|
||||
'the indicated time in the production without considering the '
|
||||
'quantities produced. The latter is useful for a setup or cleaning '
|
||||
'operation, for example.')
|
||||
quantity_uom_digits = fields.Function(fields.Integer('Quantity UOM Digits',
|
||||
on_change_with=['quantity_uom']),
|
||||
'on_change_with_quantity_uom_digits')
|
||||
quantity_uom_category = fields.Function(fields.Many2One(
|
||||
'product.uom.category', 'Quantity UOM Category'),
|
||||
'get_quantity_uom_category')
|
||||
cost = fields.Function(fields.Numeric('Cost', digits=(16, 4),
|
||||
on_change_with=['time', 'time_uom', 'calculation', 'quantity',
|
||||
'quantity_uom', 'cost_price', 'work_center',
|
||||
'work_center_category', '_parent_plan.uom']),
|
||||
'on_change_with_cost')
|
||||
|
||||
def on_change_route_operation(self):
|
||||
res = {}
|
||||
route = self.route_operation
|
||||
if not route:
|
||||
return res
|
||||
if route.work_center:
|
||||
res['work_center'] = route.work_center.id
|
||||
if route.work_center_category:
|
||||
res['work_center_category'] = route.work_center_category.id
|
||||
if route.time_uom:
|
||||
res['time_uom'] = route.time_uom.id
|
||||
if route.time:
|
||||
res['time'] = route.time
|
||||
return res
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(PlanOperationLine, cls).__setup__()
|
||||
cls._order.insert(0, ('sequence', 'ASC'))
|
||||
|
||||
@staticmethod
|
||||
def order_sequence(tables):
|
||||
table, _ = tables[None]
|
||||
return [table.sequence == None, table.sequence]
|
||||
|
||||
def on_change_with_time_uom(self):
|
||||
if self.work_center:
|
||||
|
@ -58,11 +88,29 @@ class PlanOperationLine(ModelSQL, ModelView):
|
|||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
wc = self.work_center or self.work_center_category
|
||||
if not wc:
|
||||
return Decimal('0.0')
|
||||
time = Uom.compute_qty(self.time_uom, self.time,
|
||||
wc.uom)
|
||||
return Decimal(str(time)) * wc.cost_price
|
||||
if not wc or not self.time:
|
||||
return _ZERO
|
||||
qty = 1
|
||||
time = Uom.compute_qty(self.time_uom, self.time, wc.uom, round=False)
|
||||
if self.calculation == 'standard':
|
||||
if not self.quantity:
|
||||
return None
|
||||
quantity = Uom.compute_qty(self.quantity_uom, self.quantity,
|
||||
self.plan.uom, round=False)
|
||||
time *= (qty / quantity)
|
||||
cost = Decimal(str(time)) * wc.cost_price
|
||||
cost /= qty
|
||||
digits = self.__class__.cost.digits[1]
|
||||
return cost.quantize(Decimal(str(10 ** -digits)))
|
||||
|
||||
def get_quantity_uom_category(self, name):
|
||||
if self.plan and self.plan.uom:
|
||||
return self.plan.uom.category.id
|
||||
|
||||
def on_change_with_quantity_uom_digits(self, name=None):
|
||||
if self.quantity_uom:
|
||||
return self.quantity_uom.digits
|
||||
return 2
|
||||
|
||||
|
||||
class Plan:
|
||||
|
@ -94,22 +142,17 @@ class Plan:
|
|||
#factor = self.bom.compute_factor(self.product, 1,
|
||||
#self.product.default_uom)
|
||||
for operation in self.route.operations:
|
||||
work_center = None
|
||||
work_center_category = None
|
||||
if operation.work_center:
|
||||
work_center = operation.work_center
|
||||
elif operation.work_center_category:
|
||||
work_center_category = operation.work_center_category
|
||||
|
||||
wc = work_center or work_center_category
|
||||
operations['add'].append({
|
||||
'work_center': work_center and work_center.id or None,
|
||||
'work_center_category': work_center_category and
|
||||
work_center_category.id or None,
|
||||
'route_operation': operation.id,
|
||||
'time_uom': operation.time_uom.id,
|
||||
'time': operation.time * factor,
|
||||
})
|
||||
values = {}
|
||||
for field in ('work_center', 'work_center_category', 'time_uom',
|
||||
'quantity_uom', 'quantity_uom_category', 'operation_type',
|
||||
'quantity_uom_digits', 'time', 'quantity', 'calculation'):
|
||||
value = getattr(operation, field)
|
||||
if value:
|
||||
if isinstance(value, ModelSQL):
|
||||
values[field] = value.id
|
||||
else:
|
||||
values[field] = value
|
||||
operations['add'].append(values)
|
||||
return changes
|
||||
|
||||
def on_change_route(self):
|
||||
|
|
|
@ -76,6 +76,7 @@ Create a route with two operations on diferent work center::
|
|||
>>> clean = OperationType(name='clean')
|
||||
>>> clean.save()
|
||||
>>> hour, = ProductUom.find([('name', '=', 'Hour')])
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> WorkCenter = Model.get('production.work_center')
|
||||
>>> WorkCenterCategory = Model.get('production.work_center.category')
|
||||
>>> category = WorkCenterCategory()
|
||||
|
@ -98,20 +99,27 @@ Create a route with two operations on diferent work center::
|
|||
>>> workcenter2.cost_price = Decimal('50.0')
|
||||
>>> workcenter2.save()
|
||||
>>> route = Route(name='default route')
|
||||
>>> route.uom = unit
|
||||
>>> route_operation = RouteOperation()
|
||||
>>> route.operations.append(route_operation)
|
||||
>>> route_operation.sequence = 1
|
||||
>>> route_operation.operation_type = assembly
|
||||
>>> route_operation.work_center_category = category
|
||||
>>> route_operation.work_center = workcenter1
|
||||
>>> route_operation.quantity = 5
|
||||
>>> route_operation.time = 5
|
||||
>>> route_operation.time_uom = hour
|
||||
>>> route_operation.quantity = 1
|
||||
>>> route_operation.quantity_uom = unit
|
||||
>>> route_operation = RouteOperation()
|
||||
>>> route.operations.append(route_operation)
|
||||
>>> route_operation.sequence = 2
|
||||
>>> route_operation.operation_type = clean
|
||||
>>> route_operation.work_center_category = category
|
||||
>>> route_operation.work_center = workcenter2
|
||||
>>> route_operation.time = 1
|
||||
>>> route_operation.time_uom = hour
|
||||
>>> route_operation.quantity = 1
|
||||
>>> route_operation.quantity_uom = unit
|
||||
>>> route.save()
|
||||
>>> route.reload()
|
||||
>>> len(route.operations) == 2
|
||||
|
@ -120,7 +128,6 @@ Create a route with two operations on diferent work center::
|
|||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Product = Model.get('product.product')
|
||||
>>> product = Product()
|
||||
|
@ -165,7 +172,7 @@ Create Bill of Material::
|
|||
>>> BOM = Model.get('production.bom')
|
||||
>>> BOMInput = Model.get('production.bom.input')
|
||||
>>> BOMOutput = Model.get('production.bom.output')
|
||||
>>> bom = BOM(name='product', route=route)
|
||||
>>> bom = BOM(name='product')
|
||||
>>> input1 = BOMInput()
|
||||
>>> bom.inputs.append(input1)
|
||||
>>> input1.product = component1
|
||||
|
@ -232,7 +239,7 @@ Create a cost plan for product::
|
|||
True
|
||||
>>> plan.operation_cost == Decimal('175.0')
|
||||
True
|
||||
>>> plan.total_cost == plan.product_cost + plan.operation_cost
|
||||
>>> plan.cost_price == plan.product_cost + plan.operation_cost
|
||||
True
|
||||
|
||||
Create a cost plan for 10 units::
|
||||
|
@ -255,5 +262,5 @@ Create a cost plan for 10 units::
|
|||
True
|
||||
>>> plan.operation_cost == Decimal('1750')
|
||||
True
|
||||
>>> plan.total_cost == plan.product_cost + plan.operation_cost
|
||||
>>> plan.cost_price == plan.product_cost + plan.operation_cost
|
||||
True
|
||||
|
|
|
@ -5,14 +5,24 @@
|
|||
<label name="plan"/>
|
||||
<field name="plan"/>
|
||||
<newline/>
|
||||
<label name="route_operation"/>
|
||||
<field name="route_operation"/>
|
||||
<label name="operation_type"/>
|
||||
<field name="operation_type"/>
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
<label name="work_center"/>
|
||||
<field name="work_center"/>
|
||||
<label name="work_center_category"/>
|
||||
<field name="work_center_category"/>
|
||||
<label name="time"/>
|
||||
<field name="time"/>
|
||||
<label name="time_uom"/>
|
||||
<field name="time_uom"/>
|
||||
<group id="time" col="2">
|
||||
<field name="time"/>
|
||||
<field name="time_uom"/>
|
||||
</group>
|
||||
<label name="calculation"/>
|
||||
<field name="calculation"/>
|
||||
<label name="quantity"/>
|
||||
<group id="quantity" col="2">
|
||||
<field name="quantity"/>
|
||||
<field name="quantity_uom"/>
|
||||
</group>
|
||||
</form>
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<tree string="Product Cost Plan Operation" editable="bottom">
|
||||
<tree string="Product Cost Plan Operation" editable="bottom" sequence="sequence">
|
||||
<field name="sequence" tree_invisible="1"/>
|
||||
<field name="plan"/>
|
||||
<field name="route_operation"/>
|
||||
<field name="operation_type"/>
|
||||
<field name="work_center"/>
|
||||
<field name="work_center_category"/>
|
||||
<field name="time"/>
|
||||
<field name="time_uom"/>
|
||||
<field name="calculation"/>
|
||||
<field name="quantity"/>
|
||||
<field name="quantity_uom"/>
|
||||
<field name="cost"/>
|
||||
</tree>
|
||||
|
|
Loading…
Reference in New Issue