diff --git a/addons/web/static/src/core/pager/pager.js b/addons/web/static/src/core/pager/pager.js
index 25f83d903ba..c311574fc1f 100644
--- a/addons/web/static/src/core/pager/pager.js
+++ b/addons/web/static/src/core/pager/pager.js
@@ -64,6 +64,10 @@ export class Pager extends Component {
async navigate(direction) {
let minimum = this.props.offset + this.props.limit * direction;
let total = this.props.total;
+ if (this.props.updateTotal && minimum < 0) {
+ // we must know the real total to be able to loop by doing "previous"
+ total = await this.props.updateTotal();
+ }
if (minimum >= total) {
if (!this.props.updateTotal) {
// only loop forward if we know the real total, otherwise let the minimum
diff --git a/addons/web/static/tests/core/pager_tests.js b/addons/web/static/tests/core/pager_tests.js
index 154f57729c4..1e327678ac9 100644
--- a/addons/web/static/tests/core/pager_tests.js
+++ b/addons/web/static/tests/core/pager_tests.js
@@ -382,4 +382,30 @@ QUnit.module("Components", ({ beforeEach }) => {
assert.strictEqual(target.querySelector(".o_pager_limit").innerText, "10+");
assert.hasClass(target.querySelector(".o_pager_limit"), "o_pager_limit_fetch");
});
+
+ QUnit.test("updateTotal props: click previous", async function (assert) {
+ const pager = await makePager({
+ offset: 0,
+ limit: 5,
+ total: 10,
+ onUpdate(data) {
+ pager.updateProps(data);
+ },
+ async updateTotal() {
+ const total = 23;
+ pager.updateProps({ total, updateTotal: undefined });
+ return total;
+ },
+ });
+
+ assert.strictEqual(target.querySelector(".o_pager_value").innerText, "1-5");
+ assert.strictEqual(target.querySelector(".o_pager_limit").innerText, "10+");
+ assert.hasClass(target.querySelector(".o_pager_limit"), "o_pager_limit_fetch");
+
+ await click(target, ".o_pager_previous");
+
+ assert.strictEqual(target.querySelector(".o_pager_value").innerText, "21-23");
+ assert.strictEqual(target.querySelector(".o_pager_limit").innerText, "23");
+ assert.doesNotHaveClass(target.querySelector(".o_pager_limit"), "o_pager_limit_fetch");
+ });
});
diff --git a/addons/web/static/tests/views/kanban_view_tests.js b/addons/web/static/tests/views/kanban_view_tests.js
index 125d9506d70..2c43614b27d 100644
--- a/addons/web/static/tests/views/kanban_view_tests.js
+++ b/addons/web/static/tests/views/kanban_view_tests.js
@@ -1342,6 +1342,47 @@ QUnit.module("Views", (hooks) => {
assert.verifySteps(["search_count"]);
});
+ QUnit.test(
+ "pager, ungrouped, deleting all records from last page should move to previous page",
+ async (assert) => {
+ patchDialog((_cls, props) => {
+ assert.step("open-dialog");
+ props.confirm();
+ });
+
+ await makeView({
+ type: "kanban",
+ resModel: "partner",
+ serverData,
+ arch: `
+
+
+
+
+
+ `,
+ });
+
+ assert.deepEqual(getPagerValue(target), [1, 3]);
+ assert.strictEqual(getPagerLimit(target), 4);
+
+ // move to next page
+ await pagerNext(target);
+
+ assert.deepEqual(getPagerValue(target), [4, 4]);
+
+ // delete a record
+ await click(target, ".o_kanban_record a");
+
+ assert.verifySteps(["open-dialog"]);
+ assert.deepEqual(getPagerValue(target), [1, 3]);
+ assert.strictEqual(getPagerLimit(target), 3);
+ }
+ );
+
QUnit.test("pager, update calls onUpdatedPager before the render", async (assert) => {
assert.expect(8);
diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js
index b6aba0e2cb6..1768dcef3df 100644
--- a/addons/web/static/tests/views/list_view_tests.js
+++ b/addons/web/static/tests/views/list_view_tests.js
@@ -5752,6 +5752,37 @@ QUnit.module("Views", (hooks) => {
assert.verifySteps(["web_search_read"]);
});
+ QUnit.test("pager, ungrouped, with count limit reached, click previous", async (assert) => {
+ patchWithCleanup(DynamicRecordList, { WEB_SEARCH_READ_COUNT_LIMIT: 3 });
+ serverData.models.foo.records.push({ id: 5, bar: true, foo: "xxx" });
+
+ let expectedCountLimit = 4;
+ await makeView({
+ type: "list",
+ resModel: "foo",
+ serverData,
+ arch: '',
+ mockRPC(route, args) {
+ assert.step(args.method);
+ if (args.method === "web_search_read") {
+ assert.strictEqual(args.kwargs.count_limit, expectedCountLimit);
+ }
+ },
+ });
+
+ assert.containsN(target, ".o_data_row", 2);
+ assert.strictEqual(target.querySelector(".o_pager_value").innerText, "1-2");
+ assert.strictEqual(target.querySelector(".o_pager_limit").innerText, "3+");
+ assert.verifySteps(["get_views", "web_search_read"]);
+
+ expectedCountLimit = undefined;
+ await click(target.querySelector(".o_pager_previous"));
+ assert.containsOnce(target, ".o_data_row");
+ assert.strictEqual(target.querySelector(".o_pager_value").innerText, "5-5");
+ assert.strictEqual(target.querySelector(".o_pager_limit").innerText, "5");
+ assert.verifySteps(["search_count", "web_search_read"]);
+ });
+
QUnit.test("pager, ungrouped, with count limit reached, edit pager", async (assert) => {
patchWithCleanup(DynamicRecordList, { WEB_SEARCH_READ_COUNT_LIMIT: 3 });
serverData.models.foo.records.push({ id: 5, bar: true, foo: "xxx" });