[FIX] web: handle field sorting should include id

Since commit 7d2baaa0c7 ("Unity read"), we are able to
pass a full specification to subfields in a view and retrive records directly according to that
specification. This could include "order".

In the case of a list view that has a `widget="handle"`, this order is automatically set to "[handle_field] ASC".

Before the unity read feature, it did not cause problems for one2manys because the ids of records were retrieved in python
using the  "natural order" of the model (the `model._order` slot), which usually had the right parameters.
(see `sale.order.line` for example). When fetching the ids of the one2many, those were already sorted in natural order.

In unity read, the natural order is overriden by the specification and became only "[handle_field] ASC". This was insufficient
as more often than not, sequences on model are set up with a default. So eventually, all records couls have the same sequence.
The sorting in SQL becomes undeterminate.

After this commit, we had the sorting key "id ASC" to avoid any unwanted results.

opw-3790378

see discord https://discord.com/channels/678381219515465750/687338039717920792/1231977078564585555 for a detailed discussion.

closes odoo/odoo#162933

Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
This commit is contained in:
Lucas Perais
2024-04-23 15:32:53 +00:00
parent c028578a6a
commit d143d01287
3 changed files with 32 additions and 2 deletions
@@ -226,7 +226,8 @@ export class ListArchParser {
});
if (!treeAttr.defaultOrder.length && handleField) {
treeAttr.defaultOrder = stringToOrderBy(handleField);
const handleFieldSort = `${handleField}, id`;
treeAttr.defaultOrder = stringToOrderBy(handleFieldSort);
}
return {
@@ -2678,6 +2678,31 @@ QUnit.module("Fields", (hooks) => {
]);
});
QUnit.test("one2many list order with handle widget", async (assert) => {
await makeView({
type: "form",
resModel: "partner",
serverData,
arch: `
<form>
<field name="p">
<tree editable="top">
<field name="int_field" widget="handle"/>
<field name="foo"/>
</tree>
</field>
</form>`,
resId: 1,
mockRPC(route, args) {
if (args.method === "web_read") {
assert.step(`web_read`);
assert.strictEqual(args.kwargs.specification.p.order, "int_field ASC, id ASC");
}
},
});
assert.verifySteps(["web_read"]);
});
QUnit.test("one2many field when using the pager", async function (assert) {
const ids = [];
for (let i = 0; i < 45; i++) {
@@ -10824,7 +10824,7 @@ QUnit.module("Views", (hooks) => {
});
QUnit.test("list with handle widget", async function (assert) {
assert.expect(11);
assert.expect(13);
await makeView({
type: "list",
@@ -10836,6 +10836,9 @@ QUnit.module("Views", (hooks) => {
<field name="amount" widget="float" digits="[5,0]"/>
</tree>`,
mockRPC(route, args) {
if (args.method === "web_search_read") {
assert.step(`web_search_read: order: ${args.kwargs.order}`);
}
if (route === "/web/dataset/resequence") {
assert.strictEqual(
args.offset,
@@ -10857,6 +10860,7 @@ QUnit.module("Views", (hooks) => {
},
});
assert.verifySteps(["web_search_read: order: int_field ASC, id ASC"]);
let rows = target.querySelectorAll(".o_data_row");
assert.strictEqual(
rows[0].querySelector("[name='amount']").textContent,