Files
tryton/period.py
T

176 lines
6.2 KiB
Python
Raw Normal View History

2014-12-02 12:56:04 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from itertools import chain
from sql import Literal, For
2018-12-29 14:20:29 +01:00
from trytond.i18n import gettext
2013-06-21 23:24:17 +02:00
from trytond.model import Workflow, ModelView, ModelSQL, fields
2015-06-28 23:45:31 +02:00
from trytond.pyson import Eval, If
from trytond.transaction import Transaction
2011-07-03 13:19:14 +02:00
from trytond.pool import Pool
2014-07-17 19:00:19 +02:00
from trytond.tools import grouped_slice
2018-12-29 14:20:29 +01:00
from .exceptions import PeriodCloseError
2012-09-24 22:18:17 +02:00
__all__ = ['Period', 'Cache']
2013-06-21 23:24:17 +02:00
class Period(Workflow, ModelSQL, ModelView):
'Stock Period'
2012-09-24 22:18:17 +02:00
__name__ = 'stock.period'
date = fields.Date('Date', required=True, states={
2015-06-28 23:45:31 +02:00
'readonly': Eval('state') == 'closed',
2018-12-04 12:25:45 +01:00
}, depends=['state'],
help="When the stock period ends.")
company = fields.Many2One('company.company', 'Company', required=True,
domain=[
2015-06-28 23:45:31 +02:00
('id', If(Eval('context', {}).contains('company'), '=', '!='),
2013-10-09 18:25:53 +02:00
Eval('context', {}).get('company', -1)),
2018-12-04 12:25:45 +01:00
],
help="The company the stock period is associated with.")
2013-06-21 23:24:17 +02:00
caches = fields.One2Many('stock.period.cache', 'period', 'Caches',
readonly=True)
state = fields.Selection([
('draft', 'Draft'),
('closed', 'Closed'),
2018-12-04 12:25:45 +01:00
], 'State', select=True, readonly=True,
help="The current state of the stock period.")
2012-09-24 22:18:17 +02:00
@classmethod
def __setup__(cls):
super(Period, cls).__setup__()
2013-06-21 23:24:17 +02:00
cls._transitions |= set((
('draft', 'closed'),
('closed', 'draft'),
))
2012-09-24 22:18:17 +02:00
cls._buttons.update({
2012-03-25 00:18:19 +01:00
'draft': {
'invisible': Eval('state') == 'draft',
2018-02-19 11:41:53 +01:00
'depends': ['state'],
2012-03-25 00:18:19 +01:00
},
'close': {
'invisible': Eval('state') == 'closed',
2018-02-19 11:41:53 +01:00
'depends': ['state'],
2012-03-25 00:18:19 +01:00
},
})
2015-01-16 16:14:32 +01:00
@staticmethod
def default_company():
return Transaction().context.get('company')
2012-12-05 15:12:50 +01:00
@staticmethod
def default_state():
return 'draft'
2013-06-21 23:24:17 +02:00
@staticmethod
def groupings():
return [('product',)]
@staticmethod
def get_cache(grouping):
pool = Pool()
if all(g == 'product' or g.startswith('product.') for g in grouping):
2013-06-21 23:24:17 +02:00
return pool.get('stock.period.cache')
2017-07-26 23:43:27 +02:00
def get_rec_name(self, name):
return str(self.date)
2012-09-24 22:18:17 +02:00
@classmethod
2012-03-25 00:18:19 +01:00
@ModelView.button
2013-06-21 23:24:17 +02:00
@Workflow.transition('draft')
def draft(cls, periods):
for grouping in cls.groupings():
Cache = cls.get_cache(grouping)
caches = []
2014-07-17 19:00:19 +02:00
for sub_periods in grouped_slice(periods):
2013-06-21 23:24:17 +02:00
caches.append(Cache.search([
2013-10-05 12:17:26 +02:00
('period', 'in',
2014-07-17 19:00:19 +02:00
[p.id for p in sub_periods]),
2013-06-21 23:24:17 +02:00
], order=[]))
Cache.delete(list(chain(*caches)))
@classmethod
@ModelView.button
@Workflow.transition('closed')
2012-09-24 22:18:17 +02:00
def close(cls, periods):
2011-07-03 13:19:14 +02:00
pool = Pool()
2012-09-24 22:18:17 +02:00
Product = pool.get('product.product')
Location = pool.get('stock.location')
Move = pool.get('stock.move')
Date = pool.get('ir.date')
transaction = Transaction()
connection = transaction.connection
database = transaction.database
# XXX: A move in the period could be inserted before the lock
# from a different transaction. It will not be taken in the pbl
# computation but it is quite rare because only past periods are
# closed.
database.lock(connection, Move._table)
if database.has_select_for():
move = Move.__table__()
query = move.select(Literal(1), for_=For('UPDATE', nowait=True))
with connection.cursor() as cursor:
cursor.execute(*query)
2012-09-24 22:18:17 +02:00
locations = Location.search([
('type', 'not in', ['warehouse', 'view']),
], order=[])
today = Date.today()
recent_date = max(period.date for period in periods)
if recent_date >= today:
2018-12-29 14:20:29 +01:00
raise PeriodCloseError(
gettext('stock.msg_period_close_date'))
2012-09-24 22:18:17 +02:00
if Move.search([
('state', '=', 'assigned'),
['OR', [
('effective_date', '=', None),
('planned_date', '<=', recent_date),
],
('effective_date', '<=', recent_date),
]]):
2018-12-29 14:20:29 +01:00
raise PeriodCloseError(
gettext('stock.msg_period_close_assigned_move'))
2013-06-21 23:24:17 +02:00
for grouping in cls.groupings():
Cache = cls.get_cache(grouping)
to_create = []
for period in periods:
with Transaction().set_context(
stock_date_end=period.date,
stock_date_start=None,
stock_assign=False,
forecast=False,
stock_destinations=None,
):
pbl = Product.products_by_location(
[l.id for l in locations], grouping=grouping)
2018-06-26 19:11:55 +02:00
for key, quantity in pbl.items():
2013-06-21 23:24:17 +02:00
values = {
'location': key[0],
2012-09-24 22:18:17 +02:00
'period': period.id,
'internal_quantity': quantity,
2013-06-21 23:24:17 +02:00
}
for i, field in enumerate(grouping, 1):
values[field] = key[i]
to_create.append(values)
if to_create:
Cache.create(to_create)
class Cache(ModelSQL, ModelView):
'''
Stock Period Cache
It is used to store cached computation of stock quantities.
'''
2012-09-24 22:18:17 +02:00
__name__ = 'stock.period.cache'
period = fields.Many2One('stock.period', 'Period', required=True,
2012-03-05 10:53:08 +01:00
readonly=True, select=True, ondelete='CASCADE')
location = fields.Many2One('stock.location', 'Location', required=True,
2012-03-05 10:53:08 +01:00
readonly=True, select=True, ondelete='CASCADE')
product = fields.Many2One('product.product', 'Product', required=True,
2012-03-05 10:53:08 +01:00
readonly=True, select=True, ondelete='CASCADE')
internal_quantity = fields.Float('Internal Quantity', readonly=True)