diff --git a/setup/web_time_range_menu_custom/odoo/addons/web_time_range_menu_custom b/setup/web_time_range_menu_custom/odoo/addons/web_time_range_menu_custom new file mode 120000 index 000000000000..ac6f76901ab0 --- /dev/null +++ b/setup/web_time_range_menu_custom/odoo/addons/web_time_range_menu_custom @@ -0,0 +1 @@ +../../../../web_time_range_menu_custom \ No newline at end of file diff --git a/setup/web_time_range_menu_custom/setup.py b/setup/web_time_range_menu_custom/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_time_range_menu_custom/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/web_time_range_menu_custom/README.rst b/web_time_range_menu_custom/README.rst new file mode 100644 index 000000000000..f3da3a2a0cb2 --- /dev/null +++ b/web_time_range_menu_custom/README.rst @@ -0,0 +1,100 @@ +========================== +Web Time Range Menu Custom +========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a7d5190a01dc1568ba62760ae6498f4385a83f98164c3012312979ca764ff49f + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/16.0/web_time_range_menu_custom + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_time_range_menu_custom + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Extend period and comparison period options for the date and datetime fields filted menu +adding a new option called "Custom Period". + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To see this module working: + +#. Navigate to any menu that supports date-based filters. +#. Open **Custom period** tab. +#. Add new date filter with the provided options. + +.. image:: https://raw.githubusercontent.com/web_time_range_menu_custom/static/src/description/custom_period_option.png + :alt: Custom Period Option + +For the pivots, on the comparison tab you can see the same option to make the +comparison with the provided filter, taking th reference from the set filter. + +**Note:** For "days," it functions as a range; for example, "Last 7 days" returns the +period from 7 days ago up to today. However, for other options, it provides the +range for the selected period. For instance, "Last 1 month" returns the period from +the first day of the previous month to the last day of the previous month. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `__: + + * Alexandre D. Díaz + * Carlos Roca + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_time_range_menu_custom/__init__.py b/web_time_range_menu_custom/__init__.py new file mode 100644 index 000000000000..ef5ae3587f59 --- /dev/null +++ b/web_time_range_menu_custom/__init__.py @@ -0,0 +1 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). diff --git a/web_time_range_menu_custom/__manifest__.py b/web_time_range_menu_custom/__manifest__.py new file mode 100644 index 000000000000..7f5e6bc10306 --- /dev/null +++ b/web_time_range_menu_custom/__manifest__.py @@ -0,0 +1,19 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +{ + "name": "Web Time Range Menu Custom", + "version": "16.0.1.0.0", + "author": "Tecnativa, Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/web", + "depends": ["web"], + "installable": True, + "auto_install": False, + "assets": { + "web.assets_backend": [ + "/web_time_range_menu_custom/static/src/js/*.esm.js", + "/web_time_range_menu_custom/static/src/scss/*.scss", + "/web_time_range_menu_custom/static/src/xml/*.xml", + ], + }, +} diff --git a/web_time_range_menu_custom/i18n/es.po b/web_time_range_menu_custom/i18n/es.po new file mode 100644 index 000000000000..dbddd83656f0 --- /dev/null +++ b/web_time_range_menu_custom/i18n/es.po @@ -0,0 +1,87 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_time_range_menu_custom +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-11-19 19:33+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Add" +msgstr "Añadir" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Custom period" +msgstr "Período personalizado" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Day" +msgstr "Día" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Month" +msgstr "Mes" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Week" +msgstr "Semana" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Year" +msgstr "Año" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "day" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "month" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "week" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "year" +msgstr "" diff --git a/web_time_range_menu_custom/i18n/web_time_range_menu_custom.pot b/web_time_range_menu_custom/i18n/web_time_range_menu_custom.pot new file mode 100644 index 000000000000..013a4e45f438 --- /dev/null +++ b/web_time_range_menu_custom/i18n/web_time_range_menu_custom.pot @@ -0,0 +1,84 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_time_range_menu_custom +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Add" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Custom period" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Day" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Month" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Week" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "Year" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "day" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "month" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "week" +msgstr "" + +#. module: web_time_range_menu_custom +#. openerp-web +#: code:addons/web_time_range_menu_custom/static/src/xml/date_selector.xml:0 +#, python-format +msgid "year" +msgstr "" diff --git a/web_time_range_menu_custom/readme/CONTRIBUTORS.rst b/web_time_range_menu_custom/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..0b0b82ce109a --- /dev/null +++ b/web_time_range_menu_custom/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Tecnativa `__: + + * Alexandre D. Díaz + * Carlos Roca diff --git a/web_time_range_menu_custom/readme/DESCRIPTION.rst b/web_time_range_menu_custom/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..6dca00fe89c4 --- /dev/null +++ b/web_time_range_menu_custom/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +Extend period and comparison period options for the date and datetime fields filted menu +adding a new option called "Custom Period". diff --git a/web_time_range_menu_custom/readme/USAGE.rst b/web_time_range_menu_custom/readme/USAGE.rst new file mode 100644 index 000000000000..d975ed048de6 --- /dev/null +++ b/web_time_range_menu_custom/readme/USAGE.rst @@ -0,0 +1,16 @@ +To see this module working: + +#. Navigate to any menu that supports date-based filters. +#. Open **Custom period** tab. +#. Add new date filter with the provided options. + +.. image:: /web_time_range_menu_custom/static/src/description/custom_period_option.png + :alt: Custom Period Option + +For the pivots, on the comparison tab you can see the same option to make the +comparison with the provided filter, taking th reference from the set filter. + +**Note:** For "days," it functions as a range; for example, "Last 7 days" returns the +period from 7 days ago up to today. However, for other options, it provides the +range for the selected period. For instance, "Last 1 month" returns the period from +the first day of the previous month to the last day of the previous month. \ No newline at end of file diff --git a/web_time_range_menu_custom/static/description/custom_period_option.png b/web_time_range_menu_custom/static/description/custom_period_option.png new file mode 100644 index 000000000000..c19c93488adb Binary files /dev/null and b/web_time_range_menu_custom/static/description/custom_period_option.png differ diff --git a/web_time_range_menu_custom/static/description/icon.png b/web_time_range_menu_custom/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/web_time_range_menu_custom/static/description/icon.png differ diff --git a/web_time_range_menu_custom/static/description/index.html b/web_time_range_menu_custom/static/description/index.html new file mode 100644 index 000000000000..835902f6dad3 --- /dev/null +++ b/web_time_range_menu_custom/static/description/index.html @@ -0,0 +1,445 @@ + + + + + +Web Time Range Menu Custom + + + +
+

