From 61ae6a6ca1abd4038da11ccb0ac93cb488925f4f Mon Sep 17 00:00:00 2001 From: "Achraf (abz)" Date: Thu, 3 Dec 2020 12:13:33 +0000 Subject: [PATCH] [FIX] survey, web, web_editor, website, website_form: Prevent invalid date stock.picking What are the steps to reproduce your issue? 1. Install "stock" 2. Go to Inventory/Inventory Overview/Internal Transfer 3. Create new record and set manually a date with year < 1000 like "0008-10-10 18:35:00" 4. Save What is currently happening? An error arises when saving the record, and it is then no longer possible to reopen the records or any view containing it. This also prevents us from correcting the date to no longer have the error Why is this happening? The root cause is an inconsistency in `strftime` for years < 1000 [1, 2]. To take it into account, a zero-padding is needed in the DateTime parser. How to fix the bug? The missing padding is added. However, such dates lead to problematic behaviors in Python: ``` DT_FORMAT = "%Y-%m-%d %H:%M:%S" new_date = "0008-02-05 18:10:10" datetime.strptime( datetime.strptime( new_date, DT_FORMAT ).strftime(DT_FORMAT), DT_FORMAT ) ``` This raises: ``` ValueError: time data '8-02-05 18:10:10' does not match format '%Y-%m-%d %H:%M:%S' ``` As of today there was no reported business cases where dates with year < 1000 would be necessary. Therefore, we limit the range to years >= 1000. [1] https://bugs.python.org/issue13305 [2] https://docs.python.org/dev/library/datetime.html#strftime-strptime-behavior opw-2408260 closes odoo/odoo#62889 X-original-commit: b873bda1e560bc49ab707aa238fdc962f23bc1e4 Signed-off-by: Nicolas Martinelli (nim) Signed-off-by: Achraf --- addons/survey/static/src/js/survey_form.js | 2 +- .../static/src/js/components/datepicker.js | 2 +- .../web/static/src/js/fields/field_utils.js | 21 ++++++++++++------- .../web/static/src/js/widgets/date_picker.js | 2 +- .../static/tests/fields/field_utils_tests.js | 7 +++++-- .../static/src/js/editor/snippets.options.js | 2 +- addons/website/static/src/js/menu/content.js | 2 +- .../static/src/snippets/s_website_form/000.js | 2 +- 8 files changed, 24 insertions(+), 16 deletions(-) diff --git a/addons/survey/static/src/js/survey_form.js b/addons/survey/static/src/js/survey_form.js index 5f178b40175..e789230e06b 100644 --- a/addons/survey/static/src/js/survey_form.js +++ b/addons/survey/static/src/js/survey_form.js @@ -914,7 +914,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ var minDate = minDateData ? this._formatDateTime(minDateData, datetimepickerFormat) - : moment({ y: 1 }); + : moment({ y: 1000 }); var maxDate = maxDateData ? this._formatDateTime(maxDateData, datetimepickerFormat) diff --git a/addons/web/static/src/js/components/datepicker.js b/addons/web/static/src/js/components/datepicker.js index 6031c72fc69..a11d51b5021 100644 --- a/addons/web/static/src/js/components/datepicker.js +++ b/addons/web/static/src/js/components/datepicker.js @@ -180,7 +180,7 @@ odoo.define('web.DatePickerOwl', function (require) { }, locale: moment.locale(), maxDate: moment({ y: 9999, M: 11, d: 31 }), - minDate: moment({ y: 1 }), + minDate: moment({ y: 1000 }), useCurrent: false, widgetParent: 'body', }; diff --git a/addons/web/static/src/js/fields/field_utils.js b/addons/web/static/src/js/fields/field_utils.js index 4ffceb93408..0407da1b265 100644 --- a/addons/web/static/src/js/fields/field_utils.js +++ b/addons/web/static/src/js/fields/field_utils.js @@ -484,10 +484,12 @@ function parseDate(value, field, options) { if (date.year() === 0) { date.year(moment.utc().year()); } - date.toJSON = function () { - return this.clone().locale('en').format('YYYY-MM-DD'); - }; - return date; + if (date.year() >= 1000){ + date.toJSON = function () { + return this.clone().locale('en').format('YYYY-MM-DD'); + }; + return date; + } } throw new Error(_.str.sprintf(core._t("'%s' is not a correct date"), value)); } @@ -521,6 +523,7 @@ function parseDateTime(value, field, options) { datetime = smartDate; } else { if (options && options.isUTC) { + value = value.padStart(19, "0"); // server may send "932-10-10" for "0932-10-10" on some OS // phatomjs crash if we don't use this format datetime = moment.utc(value.replace(' ', 'T') + 'Z'); } else { @@ -534,10 +537,12 @@ function parseDateTime(value, field, options) { if (datetime.year() === 0) { datetime.year(moment.utc().year()); } - datetime.toJSON = function () { - return this.clone().locale('en').format('YYYY-MM-DD HH:mm:ss'); - }; - return datetime; + if (datetime.year() >= 1000) { + datetime.toJSON = function () { + return this.clone().locale('en').format('YYYY-MM-DD HH:mm:ss'); + }; + return datetime; + } } throw new Error(_.str.sprintf(core._t("'%s' is not a correct datetime"), value)); } diff --git a/addons/web/static/src/js/widgets/date_picker.js b/addons/web/static/src/js/widgets/date_picker.js index b989cc87355..fdbd1f7d891 100644 --- a/addons/web/static/src/js/widgets/date_picker.js +++ b/addons/web/static/src/js/widgets/date_picker.js @@ -30,7 +30,7 @@ var DateWidget = Widget.extend({ this.options = _.extend({ locale: moment.locale(), format : this.type_of_date === 'datetime' ? time.getLangDatetimeFormat() : time.getLangDateFormat(), - minDate: moment({ y: 1 }), + minDate: moment({ y: 1000 }), maxDate: moment({ y: 9999, M: 11, d: 31 }), useCurrent: false, icons: { diff --git a/addons/web/static/tests/fields/field_utils_tests.js b/addons/web/static/tests/fields/field_utils_tests.js index 567390e336d..08c5281752f 100644 --- a/addons/web/static/tests/fields/field_utils_tests.js +++ b/addons/web/static/tests/fields/field_utils_tests.js @@ -316,7 +316,7 @@ QUnit.test('parse percentage', function(assert) { }); QUnit.test('parse datetime', function (assert) { - assert.expect(6); + assert.expect(7); var originalParameters = _.clone(core._t.database.parameters); var originalLocale = moment.locale(); @@ -349,6 +349,9 @@ QUnit.test('parse datetime', function (assert) { assert.throws(function () { fieldUtils.parse.datetime("10000-01-01 12:00:00", {}, {}); }, /is not a correct/, "Dates after 9999 should be invalid"); + assert.throws(function () { + fieldUtils.parse.datetime("999-01-01 12:00:00", {}, {}); + }, /is not a correct/, "Dates before 1000 should be invalid"); dateStr = '01/13/2019 10:05:45'; date1 = fieldUtils.parse.datetime(dateStr); @@ -360,7 +363,7 @@ QUnit.test('parse datetime', function (assert) { date2 = moment.utc(dateStr, ['M/D/YYYY H:m:s'], true); assert.equal(date1.format(), date2.format(), "Date without leading 0"); - dateStr = '01/01/0001 10:15:45'; + dateStr = '01/01/1000 10:15:45'; date1 = fieldUtils.parse.datetime(dateStr); date2 = moment.utc(dateStr, ['MM/DD/YYYY HH:mm:ss'], true); assert.equal(date1.format(), date2.format(), "can parse dates of year 1"); 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 8d62438bbbb..f16aed52512 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -1591,7 +1591,7 @@ const DatetimePickerUserValueWidget = InputUserValueWidget.extend({ this.inputEl.setAttribute('data-target', '#' + datetimePickerId); const datepickersOptions = { - minDate: moment({ y: 1 }), + minDate: moment({ y: 1000 }), maxDate: moment().add(200, 'y'), calendarWeeks: true, defaultDate: moment().format(), diff --git a/addons/website/static/src/js/menu/content.js b/addons/website/static/src/js/menu/content.js index 9871f01b55b..45a73ef2429 100644 --- a/addons/website/static/src/js/menu/content.js +++ b/addons/website/static/src/js/menu/content.js @@ -166,7 +166,7 @@ var PagePropertiesDialog = weWidgets.Dialog.extend({ })); var datepickersOptions = { - minDate: moment({ y: 1 }), + minDate: moment({ y: 1000 }), maxDate: moment().add(200, 'y'), calendarWeeks: true, icons: { diff --git a/addons/website_form/static/src/snippets/s_website_form/000.js b/addons/website_form/static/src/snippets/s_website_form/000.js index fa0c920a53d..3cb891a3069 100644 --- a/addons/website_form/static/src/snippets/s_website_form/000.js +++ b/addons/website_form/static/src/snippets/s_website_form/000.js @@ -37,7 +37,7 @@ odoo.define('website_form.s_website_form', function (require) { // Initialize datetimepickers var datepickers_options = { - minDate: moment({ y: 1 }), + minDate: moment({ y: 1000 }), maxDate: moment({y: 9999, M: 11, d: 31}), calendarWeeks: true, icons: {