Make module work correctly with production_route changes including time and quantity.

This commit is contained in:
Albert Cervera i Areny 2014-03-18 16:15:27 +01:00
parent 08f9a29729
commit 1ede88a424
4 changed files with 118 additions and 54 deletions

127
plan.py
View File

@ -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):

View File

@ -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

View File

@ -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>

View File

@ -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>