Web Time Range Menu Custom

+ + +

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

+

Extend period and comparison period options for the date and datetime fields filted menu +adding a new option called “Custom Period”.

+

Table of contents

+ +
+

Usage

+

To see this module working:

+
    +
  1. Navigate to any menu that supports date-based filters.
  2. +
  3. Open Custom period tab.
  4. +
  5. Add new date filter with the provided options.
  6. +
+Custom Period Option +

For the pivots, on the comparison tab you can see the same option to make the +comparison with the provided filter, taking th reference from the set filter.

+

Note: For “days,” it functions as a range; for example, “Last 7 days” returns the +period from 7 days ago up to today. However, for other options, it provides the +range for the selected period. For instance, “Last 1 month” returns the period from +the first day of the previous month to the last day of the previous month.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+
    +
  • Tecnativa:
      +
    • Alexandre D. Díaz
    • +
    • Carlos Roca
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_time_range_menu_custom/static/src/js/comparison_menu.esm.js b/web_time_range_menu_custom/static/src/js/comparison_menu.esm.js new file mode 100644 index 000000000000..d6dcb0b988f3 --- /dev/null +++ b/web_time_range_menu_custom/static/src/js/comparison_menu.esm.js @@ -0,0 +1,13 @@ +/** @odoo-module **/ +/* Copyright 2024 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ +import {ComparisonMenu} from "@web/search/comparison_menu/comparison_menu"; +import {DropdownItemCustomPeriod} from "./date_selector.esm"; +import {patch} from "@web/core/utils/patch"; + +patch(ComparisonMenu, "web_time_range_menu_custom.FilterMenu", { + components: { + ...ComparisonMenu.components, + DropdownItemCustomPeriod, + }, +}); diff --git a/web_time_range_menu_custom/static/src/js/date_selector.esm.js b/web_time_range_menu_custom/static/src/js/date_selector.esm.js new file mode 100644 index 000000000000..2fb1f62a4e2f --- /dev/null +++ b/web_time_range_menu_custom/static/src/js/date_selector.esm.js @@ -0,0 +1,121 @@ +/** @odoo-module **/ +/* Copyright 2022 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ +import {useBus} from "@web/core/utils/hooks"; + +const {Component, useState} = owl; +import * as dates from "@web/search/utils/dates"; +const {DateTime} = luxon; // eslint-disable-line no-undef +var ID_CUSTOM_DATE = 0; + +/** + * @extends Component + */ +export class DropdownItemCustomPeriod extends Component { + setup() { + this.isOpen = useState({value: false}); + this.type = useState({value: "day"}); + this.quantity = useState({value: 0}); + this.referenceMoment = DateTime.local(); + var fieldsSelected = new Set(); + for (var selected in this.props.comparisonItems) { + fieldsSelected.add(this.props.comparisonItems[selected].dateFilterId); + } + this.fields_selected = Array.from(fieldsSelected); + this.field = useState({ + value: (this.props.field && this.props.field.id) || this.fields_selected[0], + }); + + useBus(this.env.searchModel, "update", this.render); + } + + onClickCustomPeriod() { + this.isOpen.value = !this.isOpen.value; + } + + onClickAdd() { + if (this.props.type === "filter") { + this.addRange(); + } else if (this.props.type === "comparison") { + this.addComparison(); + } + } + + addRange() { + const field_id = this.field.value; + const key = "custom_" + ID_CUSTOM_DATE++; + const plusParamID = this.type.value + "s"; + var formatSearch = ""; + switch (this.type.value) { + case "day": + formatSearch = "dd MMMM yyyy"; + break; + case "week": + formatSearch = "'W'WW yyyy"; + break; + case "month": + formatSearch = "MMMM"; + break; + case "year": + formatSearch = "yyyy"; + break; + } + const periodSearch = { + [key]: { + id: key, + groupNumber: 3, + description: + "Last " + this.quantity.value + " " + this.type.value + "s", + format: formatSearch, + plusParam: {[plusParamID]: -this.quantity.value}, + granularity: this.type.value, + custom_period: { + is_custom_period: true, + last_period: this.quantity.value, + }, + }, + }; + Object.assign(dates.PERIOD_OPTIONS, periodSearch); + this.env.searchModel.optionGenerators = dates.getPeriodOptions( + this.referenceMoment + ); + this.env.searchModel.toggleDateFilter(field_id, key); + } + + addComparison() { + const key = "custom_" + ID_CUSTOM_DATE++; + const plusParamID = this.type.value + "s"; + Object.assign(dates.COMPARISON_OPTIONS, { + [key]: { + description: + "Previous " + this.quantity.value + " " + this.type.value + "s", + id: key, + plusParam: {[plusParamID]: -this.quantity.value}, + }, + }); + const comparisonId = this.env.searchModel.nextId; + this.env.searchModel.searchItems[comparisonId] = { + comparisonOptionId: key, + dateFilterId: this.field.value, + description: + this.env.searchModel.searchItems[this.field.value].description + + ": Previous " + + this.quantity.value + + " " + + this.type.value + + "s", + groupId: 14, + id: comparisonId, + type: "comparison", + }; + this.env.searchModel.nextId++; + this.env.searchModel.toggleSearchItem(comparisonId); + } +} +DropdownItemCustomPeriod.template = + "web_time_range_menu_custom.DropdownItemCustomPeriod"; +DropdownItemCustomPeriod.props = { + type: {type: String}, + field: {type: Object, optional: true}, + comparisonItems: {type: Object, optional: true}, +}; diff --git a/web_time_range_menu_custom/static/src/js/dates.esm.js b/web_time_range_menu_custom/static/src/js/dates.esm.js new file mode 100644 index 000000000000..f5be25fd641a --- /dev/null +++ b/web_time_range_menu_custom/static/src/js/dates.esm.js @@ -0,0 +1,363 @@ +/** @odoo-module **/ +/* eslint-disable init-declarations */ +/* Copyright 2022 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ + +import {patch} from "@web/core/utils/patch"; +import {Domain} from "@web/core/domain"; +import {serializeDate, serializeDateTime} from "@web/core/l10n/dates"; +import {localization} from "@web/core/l10n/localization"; +/* Redefine some methods of dates.js from web.static.src.utils to +add support to days and weeks periods */ +import * as dates from "@web/search/utils/dates"; + +/** + * Method used to calculate the quantity of days and weeks of the + * actual year. + */ +function _getQtyOfCurrentYear(option) { + const now = new Date(); + const startOfYear = new Date(now.getFullYear(), 0, 1); + const endOfYear = new Date(now.getFullYear(), 11, 31); + let millisecondsPerWeek = 0; + if (option === "week") { + millisecondsPerWeek = 604800000; + } else if (option === "day") { + millisecondsPerWeek = 86400000; + } else { + return; + } + const weeks = Math.ceil((endOfYear - startOfYear) / millisecondsPerWeek); + return weeks; +} + +Object.assign(dates.PER_YEAR, { + week: _getQtyOfCurrentYear("week"), + day: _getQtyOfCurrentYear("day"), +}); + +// This is needed to call the super functions on @web/search/utils/dates +const _getSetParam = dates.getSetParam; + +// Patch of functions defined before +patch(dates, "patch dates", { + /* + * Redefine function to avoid the exclusion of days and weeks. + */ + constructDateDomain( + referenceMoment, + fieldName, + fieldType, + selectedOptionIds, + comparisonOptionId + ) { + let plusParam; + let selectedOptions; + if (comparisonOptionId) { + [plusParam, selectedOptions] = dates.getComparisonParams( + referenceMoment, + selectedOptionIds, + comparisonOptionId + ); + } else { + selectedOptions = dates.getSelectedOptions( + referenceMoment, + selectedOptionIds + ); + } + const yearOptions = selectedOptions.year; + const otherOptions = [ + ...(selectedOptions.quarter || []), + ...(selectedOptions.month || []), + ...(selectedOptions.day || []), + ...(selectedOptions.week || []), + ]; + dates.sortPeriodOptions(yearOptions); + dates.sortPeriodOptions(otherOptions); + const ranges = []; + for (const yearOption of yearOptions) { + const constructRangeParams = { + referenceMoment, + fieldName, + fieldType, + plusParam, + }; + if (otherOptions.length) { + for (const option of otherOptions) { + const year_param = + option.granularity === "week" + ? {weekYear: yearOption.setParam.year} + : yearOption.setParam; + const setParam = Object.assign( + {}, + year_param, + option ? option.setParam : {} + ); + const {granularity, custom_period} = option; + if (comparisonOptionId && custom_period.is_custom_period) { + custom_period.is_comparison = true; + } + const range = dates.constructDateRange( + Object.assign( + {granularity, custom_period, setParam}, + constructRangeParams + ) + ); + ranges.push(range); + } + } else { + const {granularity, custom_period, setParam} = yearOption; + if (comparisonOptionId && custom_period.is_custom_period) { + custom_period.is_comparison = true; + } + const range = dates.constructDateRange( + Object.assign( + {granularity, custom_period, setParam}, + constructRangeParams + ) + ); + ranges.push(range); + } + } + const domain = Domain.combine( + ranges.map((range) => range.domain), + "OR" + ); + const description = ranges.map((range) => range.description).join("/"); + return {domain, description}; + }, + constructDateRange(params) { + const { + referenceMoment, + fieldName, + fieldType, + granularity, + setParam, + plusParam, + custom_period, + } = params; + if ("quarter" in setParam) { + // Luxon does not consider quarter key in setParam (like moment did) + setParam.month = dates.QUARTERS[setParam.quarter].coveredMonths[0]; + delete setParam.quarter; + } + const date = referenceMoment.set(setParam).plus(plusParam || {}); + // Compute domain + var leftDate = date.startOf(granularity); + var rightDate = date.endOf(granularity); + if ( + custom_period.is_custom_period && + parseInt(custom_period.last_period) !== 0 + ) { + if (granularity === "day") { + const plusParamReferenceMoment = referenceMoment.plus(plusParam || {}); + leftDate = leftDate.plus({days: 1}); + rightDate = plusParamReferenceMoment; + } else { + var customPlusParam = {}; + customPlusParam[granularity + "s"] = parseInt( + custom_period.last_period + ); + rightDate = leftDate.plus(customPlusParam); + } + } + let leftBound; + let rightBound; + if (fieldType === "date") { + leftBound = serializeDate(leftDate); + rightBound = serializeDate(rightDate); + } else { + leftBound = serializeDateTime(leftDate); + rightBound = serializeDateTime(rightDate); + } + const domain = new Domain([ + "&", + [fieldName, ">=", leftBound], + [fieldName, "<=", rightBound], + ]); + // Compute description + var description = ""; + if (custom_period.is_custom_period) { + if (plusParam) { + const key = Object.keys(plusParam)[0]; + description = "Previous " + Math.abs(plusParam[key]) + " " + key; + } else { + description = + "Last " + custom_period.last_period + " " + granularity + "s"; + } + } else { + var descriptions = [date.toFormat("yyyy")]; + const method = localization.direction === "rtl" ? "push" : "unshift"; + if (granularity === "month") { + descriptions[method](date.toFormat("MMMM")); + } else if (granularity === "quarter") { + const quarter = date.quarter; + descriptions[method](dates.QUARTERS[quarter].description.toString()); + } else if (granularity === "day") { + descriptions[method](date.toFormat("dd MMMM")); + } else if (granularity === "week") { + descriptions[method](date.toFormat("'W'WW")); + } + description = descriptions.join(" "); + } + return {domain, description}; + }, + /* + * Redefine function to allow process days and weeks. + */ + getPeriodOptions(referenceMoment) { + const options = []; + const originalOptions = Object.values(dates.PERIOD_OPTIONS); + for (const option of originalOptions) { + const {id, groupNumber} = option; + let description; + let defaultYear; + switch (option.granularity) { + case "quarter": + description = option.description.toString(); + defaultYear = referenceMoment.set(option.setParam).year; + break; + case "day": + case "week": + case "month": + case "year": + const date = referenceMoment.plus(option.plusParam); + description = option.description || date.toFormat(option.format); + defaultYear = date.year; + break; + } + const setParam = dates.getSetParam(option, referenceMoment); + options.push({id, groupNumber, description, defaultYear, setParam}); + } + const periodOptions = []; + for (const option of options) { + const {id, groupNumber, description, defaultYear} = option; + const yearOption = options.find( + (o) => o.setParam && o.setParam.year === defaultYear + ); + periodOptions.push({ + id, + groupNumber, + description, + defaultYearId: yearOption.id, + }); + } + return periodOptions; + }, + /* + * Add selection of month when week or day selected. + * + * @override + */ + getSetParam(periodOption, referenceMoment) { + if (periodOption.granularity === "day") { + const date = referenceMoment.plus(periodOption.plusParam); + return { + day: date.day, + month: date.month, + }; + } else if (periodOption.granularity === "week") { + const date = referenceMoment.plus(periodOption.plusParam); + return { + weekNumber: date.weekNumber, + }; + } + return _getSetParam(...arguments); + }, + getSelectedOptions(referenceMoment, selectedOptionIds) { + const selectedOptions = {year: []}; + for (const optionId of selectedOptionIds) { + const option = dates.PERIOD_OPTIONS[optionId]; + const setParam = dates.getSetParam(option, referenceMoment); + const custom_period = option.custom_period || {}; + const granularity = option.granularity; + if (!selectedOptions[granularity]) { + selectedOptions[granularity] = []; + } + selectedOptions[granularity].push({granularity, setParam, custom_period}); + } + return selectedOptions; + }, + /* + * Add support to day and week options. + * + */ + getComparisonParams(referenceMoment, selectedOptionIds, comparisonOptionId) { + const comparisonOption = dates.COMPARISON_OPTIONS[comparisonOptionId]; + const selectedOptions = dates.getSelectedOptions( + referenceMoment, + selectedOptionIds + ); + if (comparisonOption.plusParam) { + return [comparisonOption.plusParam, selectedOptions]; + } + const plusParam = {}; + let globalGranularity = "year"; + if (selectedOptions.day) { + globalGranularity = "day"; + } else if (selectedOptions.week) { + globalGranularity = "week"; + } else if (selectedOptions.month) { + globalGranularity = "month"; + } else if (selectedOptions.quarter) { + globalGranularity = "quarter"; + } + const granularityFactor = dates.PER_YEAR[globalGranularity]; + const years = selectedOptions.year.map((o) => o.setParam.year); + const yearMin = Math.min(...years); + const yearMax = Math.max(...years); + let optionMin = 0; + let optionMax = 0; + if (selectedOptions.quarter) { + const quarters = selectedOptions.quarter.map((o) => o.setParam.quarter); + if (globalGranularity === "month") { + delete selectedOptions.quarter; + for (const quarter of quarters) { + for (const month of dates.QUARTERS[quarter].coveredMonths) { + const monthOption = selectedOptions.month.find( + (o) => o.setParam.month === month + ); + if (!monthOption) { + selectedOptions.month.push({ + setParam: {month}, + granularity: "month", + }); + } + } + } + } else { + optionMin = Math.min(...quarters); + optionMax = Math.max(...quarters); + } + } + if (selectedOptions.month) { + const months = selectedOptions.month.map((o) => o.setParam.month); + optionMin = Math.min(...months); + optionMax = Math.max(...months); + } + if (selectedOptions.week) { + const weeks = selectedOptions.week.map((o) => o.setParam.weekNumber); + optionMin = Math.min(...weeks); + optionMax = Math.max(...weeks); + } + if (selectedOptions.day) { + const days = selectedOptions.day.map((o) => o.setParam.day); + optionMin = Math.min(...days); + optionMax = Math.max(...days); + } + const num = + -1 + granularityFactor * (yearMin - yearMax) + optionMin - optionMax; + const key = + globalGranularity === "year" + ? "years" + : globalGranularity === "month" + ? "months" + : globalGranularity === "week" + ? "weeks" + : globalGranularity === "day" + ? "days" + : "quarters"; + plusParam[key] = num; + return [plusParam, selectedOptions]; + }, +}); diff --git a/web_time_range_menu_custom/static/src/js/filter_menu.esm.js b/web_time_range_menu_custom/static/src/js/filter_menu.esm.js new file mode 100644 index 000000000000..c0999ca098d6 --- /dev/null +++ b/web_time_range_menu_custom/static/src/js/filter_menu.esm.js @@ -0,0 +1,13 @@ +/** @odoo-module **/ +/* Copyright 2024 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ +import {FilterMenu} from "@web/search/filter_menu/filter_menu"; +import {DropdownItemCustomPeriod} from "./date_selector.esm"; +import {patch} from "@web/core/utils/patch"; + +patch(FilterMenu, "web_time_range_menu_custom.FilterMenu", { + components: { + ...FilterMenu.components, + DropdownItemCustomPeriod, + }, +}); diff --git a/web_time_range_menu_custom/static/src/scss/web_time_range_menu_custom.scss b/web_time_range_menu_custom/static/src/scss/web_time_range_menu_custom.scss new file mode 100644 index 000000000000..02bdb23ee8c9 --- /dev/null +++ b/web_time_range_menu_custom/static/src/scss/web_time_range_menu_custom.scss @@ -0,0 +1,8 @@ +#add_custom_period_wrapper { + padding: 0 20px; + white-space: nowrap; + .d-table-cell { + vertical-align: middle; + padding: 3px 0; + } +} diff --git a/web_time_range_menu_custom/static/src/xml/comparison_menu.xml b/web_time_range_menu_custom/static/src/xml/comparison_menu.xml new file mode 100644 index 000000000000..e867032db7f7 --- /dev/null +++ b/web_time_range_menu_custom/static/src/xml/comparison_menu.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/web_time_range_menu_custom/static/src/xml/date_selector.xml b/web_time_range_menu_custom/static/src/xml/date_selector.xml new file mode 100644 index 000000000000..5179ef839162 --- /dev/null +++ b/web_time_range_menu_custom/static/src/xml/date_selector.xml @@ -0,0 +1,72 @@ + + + + + + + diff --git a/web_time_range_menu_custom/static/src/xml/filter_menu.xml b/web_time_range_menu_custom/static/src/xml/filter_menu.xml new file mode 100644 index 000000000000..bc13917f3c32 --- /dev/null +++ b/web_time_range_menu_custom/static/src/xml/filter_menu.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + +