server-ux/date_range/models/date_range.py

168 lines
6.9 KiB
Python
Raw Permalink Normal View History

2021-03-23 19:38:33 +00:00
# Copyright 2016 ACSONE SA/NV (<https://acsone.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from flectra import api, fields, models
from flectra.tools.translate import _
from flectra.exceptions import ValidationError
class DateRange(models.Model):
_name = "date.range"
_description = "Date Range"
_order = "type_name,date_start"
@api.model
def _default_company(self):
return self.env['res.company']._company_default_get('date.range')
name = fields.Char(required=True, translate=True)
date_start = fields.Date(string='Start date', required=True)
date_end = fields.Date(string='End date', required=True)
type_id = fields.Many2one(
comodel_name='date.range.type', string='Type', index=1, required=True,
ondelete='restrict', domain="['|', ('company_id', '=', company_id), "
"('company_id', '=', False)]")
type_name = fields.Char(
string='Type', related='type_id.name', readonly=True, store=True)
company_id = fields.Many2one(
comodel_name='res.company', string='Company', index=1,
default=_default_company)
active = fields.Boolean(
help="The active field allows you to hide the date range without "
"removing it.", default=True)
parent_type_id = fields.Many2one(
related='type_id.parent_type_id',
store=True,
readonly=True)
parent_id = fields.Many2one(
comodel_name='date.range', string="Parent", index=1)
_sql_constraints = [
('date_range_uniq', 'unique (name,type_id, company_id, parent_id)',
'A date range must be unique per company and parent!')]
@api.onchange('company_id', 'type_id')
def _onchange_company_id(self):
if self.company_id and self.type_id.company_id and \
self.type_id.company_id != self.company_id:
self._cache.update(
self._convert_to_cache({'type_id': False}, update=True))
@api.multi
@api.constrains('company_id', 'type_id')
def _check_company_id_type_id(self):
for rec in self.sudo():
if rec.company_id and rec.type_id.company_id and\
rec.company_id != rec.type_id.company_id:
raise ValidationError(
_('The Company in the Date Range and in '
'Date Range Type must be the same.'))
@api.constrains('parent_id', 'date_start', 'date_end')
def _validate_child_range(self):
for this in self:
if not this.parent_id:
continue
date_start_valid = this.parent_id.date_start <= this.date_start
date_end_valid = this.parent_id.date_end >= this.date_end
if date_start_valid and date_end_valid:
continue
text_dict = {
'name': this.name,
'start': this.date_start,
'end': this.date_end,
'parent_name': this.parent_id.name,
'parent_start': this.parent_id.date_start,
'parent_end': this.parent_id.date_end,
}
if (not date_start_valid) and date_end_valid:
text = _(
"Start date %(start)s of %(name)s must be greater than"
" or equal to "
"start date %(parent_start)s of %(parent_name)s"
) % text_dict
elif (not date_end_valid) and date_start_valid:
text = _(
"End date %(end)s of %(name)s must be smaller than"
" or equal to "
"end date %(parent_end)s of %(parent_name)s"
) % text_dict
else:
text = _(
"%(name)s range not in "
"%(parent_start)s - %(parent_end)s"
) % text_dict
raise ValidationError(
_("%(name)s not a subrange of"
" %(parent_name)s: " % text_dict) + text
)
@api.constrains('type_id', 'date_start', 'date_end', 'company_id')
def _validate_range(self):
for this in self:
start = fields.Date.from_string(this.date_start)
end = fields.Date.from_string(this.date_end)
if start > end:
raise ValidationError(
_("%s is not a valid range (%s > %s)") % (
this.name, this.date_start, this.date_end))
if this.type_id.allow_overlap:
continue
# here we use a plain SQL query to benefit of the daterange
# function available in PostgresSQL
# (http://www.postgresql.org/docs/current/static/rangetypes.html)
SQL = """
SELECT
id
FROM
date_range dt
WHERE
DATERANGE(dt.date_start, dt.date_end, '[]') &&
DATERANGE(%s::date, %s::date, '[]')
AND dt.id != %s
AND dt.active
AND dt.company_id = %s
AND dt.type_id=%s;"""
self.env.cr.execute(SQL, (this.date_start,
this.date_end,
this.id,
this.company_id.id or None,
this.type_id.id))
res = self.env.cr.fetchall()
if res:
dt = self.browse(res[0][0])
raise ValidationError(
_("%s overlaps %s") % (this.name, dt.name))
@api.multi
def get_domain(self, field_name):
self.ensure_one()
return [(field_name, '>=', self.date_start),
(field_name, '<=', self.date_end)]
@api.multi
@api.onchange('company_id', 'type_id', 'date_start', 'date_end')
def onchange_type_id(self):
"""The type_id and the dates determine the choices for parent."""
domain = []
if self.company_id:
domain.append(('company_id', '=', self.company_id.id))
if self.parent_type_id:
domain.append(('type_id', '=', self.parent_type_id.id))
if self.date_start:
domain.append('|')
domain.append(('date_start', '<=', self.date_start))
domain.append(('date_start', '=', False))
if self.date_end:
domain.append('|')
domain.append(('date_end', '>=', self.date_end))
domain.append(('date_end', '=', False))
if domain:
# If user did not select a parent already, autoselect the last
# (ordered by date_start) or only parent that applies.
if self.type_id and self.date_start and not self.parent_id:
possible_parent = self.search(
domain, limit=1, order='date_start desc')
self.parent_id = possible_parent # can be empty!
return {'domain': {'parent_id': domain}}