summaryrefslogtreecommitdiff
path: root/addons/web
diff options
context:
space:
mode:
authorChristophe Matthieu <chm@odoo.com>2018-07-26 09:55:01 +0200
committerChristophe Matthieu <chm@odoo.com>2018-08-13 17:09:28 +0200
commit61eef73b52c078daaae37f8d57e93830a2e53410 (patch)
tree8b144174691de487a584bf8ac449fb55d3ad12ab /addons/web
parent149eb38b07cace0ec2118d214c56995402008efc (diff)
[IMP] base, *: use a many2one for the company report layout
Before this rev. one could set the report layout on the company using a hardcoded list (background, clean, standard, etc.). Other modules (typically accounting modules) could add options in this list. The used template was built on the layout key. Now, the layout is a many2one field to a newly created model `report.layout`, which is linked to a view with the layout architecture. This gives more control to customize reports and create a new layout (without creating a python module that extend the layout selection).
Diffstat (limited to 'addons/web')
-rw-r--r--addons/web/__manifest__.py3
-rw-r--r--addons/web/data/report_layout.xml25
-rw-r--r--addons/web/data/res_company.xml8
-rw-r--r--addons/web/models/__init__.py1
-rw-r--r--addons/web/models/report_layout.py17
-rw-r--r--addons/web/security/ir.model.access.csv2
-rw-r--r--addons/web/static/img/preview_background.pngbin0 -> 10185 bytes
-rw-r--r--addons/web/static/img/preview_boxed.pngbin0 -> 5525 bytes
-rw-r--r--addons/web/static/img/preview_clean.pngbin0 -> 5257 bytes
-rw-r--r--addons/web/static/img/preview_standard.pngbin0 -> 5686 bytes
-rw-r--r--addons/web/static/pdf/preview_background.pdfbin0 -> 197777 bytes
-rw-r--r--addons/web/static/pdf/preview_boxed.pdfbin0 -> 42109 bytes
-rw-r--r--addons/web/static/pdf/preview_clean.pdfbin0 -> 35495 bytes
-rw-r--r--addons/web/static/pdf/preview_standard.pdfbin0 -> 27399 bytes
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js51
-rw-r--r--addons/web/static/src/js/fields/field_registry.js4
-rw-r--r--addons/web/static/src/js/fields/special_fields.js67
-rw-r--r--addons/web/static/src/scss/form_view.scss3
-rw-r--r--addons/web/static/tests/fields/basic_fields_tests.js41
-rw-r--r--addons/web/static/tests/fields/special_fields.js47
-rw-r--r--addons/web/views/report_templates.xml76
21 files changed, 216 insertions, 129 deletions
diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py
index ab31c49733b..ea15b2c22df 100644
--- a/addons/web/__manifest__.py
+++ b/addons/web/__manifest__.py
@@ -15,8 +15,11 @@ This module provides the core of the Odoo Web Client.
'depends': ['base'],
'auto_install': True,
'data': [
+ 'security/ir.model.access.csv',
'views/webclient_templates.xml',
'views/report_templates.xml',
+ 'data/res_company.xml',
+ 'data/report_layout.xml',
],
'qweb': [
"static/src/xml/base.xml",
diff --git a/addons/web/data/report_layout.xml b/addons/web/data/report_layout.xml
new file mode 100644
index 00000000000..afa8130d73a
--- /dev/null
+++ b/addons/web/data/report_layout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data>
+ <record id="report_layout_background" model="report.layout">
+ <field name="view_id" ref="web.external_layout_background"/>
+ <field name="image">/web/static/img/preview_background.png</field>
+ <field name="pdf">/web/static/pdf/preview_background.pdf</field>
+ </record>
+ <record id="report_layout_boxed" model="report.layout">
+ <field name="view_id" ref="web.external_layout_boxed"/>
+ <field name="image">/web/static/img/preview_boxed.png</field>
+ <field name="pdf">/web/static/pdf/preview_boxed.pdf</field>
+ </record>
+ <record id="report_layout_clean" model="report.layout">
+ <field name="view_id" ref="web.external_layout_clean"/>
+ <field name="image">/web/static/img/preview_clean.png</field>
+ <field name="pdf">/web/static/pdf/preview_clean.pdf</field>
+ </record>
+ <record id="report_layout_standard" model="report.layout">
+ <field name="view_id" ref="web.external_layout_standard"/>
+ <field name="image">/web/static/img/preview_standard.png</field>
+ <field name="pdf">/web/static/pdf/preview_standard.pdf</field>
+ </record>
+ </data>
+</odoo> \ No newline at end of file
diff --git a/addons/web/data/res_company.xml b/addons/web/data/res_company.xml
new file mode 100644
index 00000000000..44db6af8931
--- /dev/null
+++ b/addons/web/data/res_company.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data noupdate="1">
+ <record id="base.main_company" model="res.company">
+ <field name="external_report_layout_id" ref="web.external_layout_standard"/>
+ </record>
+ </data>
+</odoo> \ No newline at end of file
diff --git a/addons/web/models/__init__.py b/addons/web/models/__init__.py
index 29ae76aa206..cafe5fa2816 100644
--- a/addons/web/models/__init__.py
+++ b/addons/web/models/__init__.py
@@ -4,3 +4,4 @@
from . import ir_qweb
from . import ir_http
from . import models
+from . import report_layout
diff --git a/addons/web/models/report_layout.py b/addons/web/models/report_layout.py
new file mode 100644
index 00000000000..f70129ec89d
--- /dev/null
+++ b/addons/web/models/report_layout.py
@@ -0,0 +1,17 @@
+# -*- coding: ascii -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import logging
+
+from odoo import fields, models
+
+_logger = logging.getLogger(__name__)
+
+
+class View(models.Model):
+
+ _name = "report.layout"
+
+ view_id = fields.Many2one('ir.ui.view', 'Document Template', required=True)
+ image = fields.Char(string="Preview image src")
+ pdf = fields.Char(string="Preview pdf src")
diff --git a/addons/web/security/ir.model.access.csv b/addons/web/security/ir.model.access.csv
new file mode 100644
index 00000000000..fb9c59d7b89
--- /dev/null
+++ b/addons/web/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_report_layout,access_report_layout,model_report_layout,,1,1,1,1
diff --git a/addons/web/static/img/preview_background.png b/addons/web/static/img/preview_background.png
new file mode 100644
index 00000000000..cab3eae0dd0
--- /dev/null
+++ b/addons/web/static/img/preview_background.png
Binary files differ
diff --git a/addons/web/static/img/preview_boxed.png b/addons/web/static/img/preview_boxed.png
new file mode 100644
index 00000000000..963e0b7f62b
--- /dev/null
+++ b/addons/web/static/img/preview_boxed.png
Binary files differ
diff --git a/addons/web/static/img/preview_clean.png b/addons/web/static/img/preview_clean.png
new file mode 100644
index 00000000000..0318421b016
--- /dev/null
+++ b/addons/web/static/img/preview_clean.png
Binary files differ
diff --git a/addons/web/static/img/preview_standard.png b/addons/web/static/img/preview_standard.png
new file mode 100644
index 00000000000..0bd596ed9b5
--- /dev/null
+++ b/addons/web/static/img/preview_standard.png
Binary files differ
diff --git a/addons/web/static/pdf/preview_background.pdf b/addons/web/static/pdf/preview_background.pdf
new file mode 100644
index 00000000000..d33687c16b8
--- /dev/null
+++ b/addons/web/static/pdf/preview_background.pdf
Binary files differ
diff --git a/addons/web/static/pdf/preview_boxed.pdf b/addons/web/static/pdf/preview_boxed.pdf
new file mode 100644
index 00000000000..e73c1131d04
--- /dev/null
+++ b/addons/web/static/pdf/preview_boxed.pdf
Binary files differ
diff --git a/addons/web/static/pdf/preview_clean.pdf b/addons/web/static/pdf/preview_clean.pdf
new file mode 100644
index 00000000000..4f1c42b4814
--- /dev/null
+++ b/addons/web/static/pdf/preview_clean.pdf
Binary files differ
diff --git a/addons/web/static/pdf/preview_standard.pdf b/addons/web/static/pdf/preview_standard.pdf
new file mode 100644
index 00000000000..4d537376850
--- /dev/null
+++ b/addons/web/static/pdf/preview_standard.pdf
Binary files differ
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 399647d1dbe..dfcbbc71a80 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -2826,56 +2826,6 @@ var AceEditor = DebouncedField.extend({
},
});
-var ImageSelection = AbstractField.extend({
- supportedFieldTypes: ['selection'],
- events: _.extend({}, AbstractField.prototype.events, {
- 'click img': '_onImgClicked',
- }),
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @private
- */
- _render: function () {
- var self = this;
- this.$el.empty();
- _.each(this.nodeOptions, function (val, key) {
- var $container = $('<div>').addClass('col-3 text-center');
- var $img = $('<img>')
- .addClass('img img-fluid img-thumbnail ml16')
- .toggleClass('btn-info', key === self.value)
- .attr('src', val.image_link)
- .data('key', key);
- $container.append($img);
- if (val.preview_link) {
- var $previewLink = $('<a>')
- .text('Preview')
- .attr('href', val.preview_link)
- .attr('target', '_blank');
- $container.append($previewLink);
- }
- self.$el.append($container);
- });
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @private
- * @param {MouseEvent} event
- */
- _onImgClicked: function (event) {
- this._setValue($(event.currentTarget).data('key'));
- },
-});
-
return {
TranslatableFieldMixin: TranslatableFieldMixin,
DebouncedField: DebouncedField,
@@ -2906,7 +2856,6 @@ return {
HandleWidget: HandleWidget,
InputField: InputField,
AttachmentImage: AttachmentImage,
- ImageSelection: ImageSelection,
LabelSelection: LabelSelection,
StateSelectionWidget: StateSelectionWidget,
FavoriteWidget: FavoriteWidget,
diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js
index df8ffc9dd5b..c94c315cfa6 100644
--- a/addons/web/static/src/js/fields/field_registry.js
+++ b/addons/web/static/src/js/fields/field_registry.js
@@ -43,7 +43,6 @@ registry
.add('percentage', basic_fields.FieldPercentage)
.add('priority', basic_fields.PriorityWidget)
.add('attachment_image', basic_fields.AttachmentImage)
- .add('image_selection', basic_fields.ImageSelection)
.add('label_selection', basic_fields.LabelSelection)
.add('state_selection', basic_fields.StateSelectionWidget)
.add('kanban_state_selection', basic_fields.StateSelectionWidget)
@@ -81,5 +80,6 @@ registry
// Special fields
registry
- .add('timezone_mismatch', special_fields.FieldTimezoneMismatch);
+ .add('timezone_mismatch', special_fields.FieldTimezoneMismatch)
+ .add('report_layout', special_fields.FieldReportLayout);
});
diff --git a/addons/web/static/src/js/fields/special_fields.js b/addons/web/static/src/js/fields/special_fields.js
index 3db57c5e158..5a6aa8cfd28 100644
--- a/addons/web/static/src/js/fields/special_fields.js
+++ b/addons/web/static/src/js/fields/special_fields.js
@@ -102,8 +102,75 @@ var FieldTimezoneMismatch = FieldSelection.extend({
},
});
+var FieldReportLayout = relational_fields.FieldMany2One.extend({
+ supportedFieldTypes: ['many2one', 'selection'],
+ events: _.extend({}, relational_fields.FieldMany2One.prototype.events, {
+ 'click img': '_onImgClicked',
+ }),
+
+ willStart: function () {
+ var self = this;
+ this.previews = {};
+ return this._super()
+ .then(function () {
+ return self._rpc({
+ model: 'report.layout',
+ method: "search_read"
+ }).then(function (values) {
+ self.previews = values;
+ });
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ * @private
+ */
+ _render: function () {
+ var self = this;
+ this.$el.empty();
+ var value = _.isObject(this.value) ? this.value.data.id : this.value;
+ _.each(this.previews, function (val) {
+ var $container = $('<div>').addClass('col-3 text-center');
+ var $img = $('<img>')
+ .addClass('img img-fluid img-thumbnail ml16')
+ .toggleClass('btn-info', val.view_id[0] === value)
+ .attr('src', val.image)
+ .data('key', val.view_id[0]);
+ $container.append($img);
+ if (val.pdf) {
+ var $previewLink = $('<a>')
+ .text('Preview')
+ .attr('href', val.pdf)
+ .attr('target', '_blank');
+ $container.append($previewLink);
+ }
+ self.$el.append($container);
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onImgClicked: function (event) {
+ this._setValue($(event.currentTarget).data('key'));
+ },
+});
+
+
return {
FieldTimezoneMismatch: FieldTimezoneMismatch,
+ FieldReportLayout: FieldReportLayout,
};
});
diff --git a/addons/web/static/src/scss/form_view.scss b/addons/web/static/src/scss/form_view.scss
index 6f5e955e652..367d9854563 100644
--- a/addons/web/static/src/scss/form_view.scss
+++ b/addons/web/static/src/scss/form_view.scss
@@ -606,7 +606,8 @@
}
}
&.o_company_document_layout {
- .image_selection_container {
+ .report_layout_container {
+ display: inline-block;
div {
display: inline-block;
img {
diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js
index 1855b032bf3..63e677f367d 100644
--- a/addons/web/static/tests/fields/basic_fields_tests.js
+++ b/addons/web/static/tests/fields/basic_fields_tests.js
@@ -40,8 +40,6 @@ QUnit.module('basic_fields', {
selection: {string: "Selection", type: "selection", searchable:true,
selection: [['normal', 'Normal'],['blocked', 'Blocked'],['done', 'Done']]},
document: {string: "Binary", type: "binary"},
- image_selection: {string: "Image Selection", type: "selection", searchable:true,
- selection: [['background', 'Background'],['boxed', 'Boxed'],['clean', 'Clean'],['standard', 'Standard']]},
},
records: [{
id: 1,
@@ -4828,45 +4826,6 @@ QUnit.module('basic_fields', {
form.destroy();
});
-
- QUnit.module('FieldImageSelection');
-
- QUnit.test('image selection widget in form view', function (assert) {
- assert.expect(3);
-
- var nodeOptions = {
- background: {
- image_link: '/base/static/img/preview_background.png',
- preview_link: '/base/static/pdf/preview_background.pdf',
- },
- boxed: {
- image_link: '/base/static/img/preview_boxed.png',
- preview_link: '/base/static/pdf/preview_boxed.pdf',
- },
- };
- var form = createView({
- View: FormView,
- model: 'partner',
- data: this.data,
- arch: '<form>' +
- '<field name="image_selection" widget="image_selection"' +
- ' options=\'' + JSON.stringify(nodeOptions) + '\'/> '+
- '</form>',
- res_id: 2,
- });
-
- assert.strictEqual(form.$('.img.img-fluid').length, 2,
- "Two images should be rendered");
- assert.strictEqual(form.$('.img.btn-info').length, 0,
- "No image should be selected");
-
- // select first image
- form.$(".img.img-fluid:first").click();
- assert.ok(form.$(".img.img-fluid:first").hasClass('btn-info'),
- "First image should be selected");
-
- form.destroy();
- });
});
});
});
diff --git a/addons/web/static/tests/fields/special_fields.js b/addons/web/static/tests/fields/special_fields.js
index 6786f6783b7..3592f813fae 100644
--- a/addons/web/static/tests/fields/special_fields.js
+++ b/addons/web/static/tests/fields/special_fields.js
@@ -213,6 +213,53 @@ QUnit.module('special_fields', {
form.destroy();
});
+ QUnit.module('FieldReportLayout');
+
+ QUnit.test('report_layout widget in form view', function (assert) {
+ assert.expect(3);
+
+ this.data['report.layout'] = {
+ fields: {
+ view_id: {string: "Document Template", type: "many2one", relation: "product"},
+ image: {string: "Preview image src", type: "char"},
+ pdf: {string: "Preview pdf src", type: "char"}
+ },
+ records: [{
+ id: 1,
+ view_id: 37,
+ image: "/web/static/toto.png",
+ pdf: "/web/static/toto.pdf",
+ }, {
+ id: 2,
+ view_id: 41,
+ image: "/web/static/tata.png",
+ pdf: "/web/static/tata.pdf",
+ }]
+ };
+ this.data.partner.records[1].product_id = false;
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form>' +
+ '<field name="product_id" widget="report_layout"/> '+
+ '</form>',
+ res_id: 2,
+ });
+
+ assert.strictEqual(form.$('.img.img-fluid').length, 2,
+ "Two images should be rendered");
+ assert.strictEqual(form.$('.img.btn-info').length, 0,
+ "No image should be selected");
+
+ // select first image
+ form.$(".img.img-fluid:first").click();
+ assert.ok(form.$(".img.img-fluid:first").hasClass('btn-info'),
+ "First image should be selected");
+
+ form.destroy();
+ });
});
});
});
diff --git a/addons/web/views/report_templates.xml b/addons/web/views/report_templates.xml
index 433aedfbcf9..b8b954c8b44 100644
--- a/addons/web/views/report_templates.xml
+++ b/addons/web/views/report_templates.xml
@@ -55,7 +55,7 @@
t-att-data-report-landscape="data_report_landscape"
t-att-web-base-url="web_base_url">
<head>
- <meta charset="utf-8" />
+ <meta charset="utf-8"/>
<meta name="viewport" content="initial-scale=1"/>
<title><t t-esc="title or 'Odoo Report'"/></title>
<t t-call-assets="web.assets_common" t-js="false"/>
@@ -179,8 +179,8 @@
</div>
<div class="article o_report_layout_background" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id">
- <t t-call="web.address_layout" />
- <t t-raw="0" />
+ <t t-call="web.address_layout"/>
+ <t t-raw="0"/>
</div>
<div class="footer o_background_footer">
@@ -219,8 +219,8 @@
</div>
<div class="article o_report_layout_boxed" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id">
- <t t-call="web.address_layout" />
- <t t-raw="0" />
+ <t t-call="web.address_layout"/>
+ <t t-raw="0"/>
</div>
<div class="footer o_boxed_footer">
@@ -258,8 +258,8 @@
</div>
<div class="article o_report_layout_clean" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id">
- <t t-call="web.address_layout" />
- <t t-raw="0" />
+ <t t-call="web.address_layout"/>
+ <t t-raw="0"/>
</div>
<div class="footer o_clean_footer">
@@ -306,8 +306,8 @@
</div>
<div class="article o_report_layout_standard" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id">
- <t t-call="web.address_layout" />
- <t t-raw="0" />
+ <t t-call="web.address_layout"/>
+ <t t-raw="0"/>
</div>
<div class="footer o_standard_footer">
@@ -333,32 +333,40 @@
<!-- Public api: layout to t-call from reports -->
<template id="external_layout">
- <!-- Multicompany -->
- <t t-if="not o and doc">
- <t t-set="o" t-value="doc"/>
- </t>
+ <t t-if="not o" t-set="o" t-value="doc"/>
- <t t-if="o and 'company_id' in o">
- <t t-set="company" t-value="o.company_id.sudo()"/>
- </t>
- <t t-if="not o or not 'company_id' in o">
- <t t-set="company" t-value="res_company"/>
+ <t t-if="not company">
+ <!-- Multicompany -->
+ <t t-if="company_id">
+ <t t-set="company" t-value="company_id"/>
+ </t>
+ <t t-elif="o and 'company_id' in o">
+ <t t-set="company" t-value="o.company_id.sudo()"/>
+ </t>
+ <t t-else="else">
+ <t t-set="company" t-value="res_company"/>
+ </t>
</t>
- <t t-call="web.external_layout_{{company.external_report_layout or 'standard'}}"><t t-raw="0"/></t>
+ <t t-if="company.external_report_layout_id" t-call="{{company.external_report_layout_id.key}}"><t t-raw="0"/></t>
+ <t t-else="else" t-raw="0"/>
+
</template>
<template id="internal_layout">
- <!-- Multicompany -->
- <t t-if="company_id">
- <t t-set="company" t-value="company_id"/>
- <t t-set="res_company" t-value="company_id"/>
- </t>
- <t t-if="not company_id">
- <t t-set="company" t-value="res_company"/>
- </t>
- <t t-if="not o and doc">
- <t t-set="o" t-value="doc"/>
+ <t t-if="not o" t-set="o" t-value="doc"/>
+
+ <t t-if="not company">
+ <!-- Multicompany -->
+ <t t-if="company_id">
+ <t t-set="company" t-value="company_id"/>
+ </t>
+ <t t-elif="o and 'company_id' in o">
+ <t t-set="company" t-value="o.company_id.sudo()"/>
+ </t>
+ <t t-else="else">
+ <t t-set="company" t-value="res_company"/>
+ </t>
</t>
<div class="header">
@@ -379,18 +387,16 @@
</div>
</div>
<div class="article" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id">
- <t t-raw="0" />
+ <t t-raw="0"/>
</div>
</template>
<template id="basic_layout">
- <t t-if="not o and doc">
- <t t-set="o" t-value="doc"/>
- </t>
+ <t t-if="not o" t-set="o" t-value="doc"/>
<t t-call="web.html_container">
<div class="article" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id">
- <t t-raw="0" />
+ <t t-raw="0"/>
</div>
</t>
</template>
@@ -399,6 +405,7 @@
<!-- Demo reports definition -->
<template id="preview_internalreport">
<t t-call="web.html_container">
+ <t t-set="o" t-value="res_company"/>
<t t-call="web.internal_layout">
<div class="page">
<p>This is a sample of an internal report.</p>
@@ -408,6 +415,7 @@
</template>
<template id="preview_externalreport">
<t t-call="web.html_container">
+ <t t-set="o" t-value="res_company"/>
<t t-call="web.external_layout">
<div class="page">
<p>This is a sample of an external report.</p>