Files
tryton/modules/timesheet/work.py
T

225 lines
7.7 KiB
Python
Raw Normal View History

2014-12-02 12:56:21 +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.
2015-02-09 23:02:56 +01:00
import datetime
2013-09-06 22:35:00 +02:00
from sql import Literal
from sql.aggregate import Sum
2018-12-29 14:20:30 +01:00
from trytond.i18n import gettext
from trytond.model import ModelView, ModelSQL, ModelStorage, fields, Unique
from trytond.pyson import Bool, Eval, If
2010-08-10 11:59:04 -04:00
from trytond.transaction import Transaction
2011-07-03 13:19:21 +02:00
from trytond.pool import Pool
2014-07-17 19:02:04 +02:00
from trytond.tools import reduce_ids, grouped_slice
2011-07-03 13:19:21 +02:00
2018-12-29 14:20:30 +01:00
from .exceptions import CompanyValidationError
2008-05-29 17:55:36 +02:00
class Work(ModelSQL, ModelView):
2008-06-09 16:54:33 +02:00
'Work'
2012-09-24 22:18:28 +02:00
__name__ = 'timesheet.work'
name = fields.Char('Name',
states={
'invisible': Bool(Eval('origin')),
'required': ~Eval('origin'),
},
2017-04-06 15:33:15 +02:00
depends=['origin'],
help="The main identifier of the work.")
origin = fields.Reference('Origin', selection='get_origin',
states={
'invisible': Bool(Eval('name')),
'required': ~Eval('name'),
},
2017-04-06 15:33:15 +02:00
depends=['name'],
help="Use to relate the time spent to other records.")
2015-02-09 23:02:56 +01:00
duration = fields.Function(fields.TimeDelta('Timesheet Duration',
2017-04-06 15:33:15 +02:00
'company_work_time', help="Total time spent on this work."),
2015-02-09 23:02:56 +01:00
'get_duration')
2016-10-13 09:44:48 +02:00
timesheet_start_date = fields.Date('Timesheet Start',
domain=[
If(Eval('timesheet_start_date') & Eval('timesheet_end_date'),
('timesheet_start_date', '<=', Eval('timesheet_end_date')),
()),
],
2017-04-06 15:33:15 +02:00
depends=['timesheet_end_date'],
help="Restrict adding lines before the date.")
2016-10-13 09:44:48 +02:00
timesheet_end_date = fields.Date('Timesheet End',
domain=[
If(Eval('timesheet_start_date') & Eval('timesheet_end_date'),
('timesheet_end_date', '>=', Eval('timesheet_start_date')),
()),
],
2017-04-06 15:33:15 +02:00
depends=['timesheet_start_date'],
help="Restrict adding lines after the date.")
2012-03-14 09:35:19 +01:00
company = fields.Many2One('company.company', 'Company', required=True,
2017-04-06 15:33:15 +02:00
select=True, help="Make the work belong to the company.")
timesheet_lines = fields.One2Many('timesheet.line', 'work',
2011-08-04 16:57:47 +02:00
'Timesheet Lines',
2017-04-06 15:33:15 +02:00
help="Spend time on this work.")
# Self referring field to use for aggregation in graph view
work = fields.Function(fields.Many2One('timesheet.work', 'Work'),
'get_work')
2008-06-02 17:18:11 +02:00
2012-09-24 22:18:28 +02:00
@classmethod
def __setup__(cls):
super(Work, cls).__setup__()
t = cls.__table__()
cls._sql_constraints += [
('origin_unique', Unique(t, t.origin, t.company),
2018-12-29 14:20:30 +01:00
'timesheet.msg_work_origin_unique_company'),
]
@classmethod
def __register__(cls, module_name):
2018-07-24 22:01:08 +02:00
table_h = cls.__table_handler__(module_name)
table = cls.__table__()
cursor = Transaction().connection.cursor()
super(Work, cls).__register__(module_name)
# Migration from 4.0: remove required on name
table_h.not_null_action('name', 'remove')
# Migration from 4.0: remove parent, left and right
if table_h.column_exist('parent'):
id2name = {}
id2parent = {}
cursor.execute(*table.select(
table.id, table.parent, table.name))
for id_, parent, name in cursor:
id2name[id_] = name
id2parent[id_] = parent
2018-06-26 19:11:58 +02:00
for id_, name in id2name.items():
parent = id2parent[id_]
while parent:
name = '%s\\%s' % (id2name[parent], name)
parent = id2parent[parent]
cursor.execute(*table.update(
[table.name], [name],
where=table.id == id_))
table_h.drop_column('parent')
table_h.drop_column('left')
table_h.drop_column('right')
# Migration from 4.0: remove timesheet_available
if table_h.column_exist('timesheet_available'):
cursor.execute(*table.delete(
2020-03-01 12:33:54 +01:00
where=table.timesheet_available == Literal(False)))
table_h.drop_column('timesheet_available')
2012-09-24 22:18:28 +02:00
@staticmethod
def default_company():
2012-03-27 10:44:13 +02:00
return Transaction().context.get('company')
2008-06-09 19:17:07 +02:00
@classmethod
def _get_origin(cls):
'Return list of Model names for origin Reference'
return []
@classmethod
def get_origin(cls):
Model = Pool().get('ir.model')
2020-12-27 00:19:30 +01:00
get_name = Model.get_name
models = cls._get_origin()
2020-12-27 00:19:30 +01:00
return [('', '')] + [(m, get_name(m)) for m in models]
2009-05-22 19:11:29 +02:00
2012-09-24 22:18:28 +02:00
@classmethod
2015-02-09 23:02:56 +01:00
def get_duration(cls, works, name):
2013-08-21 18:40:44 +02:00
pool = Pool()
Line = pool.get('timesheet.line')
transaction = Transaction()
cursor = transaction.connection.cursor()
2013-08-21 18:40:44 +02:00
context = transaction.context
2013-09-06 22:35:00 +02:00
table_w = cls.__table__()
line = Line.__table__()
2012-09-24 22:18:28 +02:00
ids = [w.id for w in works]
2015-02-09 23:02:56 +01:00
durations = dict.fromkeys(ids, None)
2013-09-06 22:35:00 +02:00
where = Literal(True)
2013-08-21 18:40:44 +02:00
if context.get('from_date'):
2013-09-06 22:35:00 +02:00
where &= line.date >= context['from_date']
2013-08-21 18:40:44 +02:00
if context.get('to_date'):
2013-09-06 22:35:00 +02:00
where &= line.date <= context['to_date']
2013-09-20 17:32:34 +02:00
if context.get('employees'):
where &= line.employee.in_(context['employees'])
query_table = table_w.join(line, 'LEFT',
condition=line.work == table_w.id)
2014-07-17 19:02:04 +02:00
for sub_ids in grouped_slice(ids):
2013-09-06 22:35:00 +02:00
red_sql = reduce_ids(table_w.id, sub_ids)
cursor.execute(*query_table.select(table_w.id, Sum(line.duration),
2013-09-06 22:35:00 +02:00
where=red_sql & where,
group_by=table_w.id))
for work_id, duration in cursor:
2015-02-09 23:02:56 +01:00
# SQLite uses float for SUM
if duration and not isinstance(duration, datetime.timedelta):
duration = datetime.timedelta(seconds=duration)
durations[work_id] = duration
return durations
2008-06-03 01:57:48 +02:00
def get_work(self, name):
return self.id
2012-09-24 22:18:28 +02:00
def get_rec_name(self, name):
if isinstance(self.origin, ModelStorage):
return self.origin.rec_name
2012-09-24 22:18:28 +02:00
else:
return self.name
2012-04-03 20:07:54 +02:00
2013-04-06 12:29:12 +02:00
@classmethod
def search_rec_name(cls, name, clause):
if clause[1].startswith('!') or clause[1].startswith('not '):
bool_op = 'AND'
2013-04-06 12:29:12 +02:00
else:
bool_op = 'OR'
return [bool_op,
('name',) + tuple(clause[1:]),
] + [
('origin.rec_name',) + tuple(clause[1:]) + (origin,)
for origin in cls._get_origin()]
2013-04-06 12:29:12 +02:00
2012-09-24 22:18:28 +02:00
@classmethod
def copy(cls, works, default=None):
if default is None:
default = {}
2018-09-01 01:02:04 +02:00
else:
default = default.copy()
default.setdefault('timesheet_lines', None)
2012-09-24 22:18:28 +02:00
return super(Work, cls).copy(works, default=default)
2012-09-24 22:18:28 +02:00
@classmethod
def validate(cls, works):
super(Work, cls).validate(works)
for work in works:
if work.origin and not work._validate_company():
2018-12-29 14:20:30 +01:00
raise CompanyValidationError(
gettext('timesheet.msg_work_company_different_origin',
work=work.rec_name))
2013-12-19 14:45:42 +01:00
def _validate_company(self):
return True
2008-06-03 01:57:48 +02:00
2014-02-20 23:29:33 +01:00
@classmethod
def search_global(cls, text):
2015-02-04 13:39:00 +01:00
for record, rec_name, icon in super(Work, cls).search_global(text):
2014-02-20 23:29:33 +01:00
icon = icon or 'tryton-clock'
2015-02-04 13:39:00 +01:00
yield record, rec_name, icon
2014-02-20 23:29:33 +01:00
2015-02-09 23:02:56 +01:00
@property
def hours(self):
if not self.duration:
return 0
return self.duration.total_seconds() / 60 / 60
2008-06-03 01:57:48 +02:00
class WorkContext(ModelView):
'Work Context'
__name__ = 'timesheet.work.context'
2017-04-06 15:33:15 +02:00
from_date = fields.Date('From Date',
help="Do not take into account lines before the date.")
to_date = fields.Date('To Date',
help="Do not take into account lines after the date.")