server-ux/date_range/models/date_range_search_mixin.py

107 lines
3.9 KiB
Python
Raw Permalink Normal View History

2021-12-05 03:14:22 +00:00
# Copyright 2021 Opener B.V. <stefan@opener.amsterdam>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from lxml import etree
from flectra import _, api, fields, models
from flectra.osv.expression import FALSE_DOMAIN, NEGATIVE_TERM_OPERATORS, TRUE_DOMAIN
class DateRangeSearchMixin(models.AbstractModel):
_name = "date.range.search.mixin"
_description = "Mixin class to add a Many2one style period search field"
_date_range_search_field = "date"
date_range_search_id = fields.Many2one(
comodel_name="date.range",
string="Filter by period (technical field)",
compute="_compute_date_range_search_id",
search="_search_date_range_search_id",
)
def _compute_date_range_search_id(self):
"""Assign a dummy value for this search field"""
for record in self:
record.date_range_search_id = False
@api.model
def _search_date_range_search_id(self, operator, value):
"""Map the selected date ranges to the model's date field"""
# Deal with some bogus values
if not value:
if operator in NEGATIVE_TERM_OPERATORS:
return TRUE_DOMAIN
return FALSE_DOMAIN
if value is True:
if operator in NEGATIVE_TERM_OPERATORS:
return FALSE_DOMAIN
return TRUE_DOMAIN
# Assume from here on that the value is a string,
# a single id or a list of ids
ranges = self.env["date.range"]
if isinstance(value, str):
ranges = self.env["date.range"].search([("name", operator, value)])
else:
if isinstance(value, int):
value = [value]
sub_op = "not in" if operator in NEGATIVE_TERM_OPERATORS else "in"
ranges = self.env["date.range"].search([("id", sub_op, value)])
if not ranges:
return FALSE_DOMAIN
domain = (len(ranges) - 1) * ["|"] + sum(
(
[
"&",
(self._date_range_search_field, ">=", date_range.date_start),
(self._date_range_search_field, "<=", date_range.date_end),
]
for date_range in ranges
),
[],
)
return domain
@api.model
def fields_view_get(
self, view_id=None, view_type="form", toolbar=False, submenu=False
):
"""Inject the dummy Many2one field in the search view"""
result = super().fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
)
if view_type != "search":
return result
root = etree.fromstring(result["arch"])
if root.xpath("//field[@name='date_range_search_id']"):
# Field was inserted explicitely
return result
separator = etree.Element("separator")
field = etree.Element(
"field",
attrib={
"name": "date_range_search_id",
"string": _("Period"),
},
)
groups = root.xpath("/search/group")
if groups:
groups[0].addprevious(separator)
groups[0].addprevious(field)
else:
search = root.xpath("/search")
search[0].append(separator)
search[0].append(field)
result["arch"] = etree.tostring(root, encoding="unicode")
return result
@api.model
def load_views(self, views, options=None):
"""Adapt the label of the dummy search field
Ensure the technical name does not show up in the Custom Filter
fields list (while still showing up in the Export widget)
"""
result = super().load_views(views, options=options)
if "date_range_search_id" in result["fields"]:
result["fields"]["date_range_search_id"]["string"] = _("Period")
return result