[FIX] web: fix binary field in settings form
Steps: - Install sales app. - Go to settings of sales. - See header field there is no download button. Issue: - Binary field should have download button and user should be able to download file with out issue. Cause: - Since Settings is abstact model so it does not contains id field in it and download button visibility depends on id field. Fix: - Extend binary field for setting view to get proper file from related field where actual file is stored. task-3620555 closes odoo/odoo#160070 Signed-off-by: Jorge Pinna Puissant (jpp) <jpp@odoo.com>
This commit is contained in:
@@ -47,19 +47,23 @@ export class BinaryField extends Component {
|
||||
return this.props.record.update(changes);
|
||||
}
|
||||
|
||||
getDownloadData() {
|
||||
return {
|
||||
model: this.props.record.resModel,
|
||||
id: this.props.record.resId,
|
||||
field: this.props.name,
|
||||
filename_field: this.fileName,
|
||||
filename: this.fileName || "",
|
||||
download: true,
|
||||
data: isBinarySize(this.props.record.data[this.props.name])
|
||||
? null
|
||||
: this.props.record.data[this.props.name],
|
||||
};
|
||||
}
|
||||
|
||||
async onFileDownload() {
|
||||
await download({
|
||||
data: {
|
||||
model: this.props.record.resModel,
|
||||
id: this.props.record.resId,
|
||||
field: this.props.name,
|
||||
filename_field: this.fileName,
|
||||
filename: this.fileName || "",
|
||||
download: true,
|
||||
data: isBinarySize(this.props.record.data[this.props.name])
|
||||
? null
|
||||
: this.props.record.data[this.props.name],
|
||||
},
|
||||
data: this.getDownloadData(),
|
||||
url: "/web/content",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
acceptedFileExtensions="props.acceptedFileExtensions"
|
||||
onUploaded.bind="update"
|
||||
>
|
||||
<t t-if="props.record.resId and !props.record.dirty">
|
||||
<t name="download" t-if="props.record.resId and !props.record.dirty">
|
||||
<button
|
||||
class="btn btn-link btn-sm lh-1 fa fa-download o_download_file_button"
|
||||
data-tooltip="Download"
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { BinaryField, binaryField } from "@web/views/fields/binary/binary_field";
|
||||
|
||||
export class SettingsBinaryField extends BinaryField {
|
||||
static template = "web.SettingsBinaryField";
|
||||
|
||||
getDownloadData() {
|
||||
const related = this.props.record.fields[this.props.name].related;
|
||||
return {
|
||||
...super.getDownloadData(),
|
||||
model: this.props.record.fields[related.split(".")[0]].relation,
|
||||
field: related.split(".")[1] ?? related.split(".")[0],
|
||||
id: this.props.record.data[related.split(".")[0]][0],
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const settingsBinaryField = {
|
||||
...binaryField,
|
||||
component: SettingsBinaryField,
|
||||
};
|
||||
|
||||
registry.category("fields").add("base_settings.binary", settingsBinaryField);
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="web.SettingsBinaryField" t-inherit="web.BinaryField" t-inherit-mode="primary">
|
||||
<xpath expr="//t[@name='download']" position="attributes">
|
||||
<attribute name="t-if"/>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services";
|
||||
import { makeFakeLocalizationService, makeMockXHR } from "@web/../tests/helpers/mock_services";
|
||||
import {
|
||||
click,
|
||||
editInput,
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { editSearch } from "@web/../tests/search/helpers";
|
||||
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
|
||||
import { errorService } from "@web/core/errors/error_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
@@ -41,6 +42,25 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
fields: {
|
||||
foo: { string: "Foo", type: "boolean" },
|
||||
bar: { string: "Bar", type: "boolean" },
|
||||
task_id: {
|
||||
string: "many2one field",
|
||||
type: "many2one",
|
||||
relation: "task",
|
||||
default: 100,
|
||||
},
|
||||
file: {
|
||||
string: "related binary field",
|
||||
type: "binary",
|
||||
relation: "task",
|
||||
related: "task_id.file",
|
||||
default: "coucou==\n",
|
||||
},
|
||||
file_name: {
|
||||
string: "related file name",
|
||||
type: "char",
|
||||
related: "task_id.file_name",
|
||||
default: "coucou.txt",
|
||||
},
|
||||
tasks: { string: "one2many field", type: "one2many", relation: "task" },
|
||||
baz: {
|
||||
string: "Baz",
|
||||
@@ -54,7 +74,17 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
},
|
||||
},
|
||||
task: {
|
||||
fields: {},
|
||||
fields: {
|
||||
file: { string: "Binary", type: "binary" },
|
||||
file_name: { string: "File Name", type: "char"}
|
||||
},
|
||||
records: [
|
||||
{
|
||||
id: 100,
|
||||
file: "coucou==\n",
|
||||
file_name: "coucou.txt",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1868,4 +1898,99 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
await click(target.querySelector("button[name='2']"));
|
||||
assert.verifySteps(["/web/action/run"]);
|
||||
});
|
||||
|
||||
QUnit.test("BinaryField is correctly rendered in Settings form view", async function (assert) {
|
||||
|
||||
async function send(data) {
|
||||
assert.ok(data instanceof FormData);
|
||||
assert.strictEqual(
|
||||
data.get("field"),
|
||||
"file",
|
||||
"we should download the field document"
|
||||
);
|
||||
assert.strictEqual(
|
||||
data.get("data"),
|
||||
"coucou==\n",
|
||||
"we should download the correct data"
|
||||
);
|
||||
|
||||
this.status = 200;
|
||||
this.response = new Blob([data.get("data")], { type: "text/plain" });
|
||||
}
|
||||
const MockXHR = makeMockXHR("", send);
|
||||
|
||||
patchWithCleanup(browser, { XMLHttpRequest: MockXHR });
|
||||
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "res.config.settings",
|
||||
serverData,
|
||||
arch: `
|
||||
<form string="Settings" class="oe_form_configuration o_base_settings" js_class="base_settings">
|
||||
<app string="Sale" name="sale">
|
||||
<block title="Title of group Bar">
|
||||
<setting>
|
||||
<field name="task_id" invisible="1"/>
|
||||
<field name="file" filename="file_name"/>
|
||||
<field name="file_name"/>
|
||||
</setting>
|
||||
</block>
|
||||
</app>
|
||||
</form>`,
|
||||
});
|
||||
|
||||
assert.containsOnce(
|
||||
target,
|
||||
'.o_field_widget[name="file"] .fa-download',
|
||||
"Download button should be display in settings form view"
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector('.o_field_widget[name="file"].o_field_binary .o_input').value,
|
||||
"coucou.txt",
|
||||
"the binary field should display the file name in the input"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_field_binary .o_clear_file_button",
|
||||
"there shoud be a button to clear the file"
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_field_char input").value,
|
||||
"coucou.txt",
|
||||
"the filename field should have the file name as value"
|
||||
);
|
||||
|
||||
// Testing the download button in the field
|
||||
// We must avoid the browser to download the file effectively
|
||||
const prom = makeDeferred();
|
||||
const downloadOnClick = (ev) => {
|
||||
const target = ev.target;
|
||||
if (target.tagName === "A" && "download" in target.attributes) {
|
||||
ev.preventDefault();
|
||||
document.removeEventListener("click", downloadOnClick);
|
||||
prom.resolve();
|
||||
}
|
||||
};
|
||||
document.addEventListener("click", downloadOnClick);
|
||||
registerCleanup(() => document.removeEventListener("click", downloadOnClick));
|
||||
await click(target.querySelector(".fa-download"));
|
||||
await prom;
|
||||
|
||||
await click(target.querySelector(".o_field_binary .o_clear_file_button"));
|
||||
|
||||
assert.isNotVisible(
|
||||
target.querySelector(".o_field_binary input"),
|
||||
"the input should be hidden"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_field_binary .o_select_file_button",
|
||||
"there should be a button to upload the file"
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_field_char input").value,
|
||||
"",
|
||||
"the filename field should be empty since we removed the file"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user