[FIX] web: invalid date/datetime value/range/domain in mockReadGroup

The mock server's mockReadGroup function would not properly compute the
value, range and domain of the returned groups for date/datetime fields.
The value should be expressed in the local timezone,
and the range/domain shoulb be expressed in UTC timezone.

Consider the following:
- a record with a datetime field of value "2016-12-14 12:34:56" (expressed in UTC)
- a user with an UTC+2 timezone offset
- a read_group with groupby "['datetime:hour']"

The corresponding group would have had the following metadata:
- value: `"12:00 14 Dec"`
- domain: `[["datetime", ">=", "2022-12-14 11:00:00"], ["datetime", "<", "2022-12-14 12:00:00"]]`
- range: `{ from: "2022-12-14 11:00:00", to: "2022-12-14 12:00:00" }`

It now has the following valid metadata:
- value: `"13:00 14 Dec"`
- domain: `[["datetime", ">=", "2022-12-14 12:00:00"], ["datetime", "<", "2022-12-14 13:00:00"]]`
- range: `{ from: "2022-12-14 12:00:00", to: "2022-12-14 13:00:00" }`

closes odoo/odoo#96946

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Bruno Boi
2022-08-02 09:19:57 +02:00
parent 9d1786c361
commit ab3032465c
4 changed files with 361 additions and 26 deletions
+40 -19
View File
@@ -8,9 +8,14 @@ import { intersection } from "@web/core/utils/arrays";
import { registry } from "@web/core/registry";
import { makeFakeRPCService, makeMockFetch } from "./mock_services";
import { patchWithCleanup } from "./utils";
import { parseDateTime } from "@web/core/l10n/dates";
import {
deserializeDate,
deserializeDateTime,
parseDateTime,
serializeDate,
serializeDateTime,
} from "@web/core/l10n/dates";
const { DateTime } = luxon;
const serviceRegistry = registry.category("services");
const domParser = new DOMParser();
@@ -879,7 +884,7 @@ export class MockServer {
const [fieldName, aggregateFunction = "month"] = groupByField.split(":");
const { type } = fields[fieldName];
if (type === "date") {
const date = DateTime.fromSQL(val);
const date = deserializeDate(val).setZone("default");
if (aggregateFunction === "day") {
return date.toFormat("yyyy-MM-dd");
} else if (aggregateFunction === "week") {
@@ -892,7 +897,7 @@ export class MockServer {
return date.toFormat("MMMM yyyy");
}
} else if (type === "datetime") {
const date = DateTime.fromSQL(val);
const date = deserializeDateTime(val).setZone("default");
if (aggregateFunction === "hour") {
return date.toFormat("HH:00 dd MMM");
} else if (aggregateFunction === "day") {
@@ -987,48 +992,64 @@ export class MockServer {
switch (dateRange) {
case "hour": {
try {
startDate = parseDateTime(value, { format: "HH dd MMM" });
startDate = parseDateTime(value, {
format: "HH dd MMM",
timezone: type !== "date",
});
} catch {
startDate = parseDateTime(value, { format: "HH:00 dd MMM" });
startDate = parseDateTime(value, {
format: "HH:00 dd MMM",
timezone: type !== "date",
});
}
endDate = startDate.plus({ hours: 1 });
break;
}
case "day": {
startDate = parseDateTime(value, { format: "yyyy-MM-dd" });
startDate = parseDateTime(value, {
format: "yyyy-MM-dd",
timezone: type !== "date",
});
endDate = startDate.plus({ days: 1 });
break;
}
case "week": {
startDate = parseDateTime(value, { format: "WW kkkk" });
startDate = parseDateTime(value, {
format: "WW kkkk",
timezone: type !== "date",
});
endDate = startDate.plus({ weeks: 1 });
break;
}
case "quarter": {
startDate = parseDateTime(value, { format: "q yyyy" });
startDate = parseDateTime(value, {
format: "q yyyy",
timezone: type !== "date",
});
endDate = startDate.plus({ quarters: 1 });
break;
}
case "year": {
startDate = parseDateTime(value, { format: "y" });
startDate = parseDateTime(value, {
format: "y",
timezone: type !== "date",
});
endDate = startDate.plus({ years: 1 });
break;
}
case "month":
default: {
startDate = parseDateTime(value, { format: "MMMM yyyy" });
startDate = parseDateTime(value, {
format: "MMMM yyyy",
timezone: type !== "date",
});
endDate = startDate.plus({ months: 1 });
break;
}
}
const from =
type === "date"
? startDate.toFormat("yyyy-MM-dd")
: startDate.toFormat("yyyy-MM-dd HH:mm:ss");
const to =
type === "date"
? endDate.toFormat("yyyy-MM-dd")
: endDate.toFormat("yyyy-MM-dd HH:mm:ss");
const serialize = type === "date" ? serializeDate : serializeDateTime;
const from = serialize(startDate);
const to = serialize(endDate);
group.__range[gbField] = { from, to };
group.__domain = [
[fieldName, ">=", from],
@@ -1644,6 +1644,9 @@ var MockServer = Class.extend({
const to = type === "date"
? endDate.format("YYYY-MM-DD")
: endDate.format("YYYY-MM-DD HH:mm:ss");
// NOTE THAT the range and the domain computed here are not really accurate
// due to a the timezone not really taken into account.
// FYI, the non legacy version of the mock server handles this correctly.
group.__range[gbField] = { from, to };
group.__domain = [
[fieldName, ">=", from],
+316 -1
View File
@@ -394,6 +394,36 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.date_count),
[1, 1, 3, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["date", ">=", "2016-04-01"],
["date", "<", "2016-05-01"],
],
[
["date", ">=", "2016-10-01"],
["date", "<", "2016-11-01"],
],
[
["date", ">=", "2016-12-01"],
["date", "<", "2017-01-01"],
],
[
["date", ">=", "2019-12-01"],
["date", "<", "2020-01-01"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["date"]),
[
{ from: "2016-04-01", to: "2016-05-01" },
{ from: "2016-10-01", to: "2016-11-01" },
{ from: "2016-12-01", to: "2017-01-01" },
{ from: "2019-12-01", to: "2020-01-01" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -413,6 +443,41 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.date_count),
[1, 1, 1, 2, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["date", ">=", "2016-04-11"],
["date", "<", "2016-04-12"],
],
[
["date", ">=", "2016-10-26"],
["date", "<", "2016-10-27"],
],
[
["date", ">=", "2016-12-14"],
["date", "<", "2016-12-15"],
],
[
["date", ">=", "2016-12-15"],
["date", "<", "2016-12-16"],
],
[
["date", ">=", "2019-12-30"],
["date", "<", "2019-12-31"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["date:day"]),
[
{ from: "2016-04-11", to: "2016-04-12" },
{ from: "2016-10-26", to: "2016-10-27" },
{ from: "2016-12-14", to: "2016-12-15" },
{ from: "2016-12-15", to: "2016-12-16" },
{ from: "2019-12-30", to: "2019-12-31" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -432,6 +497,36 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.date_count),
[1, 1, 3, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["date", ">=", "2016-04-11"],
["date", "<", "2016-04-18"],
],
[
["date", ">=", "2016-10-24"],
["date", "<", "2016-10-31"],
],
[
["date", ">=", "2016-12-12"],
["date", "<", "2016-12-19"],
],
[
["date", ">=", "2019-12-30"],
["date", "<", "2020-01-06"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["date:week"]),
[
{ from: "2016-04-11", to: "2016-04-18" },
{ from: "2016-10-24", to: "2016-10-31" },
{ from: "2016-12-12", to: "2016-12-19" },
{ from: "2019-12-30", to: "2020-01-06" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -451,6 +546,31 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.date_count),
[1, 4, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["date", ">=", "2016-04-01"],
["date", "<", "2016-07-01"],
],
[
["date", ">=", "2016-10-01"],
["date", "<", "2017-01-01"],
],
[
["date", ">=", "2019-10-01"],
["date", "<", "2020-01-01"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["date:quarter"]),
[
{ from: "2016-04-01", to: "2016-07-01" },
{ from: "2016-10-01", to: "2017-01-01" },
{ from: "2019-10-01", to: "2020-01-01" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -470,6 +590,26 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.date_count),
[5, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["date", ">=", "2016-01-01"],
["date", "<", "2017-01-01"],
],
[
["date", ">=", "2019-01-01"],
["date", "<", "2020-01-01"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["date:year"]),
[
{ from: "2016-01-01", to: "2017-01-01" },
{ from: "2019-01-01", to: "2020-01-01" },
]
);
});
QUnit.test("performRPC: read_group, group by datetime", async function (assert) {
@@ -492,6 +632,36 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.datetime_count),
[1, 1, 3, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["datetime", ">=", "2016-03-31 23:00:00"],
["datetime", "<", "2016-04-30 23:00:00"],
],
[
["datetime", ">=", "2016-09-30 23:00:00"],
["datetime", "<", "2016-10-31 23:00:00"],
],
[
["datetime", ">=", "2016-11-30 23:00:00"],
["datetime", "<", "2016-12-31 23:00:00"],
],
[
["datetime", ">=", "2019-11-30 23:00:00"],
["datetime", "<", "2019-12-31 23:00:00"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["datetime"]),
[
{ from: "2016-03-31 23:00:00", to: "2016-04-30 23:00:00" },
{ from: "2016-09-30 23:00:00", to: "2016-10-31 23:00:00" },
{ from: "2016-11-30 23:00:00", to: "2016-12-31 23:00:00" },
{ from: "2019-11-30 23:00:00", to: "2019-12-31 23:00:00" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -505,12 +675,47 @@ QUnit.module("MockServer", (hooks) => {
});
assert.deepEqual(
result.map((x) => x["datetime:hour"]),
["12:00 11 Apr", "12:00 26 Oct", "12:00 14 Dec", "12:00 15 Dec", "12:00 30 Dec"]
["13:00 11 Apr", "13:00 26 Oct", "13:00 14 Dec", "13:00 15 Dec", "13:00 30 Dec"]
);
assert.deepEqual(
result.map((x) => x.datetime_count),
[1, 1, 1, 2, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["datetime", ">=", "2022-04-11 12:00:00"],
["datetime", "<", "2022-04-11 13:00:00"],
],
[
["datetime", ">=", "2022-10-26 12:00:00"],
["datetime", "<", "2022-10-26 13:00:00"],
],
[
["datetime", ">=", "2022-12-14 12:00:00"],
["datetime", "<", "2022-12-14 13:00:00"],
],
[
["datetime", ">=", "2022-12-15 12:00:00"],
["datetime", "<", "2022-12-15 13:00:00"],
],
[
["datetime", ">=", "2022-12-30 12:00:00"],
["datetime", "<", "2022-12-30 13:00:00"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["datetime:hour"]),
[
{ from: "2022-04-11 12:00:00", to: "2022-04-11 13:00:00" },
{ from: "2022-10-26 12:00:00", to: "2022-10-26 13:00:00" },
{ from: "2022-12-14 12:00:00", to: "2022-12-14 13:00:00" },
{ from: "2022-12-15 12:00:00", to: "2022-12-15 13:00:00" },
{ from: "2022-12-30 12:00:00", to: "2022-12-30 13:00:00" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -530,6 +735,41 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.datetime_count),
[1, 1, 1, 2, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["datetime", ">=", "2016-04-10 23:00:00"],
["datetime", "<", "2016-04-11 23:00:00"],
],
[
["datetime", ">=", "2016-10-25 23:00:00"],
["datetime", "<", "2016-10-26 23:00:00"],
],
[
["datetime", ">=", "2016-12-13 23:00:00"],
["datetime", "<", "2016-12-14 23:00:00"],
],
[
["datetime", ">=", "2016-12-14 23:00:00"],
["datetime", "<", "2016-12-15 23:00:00"],
],
[
["datetime", ">=", "2019-12-29 23:00:00"],
["datetime", "<", "2019-12-30 23:00:00"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["datetime:day"]),
[
{ from: "2016-04-10 23:00:00", to: "2016-04-11 23:00:00" },
{ from: "2016-10-25 23:00:00", to: "2016-10-26 23:00:00" },
{ from: "2016-12-13 23:00:00", to: "2016-12-14 23:00:00" },
{ from: "2016-12-14 23:00:00", to: "2016-12-15 23:00:00" },
{ from: "2019-12-29 23:00:00", to: "2019-12-30 23:00:00" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -549,6 +789,36 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.datetime_count),
[1, 1, 3, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["datetime", ">=", "2016-04-10 23:00:00"],
["datetime", "<", "2016-04-17 23:00:00"],
],
[
["datetime", ">=", "2016-10-23 23:00:00"],
["datetime", "<", "2016-10-30 23:00:00"],
],
[
["datetime", ">=", "2016-12-11 23:00:00"],
["datetime", "<", "2016-12-18 23:00:00"],
],
[
["datetime", ">=", "2019-12-29 23:00:00"],
["datetime", "<", "2020-01-05 23:00:00"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["datetime:week"]),
[
{ from: "2016-04-10 23:00:00", to: "2016-04-17 23:00:00" },
{ from: "2016-10-23 23:00:00", to: "2016-10-30 23:00:00" },
{ from: "2016-12-11 23:00:00", to: "2016-12-18 23:00:00" },
{ from: "2019-12-29 23:00:00", to: "2020-01-05 23:00:00" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -568,6 +838,31 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.datetime_count),
[1, 4, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["datetime", ">=", "2016-03-31 23:00:00"],
["datetime", "<", "2016-06-30 23:00:00"],
],
[
["datetime", ">=", "2016-09-30 23:00:00"],
["datetime", "<", "2016-12-31 23:00:00"],
],
[
["datetime", ">=", "2019-09-30 23:00:00"],
["datetime", "<", "2019-12-31 23:00:00"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["datetime:quarter"]),
[
{ from: "2016-03-31 23:00:00", to: "2016-06-30 23:00:00" },
{ from: "2016-09-30 23:00:00", to: "2016-12-31 23:00:00" },
{ from: "2019-09-30 23:00:00", to: "2019-12-31 23:00:00" },
]
);
result = await server.performRPC("", {
model: "bar",
@@ -587,6 +882,26 @@ QUnit.module("MockServer", (hooks) => {
result.map((x) => x.datetime_count),
[5, 1]
);
assert.deepEqual(
result.map((x) => x.__domain),
[
[
["datetime", ">=", "2015-12-31 23:00:00"],
["datetime", "<", "2016-12-31 23:00:00"],
],
[
["datetime", ">=", "2018-12-31 23:00:00"],
["datetime", "<", "2019-12-31 23:00:00"],
],
]
);
assert.deepEqual(
result.map((x) => x.__range["datetime:year"]),
[
{ from: "2015-12-31 23:00:00", to: "2016-12-31 23:00:00" },
{ from: "2018-12-31 23:00:00", to: "2019-12-31 23:00:00" },
]
);
});
QUnit.test("performRPC: read_group, group by m2m", async function (assert) {
@@ -4333,7 +4333,7 @@ QUnit.module("Views", (hooks) => {
if ("date" in args[1]) {
assert.deepEqual(args[1], { date: "2017-02-28" });
} else if ("datetime" in args[1]) {
assert.deepEqual(args[1], { datetime: "2017-02-28 23:59:59" });
assert.deepEqual(args[1], { datetime: "2017-02-28 22:59:59" });
}
}
},
@@ -6923,10 +6923,8 @@ QUnit.module("Views", (hooks) => {
"</kanban>",
});
patchWithCleanup(luxon.Settings, { defaultLocale: "en" });
assert.strictEqual(getCard(0).querySelector(".date").innerText, "01/25/2017");
assert.strictEqual(getCard(1).querySelector(".datetime").innerText, "12/12/2016 10:55:05");
assert.strictEqual(getCard(1).querySelector(".datetime").innerText, "12/12/2016 11:55:05");
});
QUnit.test("rendering date and datetime (raw value)", async (assert) => {
@@ -6950,8 +6948,6 @@ QUnit.module("Views", (hooks) => {
"</kanban>",
});
patchWithCleanup(luxon.Settings, { defaultLocale: "en" });
assert.ok(getCard(0).querySelector(".date").innerText.startsWith("Wed Jan 25 2017"));
assert.ok(getCard(1).querySelector(".datetime").innerText.startsWith("Mon Dec 12 2016"));
});