diff --git a/__init__.py b/__init__.py index 3f890db..0f527b6 100644 --- a/__init__.py +++ b/__init__.py @@ -4,13 +4,10 @@ from trytond.pool import Pool from .operation import * + def register(): Pool.register( - WorkCenterCategory, - WorkCenter, - Route, - RouteOperation, Operation, - OperationLine, + OperationTracking, Production, module='production_operation', type_='model') diff --git a/locale/ca_ES.po b/locale/ca_ES.po new file mode 100644 index 0000000..1e49fc8 --- /dev/null +++ b/locale/ca_ES.po @@ -0,0 +1,438 @@ +# +msgid "" +msgstr "Content-Type: text/plain; charset=utf-8\n" + +msgctxt "error:production.operation:" +msgid "You can not create an operation for Production \"%s\"." +msgstr "No podeu crear una operació per la producció \"%s\"." + +msgctxt "error:production:" +msgid "" +"Production \"%(production)s\" can not be done because their operation " +"\"%(operation)s\" is not done." +msgstr "" +"No es pot realitzar la producció \"%(production)s\" perquè la seva operació " +"\"%(operation)s\" no està realitzada." + +msgctxt "field:production,operations:" +msgid "Operations" +msgstr "Operacions" + +msgctxt "field:production,route:" +msgid "Route" +msgstr "Ruta" + +msgctxt "field:production.operation,cost:" +msgid "Cost" +msgstr "Cost" + +msgctxt "field:production.operation,create_date:" +msgid "Create Date" +msgstr "Data de creació" + +msgctxt "field:production.operation,create_uid:" +msgid "Create User" +msgstr "Usuari de creació" + +msgctxt "field:production.operation,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.operation,lines:" +msgid "Lines" +msgstr "Línies" + +msgctxt "field:production.operation,name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.operation,operation_type:" +msgid "Operation Type" +msgstr "Tipus d'operació" + +msgctxt "field:production.operation,production:" +msgid "Production" +msgstr "Producció" + +msgctxt "field:production.operation,rec_name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.operation,route_operation:" +msgid "Route Operation" +msgstr "Ruta" + +msgctxt "field:production.operation,sequence:" +msgid "Sequence" +msgstr "Seqüència" + +msgctxt "field:production.operation,state:" +msgid "State" +msgstr "Estat" + +msgctxt "field:production.operation,work_center:" +msgid "Work Center" +msgstr "Centre de treball" + +msgctxt "field:production.operation,write_date:" +msgid "Write Date" +msgstr "Data modificació" + +msgctxt "field:production.operation,write_uid:" +msgid "Write User" +msgstr "Usuari modificació" + +msgctxt "field:production.operation.tracking,cost:" +msgid "Cost" +msgstr "Cost" + +msgctxt "field:production.operation.tracking,create_date:" +msgid "Create Date" +msgstr "Data de creació" + +msgctxt "field:production.operation.tracking,create_uid:" +msgid "Create User" +msgstr "Usuari de creació" + +msgctxt "field:production.operation.tracking,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.operation.tracking,operation:" +msgid "Operation" +msgstr "Operació" + +msgctxt "field:production.operation.tracking,quantity:" +msgid "Quantity" +msgstr "Quantitat" + +msgctxt "field:production.operation.tracking,rec_name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.operation.tracking,uom:" +msgid "Uom" +msgstr "UdM" + +msgctxt "field:production.operation.tracking,work_center:" +msgid "Work Center" +msgstr "Centre de treball" + +msgctxt "field:production.operation.tracking,write_date:" +msgid "Write Date" +msgstr "Data modificació" + +msgctxt "field:production.operation.tracking,write_uid:" +msgid "Write User" +msgstr "Usuari modificació" + +msgctxt "field:production.operation.type,create_date:" +msgid "Create Date" +msgstr "Data de creació" + +msgctxt "field:production.operation.type,create_uid:" +msgid "Create User" +msgstr "Usuari de creació" + +msgctxt "field:production.operation.type,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.operation.type,name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.operation.type,rec_name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.operation.type,write_date:" +msgid "Write Date" +msgstr "Data modificació" + +msgctxt "field:production.operation.type,write_uid:" +msgid "Write User" +msgstr "Usuari modificació" + +msgctxt "field:production.route,create_date:" +msgid "Create Date" +msgstr "Data de creació" + +msgctxt "field:production.route,create_uid:" +msgid "Create User" +msgstr "Usuari de creació" + +msgctxt "field:production.route,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.route,name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.route,operations:" +msgid "Operations" +msgstr "Operacions" + +msgctxt "field:production.route,rec_name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.route,write_date:" +msgid "Write Date" +msgstr "Data modificació" + +msgctxt "field:production.route,write_uid:" +msgid "Write User" +msgstr "Usuari modificació" + +msgctxt "field:production.route.operation,create_date:" +msgid "Create Date" +msgstr "Data de creació" + +msgctxt "field:production.route.operation,create_uid:" +msgid "Create User" +msgstr "Usuari de creació" + +msgctxt "field:production.route.operation,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.route.operation,name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.route.operation,operation_type:" +msgid "Operation Type" +msgstr "Tipus d'operació" + +msgctxt "field:production.route.operation,rec_name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.route.operation,route:" +msgid "Route" +msgstr "Ruta" + +msgctxt "field:production.route.operation,sequence:" +msgid "Sequence" +msgstr "Seqüència" + +msgctxt "field:production.route.operation,work_center:" +msgid "Work Center" +msgstr "Centre de treball" + +msgctxt "field:production.route.operation,write_date:" +msgid "Write Date" +msgstr "Data modificació" + +msgctxt "field:production.route.operation,write_uid:" +msgid "Write User" +msgstr "Usuari modificació" + +msgctxt "field:production.work_center,category:" +msgid "Category" +msgstr "Categoria" + +msgctxt "field:production.work_center,cost_price:" +msgid "Cost Price" +msgstr "Preu de cost" + +msgctxt "field:production.work_center,create_date:" +msgid "Create Date" +msgstr "Data de creació" + +msgctxt "field:production.work_center,create_uid:" +msgid "Create User" +msgstr "Usuari de creació" + +msgctxt "field:production.work_center,employee:" +msgid "Employee" +msgstr "Empleat" + +msgctxt "field:production.work_center,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.work_center,name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.work_center,rec_name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.work_center,type:" +msgid "Type" +msgstr "Tipus" + +msgctxt "field:production.work_center,uom:" +msgid "Uom" +msgstr "UdM" + +msgctxt "field:production.work_center,write_date:" +msgid "Write Date" +msgstr "Data modificació" + +msgctxt "field:production.work_center,write_uid:" +msgid "Write User" +msgstr "Usuari modificació" + +msgctxt "field:production.work_center.category,create_date:" +msgid "Create Date" +msgstr "Data de creació" + +msgctxt "field:production.work_center.category,create_uid:" +msgid "Create User" +msgstr "Usuari de creació" + +msgctxt "field:production.work_center.category,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.work_center.category,name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.work_center.category,rec_name:" +msgid "Name" +msgstr "Nom" + +msgctxt "field:production.work_center.category,write_date:" +msgid "Write Date" +msgstr "Data modificació" + +msgctxt "field:production.work_center.category,write_uid:" +msgid "Write User" +msgstr "Usuari modificació" + +msgctxt "model:ir.action,name:act_production_operation" +msgid "Operation" +msgstr "Operació" + +msgctxt "model:ir.action,name:act_production_operation_tracking" +msgid "Operation Tracking" +msgstr "Registre d'operació" + +msgctxt "model:ir.action,name:act_production_operation_type" +msgid "Operation Type" +msgstr "Tipus d'operació" + +msgctxt "" +"model:ir.action.act_window.domain,name:act_production_operation_domain_all" +msgid "All" +msgstr "Tots" + +msgctxt "" +"model:ir.action.act_window.domain,name:act_production_operation_domain_running" +msgid "Running" +msgstr "En curs" + +msgctxt "" +"model:ir.action.act_window.domain,name:act_production_operation_domain_waiting" +msgid "Waiting" +msgstr "Esperant" + +msgctxt "model:ir.ui.menu,name:menu_production_operation" +msgid "Operations" +msgstr "Operacions" + +msgctxt "model:ir.ui.menu,name:menu_production_operation_type" +msgid "Operation Types" +msgstr "Tipus d'operacions" + +msgctxt "model:production.operation,name:" +msgid "Operation" +msgstr "Operació" + +msgctxt "model:production.operation.tracking,name:" +msgid "operation.tracking" +msgstr "Registre d'operació" + +msgctxt "model:production.operation.type,name:" +msgid "Operation Type" +msgstr "Tipus d'operació" + +msgctxt "model:production.route,name:" +msgid "Production Route" +msgstr "Ruta de producció" + +msgctxt "model:production.route.operation,name:" +msgid "Route Operation" +msgstr "Ruta" + +msgctxt "model:production.work_center,name:" +msgid "Work Center" +msgstr "Centre de treball" + +msgctxt "model:production.work_center.category,name:" +msgid "Work Center Category" +msgstr "Categoria del centre de treball" + +msgctxt "selection:production.operation,state:" +msgid "Done" +msgstr "Realitzat" + +msgctxt "selection:production.operation,state:" +msgid "Planned" +msgstr "Planificat" + +msgctxt "selection:production.operation,state:" +msgid "Running" +msgstr "En curs" + +msgctxt "selection:production.operation,state:" +msgid "Waiting" +msgstr "Esperant" + +msgctxt "selection:production.work_center,type:" +msgid "Employee" +msgstr "Empleat" + +msgctxt "selection:production.work_center,type:" +msgid "Machine" +msgstr "Màquina" + +msgctxt "view:production.operation.tracking:" +msgid "Operation Tracking" +msgstr "Registre d'operació" + +msgctxt "view:production.operation.type:" +msgid "Operation Type" +msgstr "Tipus d'operació" + +msgctxt "view:production.operation:" +msgid "Done" +msgstr "Realitzat" + +msgctxt "view:production.operation:" +msgid "Operation" +msgstr "Operació" + +msgctxt "view:production.operation:" +msgid "Run" +msgstr "Executa" + +msgctxt "view:production.operation:" +msgid "Wait" +msgstr "En espera" + +msgctxt "view:production.route.operation:" +msgid "Route Operation" +msgstr "Ruta" + +msgctxt "view:production.route:" +msgid "Route" +msgstr "Ruta" + +msgctxt "view:production.work_center.category:" +msgid "Work Center Category" +msgstr "Categoria del centre de treball" + +msgctxt "view:production.work_center:" +msgid "Work Center" +msgstr "Centre de treball" + +msgctxt "view:production:" +msgid "Operations" +msgstr "Operacions" diff --git a/locale/es_ES.po b/locale/es_ES.po new file mode 100644 index 0000000..ab9d680 --- /dev/null +++ b/locale/es_ES.po @@ -0,0 +1,438 @@ +# +msgid "" +msgstr "Content-Type: text/plain; charset=utf-8\n" + +msgctxt "error:production.operation:" +msgid "You can not create an operation for Production \"%s\"." +msgstr "No puede craer una operación para la producción \"%s\"." + +msgctxt "error:production:" +msgid "" +"Production \"%(production)s\" can not be done because their operation " +"\"%(operation)s\" is not done." +msgstr "" +"No se puede realizar la producción \"%(production)s\" porqué la operación " +"\"%(operation)s\" todavía no esta realizada." + +msgctxt "field:production,operations:" +msgid "Operations" +msgstr "Operaciones" + +msgctxt "field:production,route:" +msgid "Route" +msgstr "Ruta" + +msgctxt "field:production.operation,cost:" +msgid "Cost" +msgstr "Coste" + +msgctxt "field:production.operation,create_date:" +msgid "Create Date" +msgstr "Fecha de creación" + +msgctxt "field:production.operation,create_uid:" +msgid "Create User" +msgstr "Usuario de creación" + +msgctxt "field:production.operation,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.operation,lines:" +msgid "Lines" +msgstr "Líneas" + +msgctxt "field:production.operation,name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.operation,operation_type:" +msgid "Operation Type" +msgstr "Tipo de operación" + +msgctxt "field:production.operation,production:" +msgid "Production" +msgstr "Producción" + +msgctxt "field:production.operation,rec_name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.operation,route_operation:" +msgid "Route Operation" +msgstr "Ruta de operación" + +msgctxt "field:production.operation,sequence:" +msgid "Sequence" +msgstr "Secuencia" + +msgctxt "field:production.operation,state:" +msgid "State" +msgstr "Estado" + +msgctxt "field:production.operation,work_center:" +msgid "Work Center" +msgstr "Centro de trabajo" + +msgctxt "field:production.operation,write_date:" +msgid "Write Date" +msgstr "Fecha modificación" + +msgctxt "field:production.operation,write_uid:" +msgid "Write User" +msgstr "Usuario modificación" + +msgctxt "field:production.operation.tracking,cost:" +msgid "Cost" +msgstr "Coste" + +msgctxt "field:production.operation.tracking,create_date:" +msgid "Create Date" +msgstr "Fecha de creación" + +msgctxt "field:production.operation.tracking,create_uid:" +msgid "Create User" +msgstr "Usuario de creación" + +msgctxt "field:production.operation.tracking,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.operation.tracking,operation:" +msgid "Operation" +msgstr "Operación" + +msgctxt "field:production.operation.tracking,quantity:" +msgid "Quantity" +msgstr "Cantidad" + +msgctxt "field:production.operation.tracking,rec_name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.operation.tracking,uom:" +msgid "Uom" +msgstr "UdM" + +msgctxt "field:production.operation.tracking,work_center:" +msgid "Work Center" +msgstr "Centro de trabajo" + +msgctxt "field:production.operation.tracking,write_date:" +msgid "Write Date" +msgstr "Fecha modificación" + +msgctxt "field:production.operation.tracking,write_uid:" +msgid "Write User" +msgstr "Usuario modificación" + +msgctxt "field:production.operation.type,create_date:" +msgid "Create Date" +msgstr "Fecha de creación" + +msgctxt "field:production.operation.type,create_uid:" +msgid "Create User" +msgstr "Usuario de creación" + +msgctxt "field:production.operation.type,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.operation.type,name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.operation.type,rec_name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.operation.type,write_date:" +msgid "Write Date" +msgstr "Fecha modificación" + +msgctxt "field:production.operation.type,write_uid:" +msgid "Write User" +msgstr "Usuario modificación" + +msgctxt "field:production.route,create_date:" +msgid "Create Date" +msgstr "Fecha de creación" + +msgctxt "field:production.route,create_uid:" +msgid "Create User" +msgstr "Usuario de creación" + +msgctxt "field:production.route,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.route,name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.route,operations:" +msgid "Operations" +msgstr "Operaciones" + +msgctxt "field:production.route,rec_name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.route,write_date:" +msgid "Write Date" +msgstr "Fecha modificación" + +msgctxt "field:production.route,write_uid:" +msgid "Write User" +msgstr "Usuario modificación" + +msgctxt "field:production.route.operation,create_date:" +msgid "Create Date" +msgstr "Fecha de creación" + +msgctxt "field:production.route.operation,create_uid:" +msgid "Create User" +msgstr "Usuario de creación" + +msgctxt "field:production.route.operation,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.route.operation,name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.route.operation,operation_type:" +msgid "Operation Type" +msgstr "Tipo de operación" + +msgctxt "field:production.route.operation,rec_name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.route.operation,route:" +msgid "Route" +msgstr "Ruta" + +msgctxt "field:production.route.operation,sequence:" +msgid "Sequence" +msgstr "Secuencia" + +msgctxt "field:production.route.operation,work_center:" +msgid "Work Center" +msgstr "Centro de trabajo" + +msgctxt "field:production.route.operation,write_date:" +msgid "Write Date" +msgstr "Fecha modificación" + +msgctxt "field:production.route.operation,write_uid:" +msgid "Write User" +msgstr "Usuario modificación" + +msgctxt "field:production.work_center,category:" +msgid "Category" +msgstr "Categoría" + +msgctxt "field:production.work_center,cost_price:" +msgid "Cost Price" +msgstr "Precio de coste" + +msgctxt "field:production.work_center,create_date:" +msgid "Create Date" +msgstr "Fecha de creación" + +msgctxt "field:production.work_center,create_uid:" +msgid "Create User" +msgstr "Usuario de creación" + +msgctxt "field:production.work_center,employee:" +msgid "Employee" +msgstr "Empleado" + +msgctxt "field:production.work_center,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.work_center,name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.work_center,rec_name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.work_center,type:" +msgid "Type" +msgstr "Tipo" + +msgctxt "field:production.work_center,uom:" +msgid "Uom" +msgstr "UdM" + +msgctxt "field:production.work_center,write_date:" +msgid "Write Date" +msgstr "Fecha modificación" + +msgctxt "field:production.work_center,write_uid:" +msgid "Write User" +msgstr "Usuario modificación" + +msgctxt "field:production.work_center.category,create_date:" +msgid "Create Date" +msgstr "Fecha de creación" + +msgctxt "field:production.work_center.category,create_uid:" +msgid "Create User" +msgstr "Usuario de creación" + +msgctxt "field:production.work_center.category,id:" +msgid "ID" +msgstr "Identificador" + +msgctxt "field:production.work_center.category,name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.work_center.category,rec_name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "field:production.work_center.category,write_date:" +msgid "Write Date" +msgstr "Fecha modificación" + +msgctxt "field:production.work_center.category,write_uid:" +msgid "Write User" +msgstr "Usuario modificación" + +msgctxt "model:ir.action,name:act_production_operation" +msgid "Operation" +msgstr "Operación" + +msgctxt "model:ir.action,name:act_production_operation_tracking" +msgid "Operation Tracking" +msgstr "Registro de operación" + +msgctxt "model:ir.action,name:act_production_operation_type" +msgid "Operation Type" +msgstr "Tipo de operación" + +msgctxt "" +"model:ir.action.act_window.domain,name:act_production_operation_domain_all" +msgid "All" +msgstr "Todos" + +msgctxt "" +"model:ir.action.act_window.domain,name:act_production_operation_domain_running" +msgid "Running" +msgstr "En ejecución" + +msgctxt "" +"model:ir.action.act_window.domain,name:act_production_operation_domain_waiting" +msgid "Waiting" +msgstr "En espera" + +msgctxt "model:ir.ui.menu,name:menu_production_operation" +msgid "Operations" +msgstr "Operaciones" + +msgctxt "model:ir.ui.menu,name:menu_production_operation_type" +msgid "Operation Types" +msgstr "Tipos de operación" + +msgctxt "model:production.operation,name:" +msgid "Operation" +msgstr "Operación" + +msgctxt "model:production.operation.tracking,name:" +msgid "operation.tracking" +msgstr "Registro de operación" + +msgctxt "model:production.operation.type,name:" +msgid "Operation Type" +msgstr "Tipo de operación" + +msgctxt "model:production.route,name:" +msgid "Production Route" +msgstr "Ruta de producción" + +msgctxt "model:production.route.operation,name:" +msgid "Route Operation" +msgstr "Ruta de operación" + +msgctxt "model:production.work_center,name:" +msgid "Work Center" +msgstr "Centro de trabajo" + +msgctxt "model:production.work_center.category,name:" +msgid "Work Center Category" +msgstr "Centro de trabajo" + +msgctxt "selection:production.operation,state:" +msgid "Done" +msgstr "Realizado" + +msgctxt "selection:production.operation,state:" +msgid "Planned" +msgstr "Planificado" + +msgctxt "selection:production.operation,state:" +msgid "Running" +msgstr "En ejecución" + +msgctxt "selection:production.operation,state:" +msgid "Waiting" +msgstr "En espera" + +msgctxt "selection:production.work_center,type:" +msgid "Employee" +msgstr "Empleado" + +msgctxt "selection:production.work_center,type:" +msgid "Machine" +msgstr "Maquina" + +msgctxt "view:production.operation.tracking:" +msgid "Operation Tracking" +msgstr "Registro de operación" + +msgctxt "view:production.operation.type:" +msgid "Operation Type" +msgstr "Tipo de operación" + +msgctxt "view:production.operation:" +msgid "Done" +msgstr "Realizado" + +msgctxt "view:production.operation:" +msgid "Operation" +msgstr "Operación" + +msgctxt "view:production.operation:" +msgid "Run" +msgstr "Ejecutar" + +msgctxt "view:production.operation:" +msgid "Wait" +msgstr "Esperando" + +msgctxt "view:production.route.operation:" +msgid "Route Operation" +msgstr "Ruta de operación" + +msgctxt "view:production.route:" +msgid "Route" +msgstr "Ruta" + +msgctxt "view:production.work_center.category:" +msgid "Work Center Category" +msgstr "Centro de trabajo" + +msgctxt "view:production.work_center:" +msgid "Work Center" +msgstr "Centro de trabajo" + +msgctxt "view:production:" +msgid "Operations" +msgstr "Operaciones" diff --git a/operation.py b/operation.py index 4af0a19..5686257 100644 --- a/operation.py +++ b/operation.py @@ -1,94 +1,111 @@ from decimal import Decimal from trytond.model import fields, ModelSQL, ModelView, Workflow -from trytond.pool import PoolMeta -from trytond.pyson import Eval +from trytond.pool import Pool, PoolMeta +from trytond.pyson import Eval, If, Bool +from trytond.transaction import Transaction -__all__ = ['WorkCenterCategory', 'WorkCenter', 'Route', 'RouteOperation', - 'Operation', 'OperationLine', 'Production'] +__all__ = ['Operation', 'OperationTracking', 'Production'] __metaclass__ = PoolMeta - -class WorkCenterCategory(ModelSQL, ModelView): - 'Work Center Category' - __name__ = 'production.work_center.category' - name = fields.Char('Name', required=True) - cost_price = fields.Numeric('Cost Price', digits=(16, 4), required=True) - uom = fields.Many2One('product.uom', 'Uom', required=True) +STATES = { + 'readonly': Eval('state').in_(['running', 'done']) + } +DEPENDS = ['state'] -class WorkCenter(ModelSQL, ModelView): - 'Work Center' - __name__ = 'production.work_center' - name = fields.Char('Name', required=True) - category = fields.Many2One('production.work_center.category', 'Category') - type = fields.Selection([ - ('machine', 'Machine'), - ('employee', 'Employee'), - ], 'Type') - employee = fields.Many2One('company.employee', 'Employee', states={ - 'invisible': Eval('type') != 'employee', - 'required': Eval('type') == 'employee', - }, depends=['type']) - cost_price = fields.Numeric('Cost Price', digits=(16, 4), required=True) - uom = fields.Many2One('product.uom', 'Uom', required=True) - - -class Route(ModelSQL, ModelView): - 'Production Route' - __name__ = 'production.route' - name = fields.Char('Name', required=True) - operations = fields.One2Many('production.route.operation', 'bom', - 'Operations') - -class OperationType(ModelSQL, ModelView): - 'Operation Type' - __name__ = 'production.operation.type' - - name = fields.Char('Name', required=True) - - - -class RouteOperation(ModelSQL, ModelView): - 'Route Operation' - __name__ = 'production.route.operation' - route = fields.Many2One('production.route', 'Route', required=True) - name = fields.Char('Name', required=True) - sequence = fields.Integer('Sequence') - work_center = fields.Many2One('production.work_center', 'Work Center') - work_center_category = fields.Many2One('production.work_center', 'Work Center') - operation_type = fields.Many2One('production.operation.type', 'Operation Type') - - @classmethod - def __setup__(cls): - super(RouteOperation, cls).__setup__() - cls._order.insert(0, ('sequence', 'ASC')) - - @staticmethod - def order_sequence(tables): - table, _ = tables[None] - return [table.sequence == None, table.sequence] - - -class Operation(ModelSQL, ModelView): +class Operation(Workflow, ModelSQL, ModelView): 'Operation' __name__ = 'production.operation' - production = fields.Many2One('production', 'Production', required=True) - name = fields.Char('Name', required=True) - sequence = fields.Integer('Sequence') - work_center = fields.Many2One('production.work_center', 'Work Center') - route_operation = fields.Many2One('production.route.operation', - 'Route Operation') - lines = fields.One2Many('production.operation.line', 'operation', 'Lines') - cost = fields.Function(fields.Numeric('Cost'), 'get_cost') - operation_type = fields.Many2One('production.operation.type', 'Operation Type') - # TODO: Add workflow + production = fields.Many2One('production', 'Production', required=True, + states=STATES, depends=DEPENDS) + sequence = fields.Integer('Sequence', states=STATES, depends=DEPENDS) + work_center = fields.Many2One('production.work_center', 'Work Center', + states=STATES, depends=DEPENDS) + route_operation = fields.Many2One('production.route.operation', + 'Route Operation', states=STATES, depends=DEPENDS) + lines = fields.One2Many('production.operation.tracking', 'operation', + 'Lines', states=STATES, depends=DEPENDS, context={ + 'work_center': Eval('work_center'), + }) + cost = fields.Function(fields.Numeric('Cost'), 'get_cost') + operation_type = fields.Many2One('production.operation.type', + 'Operation Type', states=STATES, depends=DEPENDS, required=True) + uom_category = fields.Function(fields.Many2One( + 'product.uom.category', 'Uom Category', on_change_with=[ + 'work_center']), + 'on_change_with_uom_category') + state = fields.Selection([ + ('planned', 'Planned'), + ('waiting', 'Waiting'), + ('running', 'Running'), + ('done', 'Done'), + ], 'State', readonly=True) @classmethod def __setup__(cls): super(Operation, cls).__setup__() cls._order.insert(0, ('sequence', 'ASC')) + cls._invalid_production_states_on_create = ['running', 'done'] + cls._error_messages.update({ + 'invalid_production_state': ('You can not create an operation' + ' for Production "%s".'), + }) + cls._transitions |= set(( + ('planned', 'waiting'), + ('waiting', 'running'), + ('running', 'waiting'), + ('running', 'done'), + )) + cls._buttons.update({ + 'wait': { + 'invisible': Eval('state') != 'running', + 'icon': 'tryton-go-previous', + }, + 'run': { + 'invisible': Eval('state') != 'waiting', + 'icon': 'tryton-go-next', + }, + 'done': { + 'invisible': Eval('state') != 'running', + }, + }) + + @staticmethod + def default_state(): + return 'planned' + + def get_rec_name(self, name): + if self.operation_type: + return self.operation_type.rec_name + return super(Operation, self).get_rec_name() + + @classmethod + def search_rec_name(cls, name, clause): + return [('operation_type.name',) + tuple(clause[1:])] + + def on_change_with_uom_category(self, name=None): + if self.work_center: + return self.work_center.uom.category.id + + @classmethod + def create(cls, vlist): + pool = Pool() + Production = pool.get('production') + productions = [] + for value in vlist: + productions.append(value['production']) + + invalid_productions = Production.search([ + ('id', 'in', productions), + ('state', 'in', cls._invalid_production_states_on_create), + ], limit=1) + if invalid_productions: + production, = invalid_productions + cls.raise_user_error('invalid_production_state', + production.rec_name) + super(Operation, cls).create(vlist) @staticmethod def order_sequence(tables): @@ -96,40 +113,109 @@ class Operation(ModelSQL, ModelView): return [table.sequence == None, table.sequence] def get_cost(self, name): - # TODO - return Decimal('0.0') + cost = Decimal('0.0') + for line in self.lines: + cost += line.cost + return cost + + @classmethod + @ModelView.button + @Workflow.transition('waiting') + def wait(cls, operations): + pass + + @classmethod + @ModelView.button + @Workflow.transition('running') + def run(cls, operations): + pass + + @classmethod + def done(cls, operations): + pool = Pool() + Production = pool.get('production') + + productions = set([o.production for o in operations]) + cls.write(operations, {'state': 'done'}) + + with Transaction().set_context(from_done_operation=True): + Production.done(productions) -class OperationLine(ModelSQL, ModelView): - 'Operation Line' - __name__ = 'production.operation.line' +class OperationTracking(ModelSQL, ModelView): + 'operation.tracking' + __name__ = 'production.operation.tracking' + operation = fields.Many2One('production.operation', 'Operation', required=True) - work_center = fields.Function(fields.Many2One('production.work_center', - 'Work Center', on_change_with=['operation']), - 'on_change_with_work_center') - # Maybe start and end in a new module? - start = fields.DateTime('Start') - end = fields.DateTime('End') - quantity = fields.Float('Quantity') - uom = fields.Many2One('product.uom', 'Uom', required=True) + uom = fields.Many2One('product.uom', 'Uom', required=True, + on_change_with=['operation'], domain=[ + If(Bool(Eval('_parent_operation', 0)), + ('category', '=', Eval('_parent_operation', {}).get( + 'uom_category', 0)), (), + )]) + unit_digits = fields.Function(fields.Integer('Unit Digits', + on_change_with=['uom']), 'on_change_with_unit_digits') + quantity = fields.Float('Quantity', required=True, + digits=(16, Eval('unit_digits', 2)), depends=['unit_digits']) cost = fields.Function(fields.Numeric('Cost'), 'get_cost') - def get_cost(self, name): - # TODO - return Decimal('0.0') + @staticmethod + def default_quantity(): + return 0.0 - def on_change_with_work_center(self, name=None): - return (self.operation.work_center.id - if self.operation and self.operation.work_center else None) + @staticmethod + def default_uom(): + pool = Pool() + WorkCenter = pool.get('production.work_center') + + context = Transaction().context + if 'work_center' in context: + work_center = WorkCenter(context['work_center']) + return work_center.uom.id + + def get_cost(self, name): + pool = Pool() + Uom = pool.get('product.uom') + work_center = self.operation.work_center + if not work_center: + return Decimal('0.0') + quantity = Uom.compute_qty(self.uom, self.quantity, + work_center.uom) + return Decimal(str(quantity)) * work_center.cost_price + + def on_change_with_uom(self): + if self.operation.work_center: + return self.operation.work_center.uom.id + + def on_change_with_unit_digits(self, name=None): + if self.uom: + return self.uom.digits + return 2 class Production: __name__ = 'production' + route = fields.Many2One('production.route', 'Route', - on_change=['route', 'operations']) + on_change=['route', 'operations'], states={ + 'readonly': ~Eval('state').in_(['request', 'draft']), + }) operations = fields.One2Many('production.operation', 'production', - 'Operations') + 'Operations', order=[('sequence', 'ASC')], states={ + 'readonly': Eval('state') == 'done', + }) + + @classmethod + def __setup__(cls): + super(Production, cls).__setup__() + cls._error_messages.update({ + 'pending_operations': ('Production "%(production)s" can not be' + ' done because their operation "%(operation)s" is not ' + 'done.'), + 'no_work_center': ('We can not found any work center for ' + 'Operation "%s".'), + }) def update_operations(self): if not self.route: @@ -143,10 +229,11 @@ class Production: } for operation in self.route.operations: operations['add'].append({ - 'name': operation.name, 'sequence': operation.sequence, 'work_center': (operation.work_center.id if operation.work_center else None), + 'operation_type': (operation.operation_type.id + if operation.operation_type else None), 'route_operation': operation.id, }) return changes @@ -154,6 +241,57 @@ class Production: def on_change_route(self): return self.update_operations() + @classmethod + def run(cls, productions): + pool = Pool() + Operation = pool.get('production.operation') + + super(Production, cls).run(productions) + + operations = [] + for production in productions: + operations.extend(production.operations) + + if operations: + Operation.wait(operations) + + @classmethod + def done(cls, productions): + pool = Pool() + Operation = pool.get('production.operation') + Template = pool.get('product.template') + Product = pool.get('product.product') + + pending_operations = Operation.search([ + ('production', 'in', [p.id for p in productions]), + ('state', '!=', 'done'), + ], limit=1) + if pending_operations: + if Transaction().context.get('from_done_operation', False): + return + operation, = pending_operations + cls.raise_user_error('pending_operations', error_args={ + 'production': operation.production.rec_name, + 'operation': operation.rec_name, + }) + + if hasattr(Product, 'cost_price'): + digits = Product.cost_price.digits + else: + digits = Template.cost_price.digits + + for production in productions: + operation_cost = sum(o.cost for o in production.operations) + total_quantity = Decimal(str(sum(o.quantity for o in + production.outputs))) + added_unit_price = Decimal(operation_cost / total_quantity + ).quantize(Decimal(str(10 ** -digits[1]))) + for output in production.outputs: + output.unit_price += added_unit_price + output.save() + + super(Production, cls).done(productions) + def get_cost(self, name): cost = super(Production, self).get_cost(name) for operation in self.operations: diff --git a/operation.xml b/operation.xml index 2a652df..12a59c3 100644 --- a/operation.xml +++ b/operation.xml @@ -1,46 +1,22 @@ + production.route.operation form route_operation_form + production.route.operation tree route_operation_list + - - Route Operation - production.route.operation - - - - - - - - - - - - - - - - - - - - - - - - - - - + production.operation form @@ -55,136 +31,86 @@ Operation production.operation - + - + + + Waiting + + [('state', '=', 'waiting')] + + + + Running + + [('state', '=', 'running')] + + + + All + + + + - + - - production.operation.line + + production.operation.tracking form - operation_line_form + operation_tracking_form - - production.operation.line + + production.operation.tracking tree - operation_line_list + operation_tracking_list - - Operation Line - production.operation.line + + Operation Tracking + production.operation.tracking - + - - + + - + - - + + - - + + - - production.work_center - form - work_center_form - - - production.work_center - tree - work_center_list - - - Work Center - production.work_center - - - - - - - - - - - - - - - - - - - - - - - - - - - - - production.work_center.category - form - work_center_category_form - - - production.work_center.category - tree - work_center_category_list - - - Work Center Category - production.work_center.category - - - - - - - - - - - - - - - - - - - - - - - - - - - + production form @@ -192,62 +118,11 @@ production_form - - production.route - form - route_form - - - production.route - tree - route_list - - - Route - production.route - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + name="Operations"/> + diff --git a/tests/scenario_production_operation.rst b/tests/scenario_production_operation.rst new file mode 100644 index 0000000..d2967d7 --- /dev/null +++ b/tests/scenario_production_operation.rst @@ -0,0 +1,251 @@ +=================== +Production Scenario +=================== + +============= +General Setup +============= + +Imports:: + + >>> import datetime + >>> from dateutil.relativedelta import relativedelta + >>> from decimal import Decimal + >>> from proteus import config, Model, Wizard + >>> today = datetime.date.today() + +Create database:: + + >>> config = config.set_trytond() + >>> config.pool.test = True + +Install production Module:: + + >>> Module = Model.get('ir.module.module') + >>> modules = Module.find([('name', '=', 'production_operation')]) + >>> Module.install([x.id for x in modules], config.context) + >>> Wizard('ir.module.module.install_upgrade').execute('upgrade') + +Create company:: + + >>> Currency = Model.get('currency.currency') + >>> CurrencyRate = Model.get('currency.currency.rate') + >>> Company = Model.get('company.company') + >>> Party = Model.get('party.party') + >>> company_config = Wizard('company.company.config') + >>> company_config.execute('company') + >>> company = company_config.form + >>> party = Party(name='Dunder Mifflin') + >>> party.save() + >>> company.party = party + >>> currencies = Currency.find([('code', '=', 'USD')]) + >>> if not currencies: + ... currency = Currency(name='Euro', symbol=u'$', code='USD', + ... rounding=Decimal('0.01'), mon_grouping='[3, 3, 0]', + ... mon_decimal_point=',') + ... currency.save() + ... CurrencyRate(date=today + relativedelta(month=1, day=1), + ... rate=Decimal('1.0'), currency=currency).save() + ... else: + ... currency, = currencies + >>> company.currency = currency + >>> company_config.execute('add') + >>> company, = Company.find() + +Reload the context:: + + >>> User = Model.get('res.user') + >>> config._context = User.get_preferences(True, config.context) + +Configuration production location:: + + >>> Location = Model.get('stock.location') + >>> warehouse, = Location.find([('code', '=', 'WH')]) + >>> production_location, = Location.find([('code', '=', 'PROD')]) + >>> warehouse.production_location = production_location + >>> warehouse.save() + +Create a route with two operations on diferent work center:: + + >>> ProductUom = Model.get('product.uom') + >>> Route = Model.get('production.route') + >>> OperationType = Model.get('production.operation.type') + >>> RouteOperation = Model.get('production.route.operation') + >>> assembly = OperationType(name='Assembly') + >>> assembly.save() + >>> clean = OperationType(name='clean') + >>> clean.save() + >>> WorkCenter = Model.get('production.work_center') + >>> hour, = ProductUom.find([('name', '=', 'Hour')]) + >>> workcenter1 = WorkCenter() + >>> workcenter1.name = 'Assembler Machine' + >>> workcenter1.type = 'machine' + >>> workcenter1.uom = hour + >>> workcenter1.cost_price = Decimal('25.0') + >>> workcenter1.save() + >>> workcenter2 = WorkCenter() + >>> workcenter2.name = 'Cleaner Machine' + >>> workcenter2.type = 'machine' + >>> workcenter2.uom = hour + >>> workcenter2.cost_price = Decimal('50.0') + >>> workcenter2.save() + >>> route = Route(name='default route') + >>> route_operation = RouteOperation() + >>> route.operations.append(route_operation) + >>> route_operation.sequence = 1 + >>> route_operation.operation_type = assembly + >>> route_operation.work_center = workcenter1 + >>> route_operation = RouteOperation() + >>> route.operations.append(route_operation) + >>> route_operation.sequence = 2 + >>> route_operation.operation_type = clean + >>> route_operation.work_center = workcenter2 + >>> route.save() + >>> route.reload() + >>> len(route.operations) == 2 + True + + +Create product:: + + >>> unit, = ProductUom.find([('name', '=', 'Unit')]) + >>> ProductTemplate = Model.get('product.template') + >>> Product = Model.get('product.product') + >>> product = Product() + >>> template = ProductTemplate() + >>> template.name = 'product' + >>> template.default_uom = unit + >>> template.type = 'goods' + >>> template.list_price = Decimal(30) + >>> template.cost_price = Decimal(20) + >>> template.save() + >>> product.template = template + >>> product.save() + +Create Components:: + + >>> component1 = Product() + >>> template1 = ProductTemplate() + >>> template1.name = 'component 1' + >>> template1.default_uom = unit + >>> template1.type = 'goods' + >>> template1.list_price = Decimal(5) + >>> template1.cost_price = Decimal(1) + >>> template1.save() + >>> component1.template = template1 + >>> component1.save() + + >>> meter, = ProductUom.find([('name', '=', 'Meter')]) + >>> centimeter, = ProductUom.find([('name', '=', 'centimeter')]) + >>> component2 = Product() + >>> template2 = ProductTemplate() + >>> template2.name = 'component 2' + >>> template2.default_uom = meter + >>> template2.type = 'goods' + >>> template2.list_price = Decimal(7) + >>> template2.cost_price = Decimal(5) + >>> template2.save() + >>> component2.template = template2 + >>> component2.save() + +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') + >>> input1 = BOMInput() + >>> bom.inputs.append(input1) + >>> input1.product = component1 + >>> input1.quantity = 5 + >>> input2 = BOMInput() + >>> bom.inputs.append(input2) + >>> input2.product = component2 + >>> input2.quantity = 150 + >>> input2.uom = centimeter + >>> output = BOMOutput() + >>> bom.outputs.append(output) + >>> output.product = product + >>> output.quantity = 1 + >>> bom.save() + + >>> ProductBom = Model.get('product.product-production.bom') + >>> product.boms.append(ProductBom(bom=bom)) + >>> product.save() + +Create an Inventory:: + + >>> Inventory = Model.get('stock.inventory') + >>> InventoryLine = Model.get('stock.inventory.line') + >>> storage, = Location.find([ + ... ('code', '=', 'STO'), + ... ]) + >>> inventory = Inventory() + >>> inventory.location = storage + >>> inventory_line1 = InventoryLine() + >>> inventory.lines.append(inventory_line1) + >>> inventory_line1.product = component1 + >>> inventory_line1.quantity = 10 + >>> inventory_line2 = InventoryLine() + >>> inventory.lines.append(inventory_line2) + >>> inventory_line2.product = component2 + >>> inventory_line2.quantity = 5 + >>> inventory.save() + >>> Inventory.confirm([inventory.id], config.context) + >>> inventory.state + u'done' + +Make a production:: + + >>> Production = Model.get('production') + >>> Operation = Model.get('production.operation') + >>> OperationTracking = Model.get('production.operation.tracking') + >>> production = Production() + >>> production.product = product + >>> production.route = route + >>> len(production.operations) == 2 + True + >>> [o.operation_type for o in production.operations] == [assembly, clean] + True + >>> production.bom = bom + >>> production.quantity = 2 + >>> sorted([i.quantity for i in production.inputs]) == [10, 300] + True + >>> output, = production.outputs + >>> output.quantity == 2 + True + >>> production.save() + >>> production.reload() + >>> [o.state for o in production.operations] == ['planned', 'planned'] + True + >>> Production.wait([production.id], config.context) + >>> production.state + u'waiting' + >>> Production.assign_try([production.id], config.context) + True + >>> production.reload() + >>> all(i.state == 'assigned' for i in production.inputs) + True + >>> Production.run([production.id], config.context) + >>> production.reload() + >>> all(i.state == 'done' for i in production.inputs) + True + >>> Production.done([production.id], config.context) + Traceback (most recent call last): + ... + UserError: ('UserError', (u'Production "1" can not be done because their operation "Assembly" is not done.', '')) + >>> operation1, operation2 = production.operations + >>> tracking = OperationTracking() + >>> operation1.lines.append(tracking) + >>> minute, = ProductUom.find([('name', '=', 'Minute')]) + >>> tracking.quantity = 180.0 + >>> tracking.uom = minute + >>> operation1.save() + >>> operations = [o.id for o in production.operations] + >>> Operation.run(operations, config.context) + >>> Operation.done(operations, config.context) + >>> production.reload() + >>> production.state + u'done' + >>> production.cost == Decimal('100') + True diff --git a/tests/test_production_operation.py b/tests/test_production_operation.py index 5c94ad1..e4a835d 100644 --- a/tests/test_production_operation.py +++ b/tests/test_production_operation.py @@ -10,7 +10,7 @@ if os.path.isdir(DIR): sys.path.insert(0, os.path.dirname(DIR)) import unittest -#import doctest TODO: Remove if no sceneario needed. +import doctest import trytond.tests.test_tryton from trytond.tests.test_tryton import test_view, test_depends from trytond.backend.sqlite.database import Database as SQLiteDatabase @@ -50,10 +50,9 @@ def doctest_dropdb(test): def suite(): suite = trytond.tests.test_tryton.suite() suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase)) - # TODO: remove if no scenario needed. - #suite.addTests(doctest.DocFileSuite('scenario_invoice.rst', - # setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='utf-8', - # optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) + suite.addTests(doctest.DocFileSuite('scenario_production_operation.rst', + setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='utf-8', + optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) return suite if __name__ == '__main__': diff --git a/tryton.cfg b/tryton.cfg index dba73a9..377eb8f 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -1,6 +1,6 @@ [tryton] version=2.9.0 depends: - production + production_route xml: operation.xml diff --git a/view/operation_form.xml b/view/operation_form.xml index ee1b51c..e8042b6 100644 --- a/view/operation_form.xml +++ b/view/operation_form.xml @@ -2,14 +2,25 @@
+