Manage lead time on production
- Add lead time on BOM - Add start dates on production issue5560 review23131003
This commit is contained in:
1 parent
1281e94017
commit
4354895f80
14 files changed
+176
-11
No files matched your search
@@ -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
|
||||
|
||||
@@ -22,6 +22,7 @@ def register():
|
||||
AssignFailed,
|
||||
Product,
|
||||
ProductBom,
|
||||
ProductionLeadTime,
|
||||
Location,
|
||||
Move,
|
||||
module='production', type_='model')
|
||||
|
||||
+41
-2
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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,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>
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user