[FIX] web: form always edit: a virtual record can be discarded

Before this commit, a virtual record in form view always had the
o_form_dirty class, which is problematic when a tour ends with a virtual record.

After this commit, a virtual non-dirty record in form view doesn't have
the class o_form_dirty, and doesn't have the o_form_saved either as that doesn't make more sense.

closes odoo/odoo#102153

X-original-commit: 0041b9fd8eb75d5b680d94d491e2b9f684b2d0c1
Related: odoo/enterprise#32282
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Lucas Perais
2022-10-04 20:58:01 +02:00
parent 5992fac7aa
commit b072e1bd5b
3 changed files with 40 additions and 6 deletions
@@ -225,7 +225,7 @@ export class FormCompiler extends ViewCompiler {
? `d-flex {{ uiService.size < ${SIZES.XXL} ? "flex-column" : "flex-nowrap h-100" }}`
: "d-block";
const stateClasses =
"{{ props.record.isVirtual or props.record.isDirty ? 'o_form_dirty' : 'o_form_saved' }}";
"{{ props.record.isDirty ? 'o_form_dirty' : !props.record.isVirtual ? 'o_form_saved' : '' }}";
const form = createElement("div", {
"t-att-class": "props.class",
"t-attf-class": `{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} ${displayClasses} ${stateClasses}`,
@@ -38,7 +38,7 @@ QUnit.module("Form Compiler", (hooks) => {
const arch = /*xml*/ `<form><div>lol</div></form>`;
const expected = /*xml*/ `
<t>
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-block {{ props.record.isVirtual or props.record.isDirty ? 'o_form_dirty' : 'o_form_saved' }}" class="o_form_nosheet" t-ref="compiled_view_root">
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-block {{ props.record.isDirty ? 'o_form_dirty' : !props.record.isVirtual ? 'o_form_saved' : '' }}" class="o_form_nosheet" t-ref="compiled_view_root">
<div>lol</div>
</div>
</t>`;
@@ -52,7 +52,7 @@ QUnit.module("Form Compiler", (hooks) => {
const arch = /*xml*/ `<form><field name="test"/><label for="test" string=""/></form>`;
const expected = /*xml*/ `
<t>
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-block {{ props.record.isVirtual or props.record.isDirty ? 'o_form_dirty' : 'o_form_saved' }}" class="o_form_nosheet" t-ref="compiled_view_root">
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-block {{ props.record.isDirty ? 'o_form_dirty' : !props.record.isVirtual ? 'o_form_saved' : '' }}" class="o_form_nosheet" t-ref="compiled_view_root">
<Field id="'test'" name="'test'" record="props.record" fieldInfo="props.archInfo.fieldNodes['test']" />
<FormLabel t-props="{id:'test',fieldName:'test',record:props.record,fieldInfo:props.archInfo.fieldNodes['test'],className:&quot;&quot;}" string="\`\`" />
</div>
@@ -65,7 +65,7 @@ QUnit.module("Form Compiler", (hooks) => {
const arch = /*xml*/ `<form><div class="someClass">lol<field name="display_name"/></div></form>`;
const expected = /*xml*/ `
<t>
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-block {{ props.record.isVirtual or props.record.isDirty ? 'o_form_dirty' : 'o_form_saved' }}" class="o_form_nosheet" t-ref="compiled_view_root">
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-block {{ props.record.isDirty ? 'o_form_dirty' : !props.record.isVirtual ? 'o_form_saved' : '' }}" class="o_form_nosheet" t-ref="compiled_view_root">
<div class="someClass">
lol
<Field id="'display_name'" name="'display_name'" record="props.record" fieldInfo="props.archInfo.fieldNodes['display_name']"/>
@@ -149,7 +149,7 @@ QUnit.module("Form Compiler", (hooks) => {
const expected = /*xml*/ `
<t>
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-block {{ props.record.isVirtual or props.record.isDirty ? 'o_form_dirty' : 'o_form_saved' }}" class="o_form_nosheet" t-ref="compiled_view_root">
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-block {{ props.record.isDirty ? 'o_form_dirty' : !props.record.isVirtual ? 'o_form_saved' : '' }}" class="o_form_nosheet" t-ref="compiled_view_root">
<div class="o_form_statusbar position-relative d-flex justify-content-between border-bottom"><StatusBarButtons readonly="!props.record.isInEdition"/></div>
<div>someDiv</div>
</div>
@@ -171,7 +171,7 @@ QUnit.module("Form Compiler", (hooks) => {
const expected = /*xml*/ `
<t>
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-flex {{ uiService.size &lt; 6 ? &quot;flex-column&quot; : &quot;flex-nowrap h-100&quot; }} {{ props.record.isVirtual or props.record.isDirty ? 'o_form_dirty' : 'o_form_saved' }}" t-ref="compiled_view_root">
<div t-att-class="props.class" t-attf-class="{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} d-flex {{ uiService.size &lt; 6 ? &quot;flex-column&quot; : &quot;flex-nowrap h-100&quot; }} {{ props.record.isDirty ? 'o_form_dirty' : !props.record.isVirtual ? 'o_form_saved' : '' }}" t-ref="compiled_view_root">
<div class="o_form_sheet_bg">
<div class="o_form_statusbar position-relative d-flex justify-content-between border-bottom"><StatusBarButtons readonly="!props.record.isInEdition"/></div>
<div>someDiv</div>
@@ -4249,6 +4249,10 @@ QUnit.module("Views", (hooks) => {
},
});
assert.doesNotHaveClass(
target.querySelector(".o_form_editable"),
"o_form_saved o_form_dirty"
);
// edit the foo field
assert.strictEqual(
target.querySelector(".o_field_widget[name=foo] input").value,
@@ -4257,6 +4261,9 @@ QUnit.module("Views", (hooks) => {
);
await editInput(target, ".o_field_widget[name=foo] input", "DEF");
assert.hasClass(target.querySelector(".o_form_editable"), "o_form_dirty");
assert.doesNotHaveClass(target.querySelector(".o_form_editable"), "o_form_saved");
// discard the changes and check it has properly been discarded
assert.strictEqual(
target.querySelector(".o_field_widget[name=foo] input").value,
@@ -4264,6 +4271,11 @@ QUnit.module("Views", (hooks) => {
"input should be DEF"
);
await clickDiscard(target);
assert.doesNotHaveClass(
target.querySelector(".o_form_editable"),
"o_form_saved o_form_dirty"
);
assert.strictEqual(
target.querySelector(".o_field_widget[name=foo] input").value,
"ABC",
@@ -4287,6 +4299,28 @@ QUnit.module("Views", (hooks) => {
assert.verifySteps(["history-back"]);
});
QUnit.test("save a new dirty record", async (assert) => {
await makeView({
type: "form",
resModel: "partner",
serverData,
arch: `
<form>
<field name="foo"/>
</form>
`,
});
assert.doesNotHaveClass(
target.querySelector(".o_form_editable"),
"o_form_saved o_form_dirty"
);
await editInput(target, ".o_field_widget[name=foo] input", "DEF");
await clickSave(target);
assert.hasClass(target.querySelector(".o_form_editable"), "o_form_saved");
assert.doesNotHaveClass(target.querySelector(".o_form_editable"), "o_form_dirty");
});
QUnit.test("discard changes on a duplicated record", async function (assert) {
await makeView({
type: "form",