[FIX] web: create string ast for PyDate(time) object

Before this commit, when a new domain is created with a list of rules
and in one of those rules contains a PyDate/PyDatetime object in a
right leaf, this leaf will be converted into a plain object instead of
keeping the initial object.
For instance:
```js
a = new Domain([['date', '=', PyDate.create({day: 1, month: 1, year: 1970})]])
a.toList() // result: `[['date', '=', {day: 1, month: 1, year: 1970}]]`
```
Since it is a plain object, when the result will be passed to the
server, the final result will be an object in a string instead of giving
the utc date formatted.

This commit fixes this issue by checking if the object instances of the
`PyDate` or `PyDatetime`. If it is the case, then the ast type will be a
string and the value will stay the same instead of creating a plain
object and the ast type which determines a JSON.

closes odoo/odoo#95570

X-original-commit: be5c8d0fc340b3e2ad9721d21ef1133870d8a774
Signed-off-by: Géry Debongnie <ged@odoo.com>
Signed-off-by: Xavier <xbo@odoo.com>
This commit is contained in:
Xavier BOL (xbo)
2022-07-07 17:31:19 +02:00
parent 37d30e7c40
commit 45780793dd
6 changed files with 131 additions and 29 deletions
+28 -12
View File
@@ -1,6 +1,6 @@
/** @odoo-module **/
import { parseArgs } from "./py_utils";
import { parseArgs } from "./py_parser";
// -----------------------------------------------------------------------------
// Errors
@@ -224,10 +224,18 @@ export class PyDate {
* @returns {PyDate}
*/
static today() {
const now = new Date();
const year = now.getUTCFullYear();
const month = now.getUTCMonth() + 1;
const day = now.getUTCDate();
return this.convertDate(new Date());
}
/**
* Convert a date object into PyDate
* @param {Date} date
* @returns {PyDate}
*/
static convertDate(date) {
const year = date.getUTCFullYear();
const month = date.getUTCMonth() + 1;
const day = date.getUTCDate();
return new PyDate(year, month, day);
}
@@ -323,13 +331,21 @@ export class PyDateTime {
* @returns {PyDateTime}
*/
static now() {
const now = new Date();
const year = now.getUTCFullYear();
const month = now.getUTCMonth() + 1;
const day = now.getUTCDate();
const hour = now.getUTCHours();
const minute = now.getUTCMinutes();
const second = now.getUTCSeconds();
return this.convertDate(new Date());
}
/**
* Convert a date object into PyDateTime
* @param {Date} date
* @returns {PyDateTime}
*/
static convertDate(date) {
const year = date.getUTCFullYear();
const month = date.getUTCMonth() + 1;
const day = date.getUTCDate();
const hour = date.getUTCHours();
const minute = date.getUTCMinutes();
const second = date.getUTCSeconds();
return new PyDateTime(year, month, day, hour, minute, second, 0);
}
@@ -9,7 +9,8 @@ import {
PyTime,
PyTimeDelta,
} from "./py_date";
import { parseArgs, PY_DICT, toPyDict } from "./py_utils";
import { PY_DICT, toPyDict } from "./py_utils";
import { parseArgs } from './py_parser';
// -----------------------------------------------------------------------------
// Types
@@ -369,3 +369,18 @@ export function parse(tokens) {
}
throw new ParserError("Missing token");
}
/**
* @param {any[]} args
* @param {string[]} spec
* @returns {{[name: string]: any}}
*/
export function parseArgs(args, spec) {
const last = args[args.length - 1];
const unnamedArgs = typeof last === "object" ? args.slice(0, -1) : args;
const kwargs = typeof last === "object" ? last : {};
for (let [index, val] of unnamedArgs.entries()) {
kwargs[spec[index]] = val;
}
return kwargs;
}
+5 -15
View File
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { bp } from "./py_parser";
import { PyDate, PyDateTime } from './py_date';
// -----------------------------------------------------------------------------
// Types
@@ -14,21 +15,6 @@ import { bp } from "./py_parser";
// Utils
// -----------------------------------------------------------------------------
/**
* @param {any[]} args
* @param {string[]} spec
* @returns {{[name: string]: any}}
*/
export function parseArgs(args, spec) {
const last = args[args.length - 1];
const unnamedArgs = typeof last === "object" ? args.slice(0, -1) : args;
const kwargs = typeof last === "object" ? last : {};
for (let [index, val] of unnamedArgs.entries()) {
kwargs[spec[index]] = val;
}
return kwargs;
}
/**
* Represent any value as a primitive AST
*
@@ -48,6 +34,10 @@ export function toPyValue(value) {
return { type: 4 /* List */, value: value.map(toPyValue) };
} else if (value === null) {
return { type: 3 /* None */ };
} else if (value instanceof Date) {
return { type: 1, value: PyDateTime.convertDate(value) };
} else if (value instanceof PyDate || value instanceof PyDateTime) {
return { type: 1, value };
} else {
const content = {};
for (let key in value) {
+8 -1
View File
@@ -281,9 +281,16 @@ QUnit.module("domain", {}, () => {
return new PyDate(2013, 4, 24);
},
});
const domainStr =
let domainStr =
"[('date','>=', (context_today() - datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]";
assert.deepEqual(new Domain(domainStr).toList(), [["date", ">=", "2013-03-25"]]);
domainStr = "[('date', '>=', context_today() - relativedelta(days=30))]";
const domainList = new Domain(domainStr).toList(); // domain creation using `parseExpr` function since the parameter is a string.
assert.deepEqual(domainList[0][2], PyDate.create({ day: 25, month: 3, year: 2013 }), 'The right item in the rule in the domain should be a PyDate object');
assert.deepEqual(JSON.stringify(domainList), '[["date",">=","2013-03-25"]]');
const domainList2 = new Domain(domainList).toList(); // domain creation using `toAST` function since the parameter is a list.
assert.deepEqual(domainList2[0][2], PyDate.create({ day: 25, month: 3, year: 2013 }), 'The right item in the rule in the domain should be a PyDate object');
assert.deepEqual(JSON.stringify(domainList2), '[["date",">=","2013-03-25"]]');
});
QUnit.test("Check that there is no dependency between two domains", function (assert) {
@@ -2,6 +2,7 @@
import { evaluateExpr, formatAST, parseExpr } from "@web/core/py_js/py";
import { toPyValue } from "@web/core/py_js/py_utils";
import { PyDate, PyDateTime } from '@web/core/py_js/py_date';
QUnit.module("py", {}, () => {
QUnit.module("formatAST");
@@ -131,4 +132,76 @@ QUnit.module("py", {}, () => {
QUnit.test("null value", function (assert) {
assert.strictEqual(formatAST(toPyValue(null)), "None");
});
QUnit.module('toPyValue');
QUnit.test('toPyValue a string', function (assert) {
const ast = toPyValue('test');
assert.strictEqual(ast.type, 1);
assert.strictEqual(ast.value, 'test');
assert.strictEqual(formatAST(ast), '"test"');
});
QUnit.test('toPyValue a number', function (assert) {
const ast = toPyValue(1);
assert.strictEqual(ast.type, 0);
assert.strictEqual(ast.value, 1);
assert.strictEqual(formatAST(ast), "1");
});
QUnit.test('toPyValue a boolean', function (assert) {
let ast = toPyValue(true);
assert.strictEqual(ast.type, 2);
assert.strictEqual(ast.value, true);
assert.strictEqual(formatAST(ast), "True");
ast = toPyValue(false);
assert.strictEqual(ast.type, 2);
assert.strictEqual(ast.value, false);
assert.strictEqual(formatAST(ast), "False");
});
QUnit.test('toPyValue a object', function (assert) {
const ast = toPyValue({a: 1});
assert.strictEqual(ast.type, 11);
assert.ok('a' in ast.value);
assert.ok(['type', 'value'].every(prop => prop in ast.value.a));
assert.strictEqual(ast.value.a.type, 0);
assert.strictEqual(ast.value.a.value, 1);
assert.strictEqual(formatAST(ast), '{"a": 1}');
});
QUnit.test('toPyValue a date', function (assert) {
const date = new Date(Date.UTC(2000, 0, 1));
const ast = toPyValue(date);
assert.strictEqual(ast.type, 1);
const expectedValue = PyDateTime.convertDate(date);
assert.ok(ast.value.isEqual(expectedValue));
assert.strictEqual(formatAST(ast), JSON.stringify(expectedValue));
});
QUnit.test('toPyValue a dateime', function (assert) {
const datetime = new Date(Date.UTC(2000, 0, 1, 1, 0, 0, 0));
const ast = toPyValue(datetime);
assert.strictEqual(ast.type, 1);
const expectedValue = PyDateTime.convertDate(datetime);
assert.ok(ast.value.isEqual(expectedValue));
assert.strictEqual(formatAST(ast), JSON.stringify(expectedValue));
});
QUnit.test('toPyValue a PyDate', function (assert) {
const value = new PyDate(2000, 1, 1);
const ast = toPyValue(value);
assert.strictEqual(ast.type, 1);
assert.strictEqual(ast.value, value);
assert.strictEqual(formatAST(ast), JSON.stringify(value));
});
QUnit.test('toPyValue a PyDateTime', function (assert) {
const value = new PyDateTime(2000, 1, 1, 1, 0, 0, 0);
const ast = toPyValue(value);
assert.strictEqual(ast.type, 1);
assert.strictEqual(ast.value, value);
assert.strictEqual(formatAST(ast), JSON.stringify(value));
});
});