[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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user