260 lines
8.3 KiB
Python
260 lines
8.3 KiB
Python
# The COPYRIGHT file at the top level of this repository contains the full
|
|
# copyright notices and license terms.
|
|
import datetime
|
|
from sql import Literal
|
|
from sql.conditionals import Coalesce
|
|
|
|
from trytond.model import ModelView, Unique, Check, fields
|
|
from trytond.wizard import Wizard, StateView, Button, StateAction
|
|
from trytond.transaction import Transaction
|
|
from trytond.pyson import PYSONEncoder, Bool, Eval, If
|
|
from trytond.pool import Pool, PoolMeta
|
|
|
|
__all__ = ['BOM', 'Production',
|
|
'NewVersionStart', 'NewVersion', 'OpenVersions']
|
|
|
|
|
|
class BOM:
|
|
__name__ = 'production.bom'
|
|
__metaclass__ = PoolMeta
|
|
|
|
start_date = fields.Date('Start Date', required=True)
|
|
end_date = fields.Date('End Date')
|
|
version = fields.Integer('Version', readonly=True)
|
|
master_bom = fields.Many2One('production.bom', 'BOM', readonly=True)
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(BOM, cls).__setup__()
|
|
t = cls.__table__()
|
|
cls._sql_constraints += [
|
|
('report_code_uniq', Unique(t, t.master_bom, t.version),
|
|
'Version Must be unique per BOM'),
|
|
('end_date_check',
|
|
Check(t, ((t.end_date == None) | (t.end_date > t.start_date))),
|
|
'End date must be greater than start date'),
|
|
]
|
|
cls._error_messages.update({
|
|
'invalid_dates': 'Invalid dates for version "%(bom)s". They '
|
|
'overlap with version "%(version)s.',
|
|
})
|
|
|
|
@staticmethod
|
|
def default_version():
|
|
return 1
|
|
|
|
@staticmethod
|
|
def default_start_date():
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
return Date.today()
|
|
|
|
@classmethod
|
|
def get_last_version(cls, master_bom):
|
|
'''
|
|
Get latest version for master_bom
|
|
'''
|
|
with Transaction().set_context(show_versions=True):
|
|
boms = cls.search([
|
|
('master_bom', '=', master_bom),
|
|
], order=[
|
|
('version', 'DESC')
|
|
], limit=1)
|
|
if boms:
|
|
return boms[0]
|
|
|
|
@classmethod
|
|
def validate(cls, boms):
|
|
super(BOM, cls).validate(boms)
|
|
for bom in boms:
|
|
bom.check_dates()
|
|
|
|
def check_dates(self):
|
|
if not self.master_bom:
|
|
return
|
|
with Transaction().set_context(show_versions=True):
|
|
domain = [
|
|
('master_bom', '=', self.master_bom.id),
|
|
('id', '!=', self.id),
|
|
]
|
|
if not self.end_date:
|
|
domain.append(['OR', [
|
|
('end_date', '=', None),
|
|
], [
|
|
('end_date', '>', self.start_date),
|
|
]
|
|
])
|
|
else:
|
|
domain.append(('start_date', '<', self.end_date))
|
|
domain.append(['OR', [
|
|
('end_date', '=', None),
|
|
], [
|
|
('end_date', '>', self.start_date),
|
|
]
|
|
])
|
|
with Transaction().set_context(show_versions=True):
|
|
boms = self.search(domain, limit=1)
|
|
if boms:
|
|
self.raise_user_error('invalid_dates', {
|
|
'bom': self.rec_name,
|
|
'version': boms[0].version,
|
|
})
|
|
|
|
@classmethod
|
|
def create(cls, vlist):
|
|
boms = super(BOM, cls).create(vlist)
|
|
for bom in boms:
|
|
if not bom.master_bom:
|
|
bom.master_bom = bom
|
|
bom.save()
|
|
return boms
|
|
|
|
@classmethod
|
|
def copy(cls, boms, default=None):
|
|
if default is None:
|
|
default = {}
|
|
else:
|
|
default = default.copy()
|
|
|
|
if not Transaction().context.get('new_version', False):
|
|
default['master_bom'] = None
|
|
default['version'] = cls.default_version()
|
|
return super(BOM, cls).copy(boms, default=default)
|
|
|
|
new_boms = []
|
|
for bom in boms:
|
|
last_bom = cls.get_last_version(bom.master_bom)
|
|
default['version'] = last_bom.version + 1
|
|
new_boms.extend(super(BOM, cls).copy([bom], default=default))
|
|
return new_boms
|
|
|
|
@classmethod
|
|
def search(cls, args, offset=0, limit=None, order=None, count=False,
|
|
query=False):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
transaction = Transaction()
|
|
context = transaction.context
|
|
cursor = transaction.connection.cursor()
|
|
|
|
if not context.get('show_versions', False):
|
|
table = cls.__table__()
|
|
today = (context['production_date']
|
|
if context.get('production_date') else Date.today())
|
|
|
|
q = table.select(table.id, where=(table.start_date <= today) &
|
|
(Literal(today) <= Coalesce(table.end_date, datetime.date.max))
|
|
)
|
|
cursor.execute(*q)
|
|
ids = [r[0] for r in cursor.fetchall()]
|
|
args.append(('id', 'in', ids))
|
|
|
|
return super(BOM, cls).search(args, offset=offset, limit=limit,
|
|
order=order, count=count, query=query)
|
|
|
|
@classmethod
|
|
def new_version(cls, boms, date):
|
|
cls.write(boms, {'end_date': date - datetime.timedelta(days=1)})
|
|
with Transaction().set_context(new_version=True):
|
|
new_boms = cls.copy(boms, {
|
|
'end_date': None,
|
|
'start_date': date
|
|
})
|
|
return new_boms
|
|
|
|
|
|
class Production:
|
|
__name__ = 'production'
|
|
__metaclass__ = PoolMeta
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(Production, cls).__setup__()
|
|
if 'production_date' not in cls.bom.context:
|
|
cls.bom.context['production_date'] = If(
|
|
Bool(Eval('effective_date')),
|
|
Eval('effective_date'),
|
|
Eval('planned_date'))
|
|
for fname in ('effective_date', 'planned_date'):
|
|
if fname not in cls.bom.depends:
|
|
cls.bom.depends.append(fname)
|
|
|
|
|
|
class NewVersionStart(ModelView):
|
|
'New Version Start'
|
|
__name__ = 'production.bom.new.version.start'
|
|
|
|
date = fields.Date('Date')
|
|
|
|
@staticmethod
|
|
def default_date():
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
return Date.today()
|
|
|
|
|
|
class NewVersion(Wizard):
|
|
'New Version'
|
|
__name__ = 'production.bom.new.version'
|
|
|
|
start = StateView('production.bom.new.version.start',
|
|
'production_bom_versions.new_version_start_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Create', 'create_', 'tryton-go-next', default=True),
|
|
])
|
|
create_ = StateAction('production.act_bom_list')
|
|
|
|
def do_create_(self, action):
|
|
pool = Pool()
|
|
BOM = pool.get('production.bom')
|
|
|
|
boms = BOM.browse(Transaction().context['active_ids'])
|
|
new_versions = BOM.new_version(boms, self.start.date)
|
|
|
|
data = {'res_id': [i.id for i in new_versions]}
|
|
if len(new_versions) == 1:
|
|
action['views'].reverse()
|
|
return action, data
|
|
|
|
def transition_create_(self):
|
|
return 'end'
|
|
|
|
|
|
class OpenVersions(Wizard):
|
|
'Open Versions'
|
|
__name__ = 'production.bom.open_versions'
|
|
start_state = 'open_'
|
|
open_ = StateAction('production.act_bom_list')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(OpenVersions, cls).__setup__()
|
|
cls._error_messages.update({
|
|
'versions': ('%s\'s versions'),
|
|
})
|
|
|
|
def do_open_(self, action):
|
|
pool = Pool()
|
|
Bom = pool.get('production.bom')
|
|
|
|
transaction = Transaction()
|
|
context = transaction.context
|
|
|
|
bom = Bom(context.get('active_id'))
|
|
|
|
encoder = PYSONEncoder()
|
|
action['pyson_domain'] = encoder.encode(
|
|
[('master_bom', '=', bom and bom.master_bom and bom.master_bom.id)])
|
|
action['pyson_order'] = encoder.encode([('version', 'DESC')])
|
|
context = {'show_versions': True}
|
|
bom = Bom.get_last_version(bom.master_bom and bom.master_bom.id)
|
|
action['pyson_context'] = encoder.encode(context)
|
|
|
|
action['name'] += ' - %s' % (self.raise_user_error(error='versions',
|
|
raise_exception=False, error_args=bom.rec_name))
|
|
|
|
return action, {}
|
|
|
|
def transition_open_(self):
|
|
return 'end'
|