Manage lead time on production

- Add lead time on BOM
- Add start dates on production

issue5560
review23131003
This commit is contained in:
Cédric Krier committed 2016-06-24 09:22:52 +02:00
1 parent 1281e94017
commit 4354895f80
14 files changed
+176 -11

No files matched your search

+3
View File
@@ -1,3 +1,6 @@
* Add Lead Time of Production
* Add Start Dates on Production
Version 4.0.0 - 2016-05-02
* Bug fixes (see mercurial logs for details)
* Rename Production code into number
+1
View File
@@ -22,6 +22,7 @@ def register():
AssignFailed,
Product,
ProductBom,
ProductionLeadTime,
Location,
Move,
module='production', type_='model')
+41 -2
View File
@@ -3,11 +3,11 @@
from sql import Null
from sql.conditionals import Case
from trytond.model import ModelView, ModelSQL, fields
from trytond.model import ModelView, ModelSQL, MatchMixin, fields
from trytond.pyson import Eval, Get, If, Bool
from trytond.pool import PoolMeta
__all__ = ['Product', 'ProductBom']
__all__ = ['Product', 'ProductBom', 'ProductionLeadTime']
class Product:
@@ -22,6 +22,14 @@ class Product:
'type', 'service').in_(['service', None]))),
},
depends=['type'])
lead_times = fields.One2Many('production.lead_time',
'product', 'Lead Times', order=[('sequence', 'ASC'), ('id', 'ASC')],
states={
'invisible': (Eval('type', 'service').in_(['service', None])
& (Eval('_parent_template', {}).get(
'type', 'service').in_(['service', None]))),
},
depends=['type'])
@classmethod
def __setup__(cls):
@@ -91,3 +99,34 @@ class ProductBom(ModelSQL, ModelView):
def order_sequence(tables):
table, _ = tables[None]
return [Case((table.sequence == Null, 0), else_=1), table.sequence]
class ProductionLeadTime(ModelSQL, ModelView, MatchMixin):
'Production Lead Time'
__name__ = 'production.lead_time'
product = fields.Many2One('product.product', 'Product',
ondelete='CASCADE', select=True, required=True,
domain=[
('type', '!=', 'service'),
])
sequence = fields.Integer('Sequence')
bom = fields.Many2One('production.bom', 'BOM', ondelete='CASCADE',
domain=[
('output_products', '=', If(Bool(Eval('product')),
Eval('product', -1),
Get(Eval('_parent_product', {}), 'id', 0))),
],
depends=['product'])
lead_time = fields.TimeDelta('Lead Time')
@classmethod
def __setup__(cls):
super(ProductionLeadTime, cls).__setup__()
cls._order.insert(0, ('product', 'ASC'))
cls._order.insert(0, ('sequence', 'ASC'))
@classmethod
def order_sequence(cls, tables):
table, _ = tables[None]
return [Case((table.sequence == Null, 0), else_=1), table.sequence]
+35
View File
@@ -44,5 +44,40 @@ this repository contains the full copyright notices and license terms. -->
<field name="inherit" ref="product.product_view_form"/>
<field name="name">product_form</field>
</record>
<record model="ir.ui.view" id="production_lead_time_view_list">
<field name="model">production.lead_time</field>
<field name="type">tree</field>
<field name="name">production_lead_time_list</field>
</record>
<record model="ir.ui.view"
id="production_lead_time_view_list_sequence">
<field name="model">production.lead_time</field>
<field name="type">tree</field>
<field name="name">production_lead_time_list_sequence</field>
</record>
<record model="ir.ui.view" id="production_lead_time_view_form">
<field name="model">production.lead_time</field>
<field name="type">form</field>
<field name="name">production_lead_time_form</field>
</record>
<record model="ir.model.access" id="access_production_lead_time">
<field name="model" search="[('model', '=', 'production.lead_time')]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_production_lead_time_admin">
<field name="model" search="[('model', '=', 'production.lead_time')]"/>
<field name="group" ref="group_production_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
</data>
</tryton>
+38 -1
View File
@@ -2,6 +2,8 @@
# this repository contains the full copyright notices and license terms.
from decimal import Decimal
from sql import Null
from trytond.model import ModelView, ModelSQL, Workflow, fields
from trytond.wizard import Wizard, StateTransition, StateView, Button
from trytond.pyson import Eval, Bool, If, Id
@@ -38,6 +40,17 @@ class Production(Workflow, ModelSQL, ModelView):
'readonly': Eval('state').in_(['cancel', 'done']),
},
depends=['state'])
planned_start_date = fields.Date('Planned Start Date',
states={
'readonly': ~Eval('state').in_(['request', 'draft']),
'required': Bool(Eval('planned_date')),
},
depends=['state', 'planned_date'])
effective_start_date = fields.Date('Effective Start Date',
states={
'readonly': Eval('state').in_(['cancel', 'running', 'done']),
},
depends=['state'])
company = fields.Many2One('company.company', 'Company', required=True,
states={
'readonly': ~Eval('state').in_(['request', 'draft']),
@@ -207,6 +220,7 @@ class Production(Workflow, ModelSQL, ModelView):
def __register__(cls, module_name):
TableHandler = backend.get('TableHandler')
table_h = TableHandler(cls, module_name)
table = cls.__table__()
# Migration from 3.8: rename code into number
if table_h.column_exist('code'):
@@ -214,6 +228,14 @@ class Production(Workflow, ModelSQL, ModelView):
super(Production, cls).__register__(module_name)
# Migration from 4.0: fill planned_start_date
cursor = Transaction().connection.cursor()
cursor.execute(*table.update(
[table.planned_start_date],
[table.planned_date],
where=(table.planned_start_date == Null)
& (table.planned_date != Null)))
@staticmethod
def default_state():
return 'draft'
@@ -237,6 +259,17 @@ class Production(Workflow, ModelSQL, ModelView):
def default_company():
return Transaction().context.get('company')
@fields.depends('planned_date', 'product', 'bom')
def on_change_with_planned_start_date(self, pattern=None):
if self.planned_date and self.product:
if pattern is None:
pattern = {}
pattern.setdefault('bom', self.bom.id if self.bom else None)
for line in self.product.lead_times:
if line.match(pattern):
return self.planned_date - line.lead_time
return self.planned_date
def _move(self, from_location, to_location, company, product, uom,
quantity):
Move = Pool().get('stock.move')
@@ -486,7 +519,7 @@ class Production(Workflow, ModelSQL, ModelView):
def _get_move_planned_date(self):
"Return the planned dates for input and output moves"
return self.planned_date, self.planned_date
return self.planned_start_date, self.planned_date
def _set_move_planned_date(self):
"Set planned date of moves for the shipments"
@@ -541,7 +574,11 @@ class Production(Workflow, ModelSQL, ModelView):
def run(cls, productions):
pool = Pool()
Move = pool.get('stock.move')
Date = pool.get('ir.date')
Move.do([m for p in productions for m in p.inputs])
cls.write([p for p in productions if not p.effective_start_date], {
'effective_start_date': Date.today(),
})
@classmethod
@ModelView.button
+1 -1
View File
@@ -37,7 +37,7 @@ class Move:
def set_effective_date(self):
if not self.effective_date and self.production_input:
self.effective_date = self.production_input.effective_date
self.effective_date = self.production_input.effective_start_date
if not self.effective_date and self.production_output:
self.effective_date = self.production_output.effective_date
super(Move, self).set_effective_date()
+14 -2
View File
@@ -13,6 +13,7 @@ Imports::
>>> from trytond.modules.production.production import BOM_CHANGES
>>> today = datetime.date.today()
>>> yesterday = today - relativedelta(days=1)
>>> before_yesterday = yesterday - relativedelta(days=1)
Create database::
@@ -104,6 +105,13 @@ Create Bill of Material::
>>> product.boms.append(ProductBom(bom=bom))
>>> product.save()
>>> ProductionLeadTime = Model.get('production.lead_time')
>>> production_lead_time = ProductionLeadTime()
>>> production_lead_time.product = product
>>> production_lead_time.bom = bom
>>> production_lead_time.lead_time = datetime.timedelta(1)
>>> production_lead_time.save()
Create an Inventory::
>>> Inventory = Model.get('stock.inventory')
@@ -130,9 +138,12 @@ Make a production::
>>> Production = Model.get('production')
>>> production = Production()
>>> production.planned_date = today
>>> production.product = product
>>> production.bom = bom
>>> production.quantity = 2
>>> production.planned_start_date == yesterday
True
>>> sorted([i.quantity for i in production.inputs]) == [10, 300]
True
>>> output, = production.outputs
@@ -177,11 +188,12 @@ Do the production::
>>> product.quantity == 2
True
Make a production with effective date yesterday::
Make a production with effective date yesterday and running the day before::
>>> Production = Model.get('production')
>>> production = Production()
>>> production.effective_date = yesterday
>>> production.effective_start_date = before_yesterday
>>> production.product = product
>>> production.bom = bom
>>> production.quantity = 2
@@ -190,7 +202,7 @@ Make a production with effective date yesterday::
True
>>> production.click('run')
>>> production.reload()
>>> all(i.effective_date == yesterday for i in production.inputs)
>>> all(i.effective_date == before_yesterday for i in production.inputs)
True
>>> production.click('done')
>>> production.reload()
+3 -1
View File
@@ -3,9 +3,11 @@
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form/notebook" position="inside">
<page name="boms">
<page string="Production" id="production">
<field name="boms" colspan="4"
view_ids="production.product-bom_view_list_sequence"/>
<field name="lead_times" colspan="4"
view_ids="production.production_lead_time_view_list_sequence"/>
</page>
</xpath>
</data>
+3 -1
View File
@@ -1,7 +1,9 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<calendar string="Productions" dtstart="planned_date">
<calendar string="Productions"
dtstart="planned_start_date"
dtend="planned_date">
<field name="number"/>
<field name="product"/>
<field name="reference"/>
+6 -2
View File
@@ -8,8 +8,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="number"/>
<label name="planned_date"/>
<field name="planned_date"/>
<label name="effective_date"/>
<field name="effective_date"/>
<label name="planned_start_date"/>
<field name="planned_start_date"/>
<label name="product"/>
<field name="product"/>
<label name="bom"/>
@@ -31,6 +31,10 @@ this repository contains the full copyright notices and license terms. -->
<field name="warehouse"/>
<label name="location"/>
<field name="location"/>
<label name="effective_date"/>
<field name="effective_date"/>
<label name="effective_start_date"/>
<field name="effective_start_date"/>
<label name="cost"/>
<field name="cost"/>
</page>
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form string="Production Lead Time">
<label name="product"/>
<field name="product" colspan="3"/>
<label name="sequence"/>
<field name="sequence"/>
<newline/>
<label name="bom"/>
<field name="bom" widget="selection"/>
<newline/>
<label name="lead_time"/>
<field name="lead_time"/>
</form>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree string="Production Lead Times">
<field name="product"/>
<field name="bom"/>
<field name="lead_time"/>
</tree>
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree string="Production Lead Times" sequence="sequence" editable="bottom">
<field name="bom" widget="selection"/>
<field name="lead_time"/>
</tree>
+1 -1
View File
@@ -7,7 +7,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="product"/>
<field name="quantity"/>
<field name="uom"/>
<field name="planned_start_date"/>
<field name="planned_date"/>
<field name="effective_date"/>
<field name="state"/>
</tree>