From 3bc94e52cec310154e7eac7c85797fad31925308 Mon Sep 17 00:00:00 2001 From: "Pierre Pulinckx (pipu)" Date: Mon, 31 Jul 2023 11:38:58 +0000 Subject: [PATCH] [REF] *:Replace moment usages with luxon luxon and moment are both used in the solution, but these two libraries facilitate the manipulation of dates. It was decided to replace all uses of moment with luxon so we can then remove moment.js from the code and lighten the assets. task-3391739 closes odoo/odoo#128752 Signed-off-by: Bruno Boi (boi) --- .../tests/helpers/mock_python_environment.js | 6 ++++-- .../static/src/activity/activity_menu_patch.js | 11 ++++------- .../helpers/mock_server/models/discuss_channel.js | 3 ++- .../helpers/mock_server/models/mail_activity.js | 8 ++++---- .../app/screens/ticket_screen/ticket_screen.js | 6 +++--- .../point_of_sale/static/src/app/store/models.js | 8 ++++---- .../sale_order_row/sale_order_row.js | 3 ++- addons/project/static/src/js/portal_rating.js | 6 +++--- addons/test_mail/static/tests/activity_tests.js | 2 +- addons/web/static/src/core/l10n/dates.js | 2 ++ .../static/src/js/editor/snippets.options.js | 3 ++- .../static/src/js/crm_partner_assign.js | 15 +++++++++------ 12 files changed, 40 insertions(+), 33 deletions(-) diff --git a/addons/bus/static/tests/helpers/mock_python_environment.js b/addons/bus/static/tests/helpers/mock_python_environment.js index a4a22456089..b462daf1df6 100644 --- a/addons/bus/static/tests/helpers/mock_python_environment.js +++ b/addons/bus/static/tests/helpers/mock_python_environment.js @@ -6,6 +6,8 @@ import { registry } from "@web/core/registry"; import { registerCleanup } from "@web/../tests/helpers/cleanup"; import { makeMockServer } from "@web/../tests/helpers/mock_server"; import core from "@web/legacy/js/services/core"; +import { serializeDateTime, serializeDate } from "@web/core/l10n/dates"; +const { DateTime } = luxon; const modelDefinitionsPromise = new Promise((resolve) => { QUnit.begin(() => resolve(getModelDefinitions())); @@ -56,8 +58,8 @@ async function getModelDefinitions() { if (["date", "datetime"].includes(field.type) && !field.default) { const defaultFieldValue = field.type === "date" - ? () => moment.utc().format("YYYY-MM-DD") - : () => moment.utc().format("YYYY-MM-DD HH:mm:ss"); + ? () => serializeDate(DateTime.utc()) + : () => serializeDateTime(DateTime.utc()); field.default = defaultFieldValue; } else if (fname === "active" && !("default" in field)) { // records are active by default. diff --git a/addons/calendar/static/src/activity/activity_menu_patch.js b/addons/calendar/static/src/activity/activity_menu_patch.js index 5979974b2ac..3a7b27c8e2b 100644 --- a/addons/calendar/static/src/activity/activity_menu_patch.js +++ b/addons/calendar/static/src/activity/activity_menu_patch.js @@ -2,8 +2,8 @@ import { ActivityMenu } from "@mail/core/web/activity_menu"; import { patch } from "@web/core/utils/patch"; -import fieldUtils from "@web/legacy/js/fields/field_utils"; -import time from "@web/legacy/js/core/time"; +import { deserializeDateTime, formatDateTime } from "@web/core/l10n/dates"; +import { localization } from "@web/core/l10n/localization"; patch(ActivityMenu.prototype, "calendar", { async fetchSystrayActivities() { @@ -12,11 +12,8 @@ patch(ActivityMenu.prototype, "calendar", { if (group.type === "meeting") { for (const meeting of group.meetings) { if (meeting.start) { - meeting.formattedStart = moment( - fieldUtils.parse.datetime(meeting.start, false, { isUTC: true }) - ) - .local() - .format(time.getLangTimeFormat()); + const date = deserializeDateTime(meeting.start); + meeting.formattedStart = formatDateTime(date, { format: localization.timeFormat }); } } } diff --git a/addons/mail/static/tests/helpers/mock_server/models/discuss_channel.js b/addons/mail/static/tests/helpers/mock_server/models/discuss_channel.js index c1f5e04ca9e..6b8fa0b1478 100644 --- a/addons/mail/static/tests/helpers/mock_server/models/discuss_channel.js +++ b/addons/mail/static/tests/helpers/mock_server/models/discuss_channel.js @@ -7,6 +7,7 @@ import { datetime_to_str } from "@web/legacy/js/core/time"; import { assignDefined } from "@mail/utils/common/misc"; import { formatDate } from "@web/core/l10n/dates"; import { Command } from "@mail/../tests/helpers/command"; +const { DateTime } = luxon; patch(MockServer.prototype, "mail/models/discuss_channel", { async _performRPC(route, args) { @@ -953,7 +954,7 @@ patch(MockServer.prototype, "mail/models/discuss_channel", { */ _mockDiscussChannelWriteImage128(id) { this.pyEnv["discuss.channel"].write([id], { - avatarCacheKey: moment.utc().format("YYYYMMDDHHmmss"), + avatarCacheKey: DateTime.utc().toFormat("yyyyMMddHHmmss"), }); const channel = this.pyEnv["discuss.channel"].searchRead([["id", "=", id]])[0]; this.pyEnv["bus.bus"]._sendone(channel, "mail.record/insert", { diff --git a/addons/mail/static/tests/helpers/mock_server/models/mail_activity.js b/addons/mail/static/tests/helpers/mock_server/models/mail_activity.js index 901526c04ba..6442629d3c3 100644 --- a/addons/mail/static/tests/helpers/mock_server/models/mail_activity.js +++ b/addons/mail/static/tests/helpers/mock_server/models/mail_activity.js @@ -3,7 +3,7 @@ import { sortBy } from "@web/core/utils/arrays"; import { patch } from "@web/core/utils/patch"; import { MockServer } from "@web/../tests/helpers/mock_server"; -import { serializeDate } from "@web/core/l10n/dates"; +import { serializeDate, deserializeDate } from "@web/core/l10n/dates"; const { DateTime} = luxon; @@ -140,7 +140,7 @@ patch(MockServer.prototype, "mail/models/mail_activity", { group.activity_type_id = group.activity_type_id[0]; let minDate; activites.forEach(function (activity) { - if (!minDate || moment(activity.date_deadline) < moment(minDate)) { + if (!minDate || deserializeDate(activity.date_deadline) < deserializeDate(minDate)) { minDate = activity.date_deadline; } }); @@ -149,7 +149,7 @@ patch(MockServer.prototype, "mail/models/mail_activity", { let state; if (group.date_deadline === serializeDate(DateTime.now())) { state = "today"; - } else if (moment(group.date_deadline) > moment()) { + } else if (deserializeDate(group.date_deadline) > DateTime.now()) { state = "planned"; } else { state = "overdue"; @@ -183,7 +183,7 @@ patch(MockServer.prototype, "mail/models/mail_activity", { }), activity_res_ids: sortBy( records.map((x) => x.id), - (id) => moment(resIdToDeadline[id]) + (id) => deserializeDate(resIdToDeadline[id]) ), grouped_activities: groupedActivities, }; diff --git a/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js b/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js index fdc322a8b28..3931ff3e80d 100644 --- a/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js +++ b/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js @@ -3,7 +3,7 @@ import { Order } from "@point_of_sale/app/store/models"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { deserializeDateTime } from "@web/core/l10n/dates"; +import { deserializeDateTime, deserializeDate } from "@web/core/l10n/dates"; import { parseFloat } from "@web/views/fields/parsers"; import { _lt } from "@web/core/l10n/translation"; @@ -333,7 +333,7 @@ export class TicketScreen extends Component { return this._getOrderList().filter(predicate); } getDate(order) { - return moment(order.validation_date).format("YYYY-MM-DD hh:mm A"); + return deserializeDate(order.validation_date).toFormat("yyyy-MM-dd HH:mm a"); } getTotal(order) { return this.env.utils.formatCurrency(order.get_total_with_tax()); @@ -580,7 +580,7 @@ export class TicketScreen extends Component { modelField: "pos_reference", }, DATE: { - repr: (order) => moment(order.creation_date).format("YYYY-MM-DD hh:mm A"), + repr: (order) => deserializeDate(order.creation_date).toFormat("yyyy-MM-dd HH:mm a"), displayName: this.env._t("Date"), modelField: "date_order", }, diff --git a/addons/point_of_sale/static/src/app/store/models.js b/addons/point_of_sale/static/src/app/store/models.js index 8d9a4fbf183..ee6dbde4d62 100644 --- a/addons/point_of_sale/static/src/app/store/models.js +++ b/addons/point_of_sale/static/src/app/store/models.js @@ -4,7 +4,7 @@ import { formatFloat } from "@web/views/fields/formatters"; import { uuidv4 } from "@point_of_sale/utils"; // FIXME POSREF - unify use of native parseFloat and web's parseFloat. We probably don't need the native version. import { parseFloat as oParseFloat } from "@web/views/fields/parsers"; -import { formatDate, formatDateTime, serializeDateTime } from "@web/core/l10n/dates"; +import { formatDate, formatDateTime, serializeDateTime, deserializeDate } from "@web/core/l10n/dates"; import { roundDecimals as round_di, roundPrecision as round_pr, @@ -227,8 +227,8 @@ export class Product extends PosModel { const categories = this.parent_category_ids.concat(this.categ.id); return ( (!item.categ_id || categories.includes(item.categ_id[0])) && - (!item.date_start || moment.utc(item.date_start).isSameOrBefore(date)) && - (!item.date_end || moment.utc(item.date_end).isSameOrAfter(date)) + (!item.date_start || deserializeDate(item.date_start) <= date) && + (!item.date_end || deserializeDate(item.date_end) >= date) ); } // Port of _get_product_price on product.pricelist. @@ -242,7 +242,7 @@ export class Product extends PosModel { // and were automatically sorted based on their _order by the // ORM. After that they are added in this order to the pricelists. get_price(pricelist, quantity, price_extra = 0, recurring = false) { - var date = moment(); + const date = DateTime.now(); // In case of nested pricelists, it is necessary that all pricelists are made available in // the POS. Display a basic alert to the user in the case where there is a pricelist item diff --git a/addons/pos_sale/static/src/app/order_management_screen/sale_order_row/sale_order_row.js b/addons/pos_sale/static/src/app/order_management_screen/sale_order_row/sale_order_row.js index 2ea1e314515..5dc991bd83a 100644 --- a/addons/pos_sale/static/src/app/order_management_screen/sale_order_row/sale_order_row.js +++ b/addons/pos_sale/static/src/app/order_management_screen/sale_order_row/sale_order_row.js @@ -1,6 +1,7 @@ /** @odoo-module */ import { Component, useState } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; +import { deserializeDateTime } from "@web/core/l10n/dates"; /** * @props {models.Order} order @@ -29,7 +30,7 @@ export class SaleOrderRow extends Component { return this.order.name; } get date() { - return moment(this.order.date_order).format("YYYY-MM-DD hh:mm A"); + return deserializeDateTime(this.order.date_order).toFormat("yyyy-MM-dd HH:mm a"); } get partner() { const partner = this.order.partner_id; diff --git a/addons/project/static/src/js/portal_rating.js b/addons/project/static/src/js/portal_rating.js index 9c65101cd2d..6cc7ff0b1a6 100644 --- a/addons/project/static/src/js/portal_rating.js +++ b/addons/project/static/src/js/portal_rating.js @@ -1,7 +1,7 @@ /** @odoo-module **/ -import time from '@web/legacy/js/core/time'; import publicWidget from '@web/legacy/js/public/public_widget'; +import { parseDate } from '@web/core/l10n/dates'; publicWidget.registry.ProjectRatingImage = publicWidget.Widget.extend({ selector: '.o_portal_project_rating .o_rating_image', @@ -18,8 +18,8 @@ publicWidget.registry.ProjectRatingImage = publicWidget.Widget.extend({ var $elem = $(this); var id = $elem.data('id'); var ratingDate = $elem.data('rating-date'); - var baseDate = time.auto_str_to_date(ratingDate); - var duration = moment(baseDate).fromNow(); + var baseDate = parseDate(ratingDate); + var duration = baseDate.toRelative(); var $rating = $('#rating_' + id); $rating.find('.rating_timeduration').text(duration); return $rating.html(); diff --git a/addons/test_mail/static/tests/activity_tests.js b/addons/test_mail/static/tests/activity_tests.js index 464ceb4b915..28ac785567f 100644 --- a/addons/test_mail/static/tests/activity_tests.js +++ b/addons/test_mail/static/tests/activity_tests.js @@ -89,7 +89,7 @@ QUnit.module("test_mail", {}, function () { }); var activityDateFormat = function (date) { - return date.toLocaleDateString(moment().locale(), { day: "numeric", month: "short" }); + return DateTime.now().toLocaleString({ day: "numeric", month: "short" }); }; QUnit.test("activity view: simple activity rendering", async function (assert) { diff --git a/addons/web/static/src/core/l10n/dates.js b/addons/web/static/src/core/l10n/dates.js index 09567e9ff74..374172c3e48 100644 --- a/addons/web/static/src/core/l10n/dates.js +++ b/addons/web/static/src/core/l10n/dates.js @@ -297,6 +297,7 @@ export function formatDateTime(value, options = {}) { /** * Formats the given DateTime to the server date format. * @param {DateTime} value + * @returns {string} */ export function serializeDate(value) { if (!dateCache.has(value)) { @@ -308,6 +309,7 @@ export function serializeDate(value) { /** * Formats the given DateTime to the server datetime format. * @param {DateTime} value + * @returns {string} */ export function serializeDateTime(value) { if (!dateTimeCache.has(value)) { diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 26c19a6367c..ba963f6a70f 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -49,6 +49,7 @@ var _t = core._t; const _lt = core._lt; const preserveCursor = OdooEditorLib.preserveCursor; const descendants = OdooEditorLib.descendants; +const { DateTime } = luxon; /** * @param {HTMLElement} el @@ -1849,7 +1850,7 @@ const DatetimePickerUserValueWidget = InputUserValueWidget.extend({ */ init: function () { this._super(...arguments); - this._value = moment().unix().toString(); + this._value = DateTime.now().toUnixInteger().toString(); this.__libInput = 0; }, /** diff --git a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js index 084c3a44fda..333c303bc56 100644 --- a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js +++ b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js @@ -4,6 +4,8 @@ import { sprintf } from "@web/core/utils/strings"; import {_t} from "@web/legacy/js/services/core"; import publicWidget from "@web/legacy/js/public/public_widget"; import time from "@web/legacy/js/core/time"; +import { parseDate, formatDate } from "@web/core/l10n/dates"; +const { DateTime } = luxon; publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ selector: '#wrapwrap:has(.interested_partner_assign_form, .desinterested_partner_assign_form, .opp-stage-button, .new_opp_form)', @@ -252,9 +254,10 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ $('.edit_opp_form .activity_summary').val($selected.attr('activity_summary')); } if ($selected.attr('delay_count')) { - var now = moment(); - var date = now.add(parseInt($selected.attr('delay_count')), $selected.attr('delay_unit')); - $('.edit_opp_form .activity_date_deadline').val(date.format(time.getLangDateFormat())); + const value = +$selected.attr('delay_count'); + const unit = $selected.attr('delay_unit'); + const date = DateTime.now().plus({ [unit]: value}); + $('.edit_opp_form .activity_date_deadline').val( formatDate(date) ); } }, /** @@ -277,9 +280,9 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ _parse_date: function (value) { console.log(value); - var date = moment(value, time.getLangDateFormat(), true); - if (date.isValid() && date.year() >= 1900) { - return time.date_to_str(date.toDate()); + var date = parseDate(value); + if (date.isValid && date.year >= 1900) { + return formatDate(date); } else { return false;