[IMP] web: evaluate domain with user context

Currently, the domain editor dialog doesn't handle domains like
[('foo', '=', uid)] if you don't provide the (user) context in the
component props.

Since this kind of domain is expected to work everywhere, the component
now uses the user context by default to evaluate the domain.
There's no need to provide the context everytime the component is used.

closes odoo/odoo#127565

Signed-off-by: Mathieu Duckerts-Antoine (dam) <dam@odoo.com>
This commit is contained in:
Lucas Lefèvre (lul)
2023-07-06 17:52:43 +02:00
parent b0fdb2b429
commit 6ea05e844a
2 changed files with 142 additions and 1 deletions
@@ -11,6 +11,7 @@ export class DomainSelectorDialog extends Component {
setup() {
this.notification = useService("notification");
this.orm = useService("orm");
this.user = useService("user");
this.state = useState({ domain: this.props.domain });
}
@@ -51,7 +52,8 @@ export class DomainSelectorDialog extends Component {
async onConfirm() {
try {
let domain = new Domain(this.state.domain);
domain = domain.toList(this.props.context);
const evalContext = { ...this.user.context, ...this.props.context };
domain = domain.toList(evalContext);
await this.orm.silent.searchCount(this.props.resModel, domain, { limit: 1 });
} catch {
this.notification.add(this.env._t("Domain is invalid. Please correct it"), {
@@ -0,0 +1,139 @@
/** @odoo-module **/
import { Component, xml } from "@odoo/owl";
import { DomainSelectorDialog } from "@web/core/domain_selector_dialog/domain_selector_dialog";
import { click, getFixture, mount } from "../helpers/utils";
import { makeDialogTestEnv } from "../helpers/mock_env";
import { registry } from "@web/core/registry";
import { notificationService } from "@web/core/notifications/notification_service";
import { ormService } from "@web/core/orm_service";
import { uiService } from "@web/core/ui/ui_service";
import { MainComponentsContainer } from "@web/core/main_components_container";
import { makeFakeLocalizationService } from "../helpers/mock_services";
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
import { fieldService } from "@web/core/field_service";
import { popoverService } from "@web/core/popover/popover_service";
/**
* @typedef {Record<keyof DomainSelectorDialog.props, any>} Props
*/
/**
* @param {Partial<Props> & { mockRPC: Function }} [params]
*/
async function makeDomainSelectorDialog(params = {}) {
const props = { ...params };
const mockRPC = props.mockRPC;
delete props.mockRPC;
class Parent extends Component {
static components = { DomainSelectorDialog };
static template = xml`<DomainSelectorDialog t-props="domainSelectorProps"/>`;
setup() {
this.domainSelectorProps = {
resModel: "partner",
readonly: false,
domain: "[]",
close: () => {},
onConfirm: () => {},
...props,
};
}
}
const env = await makeDialogTestEnv({ serverData, mockRPC });
await mount(MainComponentsContainer, fixture, { env });
return mount(Parent, fixture, { env, props });
}
/** @type {Element} */
let fixture;
let serverData;
QUnit.module("Components", (hooks) => {
hooks.beforeEach(async () => {
serverData = {
models: {
partner: {
fields: {
foo: { string: "Foo", type: "char", searchable: true },
bar: { string: "Bar", type: "boolean", searchable: true },
product_id: {
string: "Product",
type: "many2one",
relation: "product",
searchable: true,
},
datetime: { string: "Date Time", type: "datetime", searchable: true },
int: { string: "Integer", type: "integer", searchable: true },
json_field: { string: "Json Field", type: "json", searchable: true },
},
records: [
{ id: 1, foo: "yop", bar: true, product_id: 37 },
{ id: 2, foo: "blip", bar: true, product_id: false },
{ id: 4, foo: "abc", bar: false, product_id: 41 },
],
onchanges: {},
},
product: {
fields: {
name: { string: "Product Name", type: "char", searchable: true },
},
records: [
{ id: 37, display_name: "xphone" },
{ id: 41, display_name: "xpad" },
],
},
},
};
registry.category("services").add("notification", notificationService);
registry.category("services").add("orm", ormService);
registry.category("services").add("ui", uiService);
registry.category("services").add("hotkey", hotkeyService);
registry.category("services").add("localization", makeFakeLocalizationService());
registry.category("services").add("popover", popoverService);
registry.category("services").add("field", fieldService);
fixture = getFixture();
});
QUnit.module("DomainSelector");
QUnit.test("a domain with a user context dynamic part is valid", async (assert) => {
await makeDomainSelectorDialog({
domain: "[('foo', '=', uid)]",
onConfirm(domain) {
assert.strictEqual(domain, "[('foo', '=', uid)]");
assert.step("confirmed");
},
mockRPC(route, args) {
if (args.method === "search_count") {
const domain = args.args[0];
assert.deepEqual(domain, [["foo", "=", 7]]);
assert.step("rpc validation");
}
},
});
const confirmButton = fixture.querySelector(".o_dialog footer button");
await click(confirmButton);
assert.verifySteps(["rpc validation", "confirmed"]);
});
QUnit.test("can override user context", async (assert) => {
await makeDomainSelectorDialog({
domain: "[('foo', '=', uid)]",
context: { uid: 99 },
mockRPC(route, args) {
if (args.method === "search_count") {
const domain = args.args[0];
assert.deepEqual(domain, [["foo", "=", 99]]);
assert.step("rpc validation");
}
},
});
const confirmButton = fixture.querySelector(".o_dialog footer button");
await click(confirmButton);
assert.verifySteps(["rpc validation"]);
});
});