[FIX] web: image/sign field doesn't correctly update its unique
Before this commit, if the value of an image/sign field is modified by an
onchange and the record is saved manually, the old image is displayed.
Why:
ImageField's rawCacheKey, which is used to know if the image has been updated,
is not updated. So we will reuse the old image.
Solution:
rawCacheKey becomes a getter that always returns the current value of __last_updated.
How to reproduce:
- Go to a form view with a char field and an image field
- Edit the char field
- An onchange is performed and modifies the value of the image field
- The new image is displayed
- Click on the save button
Before this commit:
The old image is displayed
After this commit:
The new image is displayed
closes odoo/odoo#142471
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Signed-off-by: Francois Georis (fge) <fge@odoo.com>
This commit is contained in:
@@ -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"),
|
||||
|
||||
@@ -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 */ `
|
||||
<form>
|
||||
<field name="foo"/>
|
||||
<field name="document" widget="image" />
|
||||
</form>`,
|
||||
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: `
|
||||
<form>
|
||||
<field name="foo" />
|
||||
<field name="document" widget="image" required="1" />
|
||||
</form>`,
|
||||
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";
|
||||
|
||||
@@ -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 */ `
|
||||
<form>
|
||||
<field name="foo" />
|
||||
<field name="sign" widget="signature" />
|
||||
</form>`,
|
||||
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 */ `
|
||||
<form>
|
||||
<field name="foo" />
|
||||
<field name="sign" widget="signature" />
|
||||
</form>`,
|
||||
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"]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user