From 909aff92678cfb88c19a3677b155557fae2a3320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Tue, 7 Jan 2025 15:47:53 -0300 Subject: [PATCH] [FIX] web: use count limit on the domain field widget Executing a search count on a table with a large number of records can be slow, which is why a limit was added on b37d221e. The same issue exists on the domain field widget, and so the same solution is applied here. Task 4471410 --- .../src/views/fields/domain/domain_field.js | 28 ++++- .../src/views/fields/domain/domain_field.xml | 4 +- .../static/tests/core/domain_field.test.js | 108 ++++++++++++++++++ 3 files changed, 133 insertions(+), 7 deletions(-) diff --git a/addons/web/static/src/views/fields/domain/domain_field.js b/addons/web/static/src/views/fields/domain/domain_field.js index 37719cea4c104..3c27a2958ef3a 100644 --- a/addons/web/static/src/views/fields/domain/domain_field.js +++ b/addons/web/static/src/views/fields/domain/domain_field.js @@ -25,10 +25,12 @@ export class DomainField extends Component { editInDialog: { type: Boolean, optional: true }, resModel: { type: String, optional: true }, isFoldable: { type: Boolean, optional: true }, + countLimit: { type: Number, optional: true }, }; static defaultProps = { editInDialog: false, isFoldable: false, + countLimit: 10000, }; setup() { @@ -41,6 +43,7 @@ export class DomainField extends Component { this.state = useState({ isValid: null, recordCount: null, + hasLimitedCount: null, folded: this.props.isFoldable, facets: [], }); @@ -169,24 +172,32 @@ export class DomainField extends Component { const domain = this.getEvaluatedDomain(props); if (domain.isInvalid) { - this.updateState({ isValid: false, recordCount: 0 }); + this.updateState({ isValid: false, recordCount: 0, hasLimitedCount: false }); return; } let recordCount; + let hasLimitedCount = false; const context = this.getContext(props); + let limit; + if (props.countLimit !== Number.MAX_SAFE_INTEGER) { + limit = props.countLimit + 1; + } try { - recordCount = await this.orm.silent.searchCount(resModel, domain, { context }); + recordCount = await this.orm.silent.searchCount(resModel, domain, { context, limit }); } catch (error) { if (error.data?.name === "builtins.KeyError" && error.data.message === resModel) { // we don't want to support invalid models throw new Error(`Invalid model: ${resModel}`); } - this.updateState({ isValid: false, recordCount: 0 }); + this.updateState({ isValid: false, recordCount: 0, hasLimitedCount: false }); return; } - - this.updateState({ isValid: true, recordCount }); + if (limit && recordCount >= limit) { + hasLimitedCount = true; + recordCount = props.countLimit; + } + this.updateState({ isValid: true, recordCount, hasLimitedCount }); } onButtonClick() { @@ -256,6 +267,7 @@ export class DomainField extends Component { Object.assign(this.state, { isValid: "isValid" in params ? params.isValid : null, recordCount: "recordCount" in params ? params.recordCount : null, + hasLimitedCount: "hasLimitedCount" in params ? params.hasLimitedCount : null, }); } } @@ -280,6 +292,11 @@ export const domainField = { name: "model", type: "string", }, + { + label: _t("Count Limit"), + name: "count_limit", + type: "number", + }, ], supportedTypes: ["char"], isEmpty: () => false, @@ -288,6 +305,7 @@ export const domainField = { editInDialog: options.in_dialog, isFoldable: options.foldable, resModel: options.model, + countLimit: options.count_limit, context: dynamicInfo.context, }; }, diff --git a/addons/web/static/src/views/fields/domain/domain_field.xml b/addons/web/static/src/views/fields/domain/domain_field.xml index 76563237337be..9cba8ea9aaed4 100644 --- a/addons/web/static/src/views/fields/domain/domain_field.xml +++ b/addons/web/static/src/views/fields/domain/domain_field.xml @@ -32,7 +32,7 @@ @@ -61,7 +61,7 @@ diff --git a/addons/web/static/tests/core/domain_field.test.js b/addons/web/static/tests/core/domain_field.test.js index c5757571abe21..a3f9d4d32df36 100644 --- a/addons/web/static/tests/core/domain_field.test.js +++ b/addons/web/static/tests/core/domain_field.test.js @@ -554,6 +554,114 @@ test("domain field: does not wait for the count to render", async function () { expect(".o_domain_show_selection_button").toHaveText("2 record(s)"); }); +test("domain field: have a default count limit of 10000", async function () { + serverState.debug = true; + + Partner._fields.bar = fields.Char(); + Partner._records = [ + { + foo: "[]", + bar: "product", + }, + ]; + Partner._views = { + form: ` +
+ + + `, + search: ``, + }; + + onRpc("search_count", ({ kwargs }) => { + expect.step(kwargs.limit); + return 99999; + }); + + await mountWithCleanup(WebClient); + await getService("action").doAction({ + name: "test", + res_id: 1, + res_model: "partner", + type: "ir.actions.act_window", + views: [[false, "form"]], + }); + expect.verifySteps([10001]); + expect(".o_domain_show_selection_button").toHaveText("10000+ record(s)"); +}); + +test("domain field: foldable and count limit reached", async function () { + serverState.debug = true; + + Partner._fields.bar = fields.Char(); + Partner._records = [ + { + foo: "[]", + bar: "product", + }, + ]; + Partner._views = { + form: ` +
+ + + `, + search: ``, + }; + + onRpc("search_count", ({ kwargs }) => { + expect.step(kwargs.limit); + return 99999; + }); + + await mountWithCleanup(WebClient); + await getService("action").doAction({ + name: "test", + res_id: 1, + res_model: "partner", + type: "ir.actions.act_window", + views: [[false, "form"]], + }); + expect.verifySteps([10001]); + expect(".o_domain_show_selection_button").toHaveText("10000+ record(s)"); +}); + +test("domain field: configurable count limit", async function () { + serverState.debug = true; + + Partner._fields.bar = fields.Char(); + Partner._records = [ + { + foo: "[]", + bar: "product", + }, + ]; + Partner._views = { + form: ` +
+ + + `, + search: ``, + }; + + onRpc("search_count", ({ kwargs }) => { + expect.step(kwargs.limit); + return 99999; + }); + + await mountWithCleanup(WebClient); + await getService("action").doAction({ + name: "test", + res_id: 1, + res_model: "partner", + type: "ir.actions.act_window", + views: [[false, "form"]], + }); + expect.verifySteps([11]); + expect(".o_domain_show_selection_button").toHaveText("10+ record(s)"); +}); + test("domain field: edit domain with dynamic content", async function () { expect.assertions(3);