diff --git a/addons/web/static/src/views/fields/signature/signature_field.js b/addons/web/static/src/views/fields/signature/signature_field.js index 227e5daffe5..00e9b77e311 100644 --- a/addons/web/static/src/views/fields/signature/signature_field.js +++ b/addons/web/static/src/views/fields/signature/signature_field.js @@ -9,7 +9,7 @@ import { isBinarySize } from "@web/core/utils/binary"; import { fileTypeMagicWordMap, imageCacheKey } from "@web/views/fields/image/image_field"; import { standardFieldProps } from "@web/views/fields/standard_field_props"; -import { Component, onWillUpdateProps, useState } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; const placeholder = "/web/static/img/placeholder.png"; @@ -31,15 +31,10 @@ export class SignatureField extends Component { this.state = useState({ isValid: true, }); + } - this.rawCacheKey = this.props.record.data.write_date; - onWillUpdateProps((nextProps) => { - const { record } = this.props; - const { record: nextRecord } = nextProps; - if (record.resId !== nextRecord.resId || nextRecord.mode === "readonly") { - this.rawCacheKey = nextRecord.data.write_date; - } - }); + get rawCacheKey() { + return this.props.record.data.write_date; } get getUrl() { @@ -54,8 +49,7 @@ export class SignatureField extends Component { }); } else { // Use magic-word technique for detecting image type - const magic = - fileTypeMagicWordMap[this.props.record.data[this.props.name][0]] || "png"; + const magic = fileTypeMagicWordMap[this.value[0]] || "png"; return `data:image/${magic};base64,${this.props.record.data[this.props.name]}`; } } @@ -141,6 +135,7 @@ export class SignatureField extends Component { export const signatureField = { component: SignatureField, + fieldDependencies: [{ name: "write_date", type: "datetime" }], supportedOptions: [ { label: _t("Prefill with"), diff --git a/addons/web/static/tests/views/fields/image_field_tests.js b/addons/web/static/tests/views/fields/image_field_tests.js index 08b16ffaaf4..988c7bb2664 100644 --- a/addons/web/static/tests/views/fields/image_field_tests.js +++ b/addons/web/static/tests/views/fields/image_field_tests.js @@ -374,6 +374,49 @@ QUnit.module("Fields", (hooks) => { } ); + QUnit.test("save record with image field modified by onchange", async function (assert) { + serverData.models.partner.onchanges = { + foo: (data) => { + data.document = MY_IMAGE; + }, + }; + const rec = serverData.models.partner.records.find((rec) => rec.id === 1); + rec.document = "3 kb"; + rec.write_date = "2022-08-05 08:37:00"; // 1659688620000 + + // 1659692220000 + const lastUpdates = ["2022-08-05 09:37:00"]; + let index = 0; + + await makeView({ + type: "form", + resModel: "partner", + resId: 1, + serverData, + arch: /* xml */ ` +
`, + mockRPC(_route, { method, args }) { + if (method === "web_save") { + args[1].write_date = lastUpdates[index]; + args[1].document = "3 kb"; + index++; + } + }, + }); + assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659688620000"); + await editInput(target, "[name='foo'] input", "grrr"); + assert.strictEqual( + target.querySelector("div[name=document] img").dataset.src, + `data:image/png;base64,${MY_IMAGE}` + ); + + await clickSave(target); + assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659692220000"); + }); + QUnit.test("ImageField: option accepted_file_extensions", async function (assert) { await makeView({ type: "form", @@ -695,6 +738,52 @@ QUnit.module("Fields", (hooks) => { ); }); + QUnit.test("unique in url doesn't change on onchange", async (assert) => { + serverData.models.partner.onchanges = { + foo: () => {}, + }; + + const rec = serverData.models.partner.records.find((rec) => rec.id === 1); + rec.document = "3 kb"; + rec.write_date = "2022-08-05 08:37:00"; + + await makeView({ + resId: 1, + type: "form", + resModel: "partner", + serverData, + arch: ` + `, + mockRPC(route, { method, args }) { + assert.step(method); + if (method === "web_save") { + // 1659692220000 + args[1].write_date = "2022-08-05 09:37:00"; + } + }, + }); + + assert.verifySteps(["get_views", "web_read"]); + assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659688620000"); + + assert.verifySteps([]); + // same unique as before + assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659688620000"); + + await editInput(target, ".o_field_widget[name='foo'] input", "grrr"); + assert.verifySteps(["onchange"]); + // also same unique + assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659688620000"); + + await clickSave(target); + assert.verifySteps(["web_save"]); + + assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659692220000"); + }); + QUnit.test("unique in url change on record change", async (assert) => { const rec = serverData.models.partner.records.find((rec) => rec.id === 1); rec.document = "3 kb"; diff --git a/addons/web/static/tests/views/fields/signature_field_tests.js b/addons/web/static/tests/views/fields/signature_field_tests.js index 194dab77c62..f5c0efadfc3 100644 --- a/addons/web/static/tests/views/fields/signature_field_tests.js +++ b/addons/web/static/tests/views/fields/signature_field_tests.js @@ -1,10 +1,13 @@ /** @odoo-module **/ import { click, + clickSave, + editInput, getFixture, makeDeferred, nextTick, patchWithCleanup, + triggerEvent, triggerEvents, } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; @@ -13,6 +16,11 @@ import { NameAndSignature } from "@web/core/signature/name_and_signature"; let serverData; let target; +function getUnique(target) { + const src = target.dataset.src; + return new URL(src).searchParams.get("unique"); +} + QUnit.module("Fields", (hooks) => { hooks.beforeEach(() => { target = getFixture(); @@ -231,4 +239,164 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual(sign3.style.width, "120px"); assert.strictEqual(sign3.style.height, "40px"); }); + + QUnit.test( + "clicking save manually after changing signature should change the unique of the image src", + async function (assert) { + serverData.models.partner.fields.foo = { type: "char" }; + serverData.models.partner.onchanges = { foo: () => {} }; + + const rec = serverData.models.partner.records.find((rec) => rec.id === 1); + rec.sign = "3 kb"; + rec.write_date = "2022-08-05 08:37:00"; // 1659688620000 + + // 1659692220000, 1659695820000 + const lastUpdates = ["2022-08-05 09:37:00", "2022-08-05 10:37:00"]; + let index = 0; + + await makeView({ + type: "form", + resModel: "partner", + resId: 1, + serverData, + arch: /* xml */ ` + `, + mockRPC(route, { method, args }) { + if (route === "/web/sign/get_fonts/") { + return {}; + } + if (method === "web_save") { + assert.step("web_save"); + args[1].write_date = lastUpdates[index]; + args[1].sign = "4 kb"; + index++; + } + }, + }); + assert.strictEqual( + getUnique(target.querySelector(".o_field_signature img")), + "1659688620000" + ); + + await click(target, ".o_field_signature img", { skipVisibilityCheck: true }); + assert.containsOnce(target, ".modal canvas"); + + let canvas = target.querySelector(".modal canvas"); + canvas.setAttribute("width", "2px"); + canvas.setAttribute("height", "2px"); + let ctx = canvas.getContext("2d"); + ctx.beginPath(); + ctx.strokeStyle = "blue"; + ctx.moveTo(0, 0); + ctx.lineTo(0, 2); + ctx.stroke(); + await triggerEvent(target, ".o_web_sign_signature", "change"); + await click(target, ".modal-footer .btn-primary"); + + const MYB64 = `iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABRJREFUGFdjZGD438DAwNjACGMAACQlBAMW7JulAAAAAElFTkSuQmCC`; + assert.strictEqual( + target.querySelector("div[name=sign] img").dataset.src, + `data:image/png;base64,${MYB64}` + ); + + await editInput(target, ".o_field_widget[name='foo'] input", "grrr"); + assert.strictEqual( + target.querySelector("div[name=sign] img").dataset.src, + `data:image/png;base64,${MYB64}` + ); + + await clickSave(target); + assert.verifySteps(["web_save"]); + assert.strictEqual( + getUnique(target.querySelector(".o_field_signature img")), + "1659692220000" + ); + + await click(target, ".o_field_signature img", { skipVisibilityCheck: true }); + assert.containsOnce(target, ".modal canvas"); + + canvas = target.querySelector(".modal canvas"); + canvas.setAttribute("width", "2px"); + canvas.setAttribute("height", "2px"); + ctx = canvas.getContext("2d"); + ctx.beginPath(); + ctx.strokeStyle = "blue"; + ctx.moveTo(0, 0); + ctx.lineTo(2, 0); + ctx.stroke(); + await triggerEvent(target, ".o_web_sign_signature", "change"); + await click(target, ".modal-footer .btn-primary"); + + const MYB64_2 = `iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABVJREFUGFdjZGD438DAwMDACCJAAAAWHgGCN0++VgAAAABJRU5ErkJggg==`; + assert.notOk(MYB64 === MYB64_2); + assert.strictEqual( + target.querySelector("div[name=sign] img").dataset.src, + `data:image/png;base64,${MYB64_2}` + ); + await clickSave(target); + assert.verifySteps(["web_save"]); + assert.strictEqual( + getUnique(target.querySelector(".o_field_signature img")), + "1659695820000" + ); + } + ); + + QUnit.test("save record with signature field modified by onchange", async function (assert) { + const MYB64 = `iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABRJREFUGFdjZGD438DAwNjACGMAACQlBAMW7JulAAAAAElFTkSuQmCC`; + + serverData.models.partner.fields.foo = { type: "char" }; + serverData.models.partner.onchanges = { + foo: (data) => { + data.sign = MYB64; + }, + }; + + const rec = serverData.models.partner.records.find((rec) => rec.id === 1); + rec.sign = "3 kb"; + rec.write_date = "2022-08-05 08:37:00"; // 1659688620000 + + // 1659692220000, 1659695820000 + const lastUpdates = ["2022-08-05 09:37:00", "2022-08-05 10:37:00"]; + let index = 0; + + await makeView({ + type: "form", + resModel: "partner", + resId: 1, + serverData, + arch: /* xml */ ` + `, + mockRPC(route, { method, args }) { + if (method === "web_save") { + assert.step("web_save"); + args[1].write_date = lastUpdates[index]; + args[1].sign = "4 kb"; + index++; + } + }, + }); + assert.strictEqual( + getUnique(target.querySelector(".o_field_signature img")), + "1659688620000" + ); + await editInput(target, "[name='foo'] input", "grrr"); + assert.strictEqual( + target.querySelector("div[name=sign] img").dataset.src, + `data:image/png;base64,${MYB64}` + ); + + await clickSave(target); + assert.strictEqual( + getUnique(target.querySelector(".o_field_signature img")), + "1659692220000" + ); + assert.verifySteps(["web_save"]); + }); });