[FIX] web: Fixed the printing of reports.
A new exception has been added to detect inconsistent reports. However, many are not structured as expected. Thus, the change consists mainly of checking only when the print has to be separated in part to attach it to the respective documents. We now take the page tag instead of the article, because the article tag is in the layout, so to separate the file, the t-call had to be inside the t-foreach (for the separation) and so possibly read the assets several times. Since all templates have the first tag (in the t-foreach) tag page, the choice is more sensible. The javascripts files used in readonly were taken out of the editing assets. A turn was made but not activated, it works locally, but on the runbot, an error is triggered when generating assets after choosing the settings.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
<address t-field="o.partner_id" t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
|
||||
<div t-if="o.partner_id.vat" class="mt16"><t t-esc="o.company_id.country_id.vat_label or 'Tax ID'"/>: <span t-field="o.partner_id.vat"/></div>
|
||||
</t>
|
||||
<div class="page">
|
||||
<div class="page" t-att-data-model="o._name" t-att-data-id="o.id">
|
||||
<h2>
|
||||
<span t-if="o.type == 'out_invoice' and o.state in ('open', 'in_payment', 'paid')">Invoice</span>
|
||||
<span t-if="o.type == 'out_invoice' and o.state == 'draft'">Draft Invoice</span>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
odoo.define('stock.tour', function (require) {
|
||||
'use strict';
|
||||
|
||||
var tour = require('web_tour.tour');
|
||||
|
||||
tour.register('stock', {
|
||||
test: true,
|
||||
url: '/web?debug=assets#action=stock.action_stock_config_settings',
|
||||
},
|
||||
[
|
||||
{
|
||||
content: "wait web client",
|
||||
trigger: ".o_base_settings",
|
||||
run: function() {}
|
||||
},
|
||||
{
|
||||
content: "active 'Multi-Step Routes'",
|
||||
trigger: '.o_field_boolean[name="group_stock_adv_location"]',
|
||||
run: function() {
|
||||
if (!this.$anchor.find('input').prop('checked')) {
|
||||
this.$anchor.find('label').click();
|
||||
$('.o_statusbar_buttons button[name="execute"]').click();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
content: "list of product variants",
|
||||
trigger: '.o_sub_menu .oe_secondary_submenu [data-menu-xmlid="stock.menu_product_variant_config_stock"]',
|
||||
extra_trigger: '.o_sub_menu .oe_secondary_submenu:has(a[data-menu-xmlid="stock.menu_routes_config"])',
|
||||
timeout: 5000,
|
||||
},
|
||||
{
|
||||
content: "select a product variant",
|
||||
trigger: '.oe_kanban_global_click:first',
|
||||
},
|
||||
{
|
||||
content: "click on routes",
|
||||
trigger: 'button.oe_stat_button i.fa-cogs',
|
||||
},
|
||||
{
|
||||
content: "print the report",
|
||||
trigger: 'button[name="print_report"]',
|
||||
},
|
||||
{
|
||||
content: "print inside the report and check the breadcrum",
|
||||
trigger: '.o_content iframe .o_report_stock_rule_rule_name',
|
||||
extra_trigger: 'ol.breadcrumb li:eq(2).active',
|
||||
},
|
||||
{
|
||||
content: "print the report",
|
||||
trigger: 'ol.breadcrumb li:eq(3).active',
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
});
|
||||
@@ -14,3 +14,6 @@ from . import test_packing
|
||||
from . import test_packing_neg
|
||||
from . import test_proc_rule
|
||||
from . import test_wise_operator
|
||||
|
||||
# TODO enable this test: error on runbot to create asset and open the iframe
|
||||
# from . import test_ui
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo.tests
|
||||
|
||||
@odoo.tests.tagged('post_install', '-at_install')
|
||||
class TestUi(odoo.tests.HttpCase):
|
||||
|
||||
def test_01_admin_stock_route(self):
|
||||
self.phantom_js("/web", "odoo.__DEBUG__.services['web_tour.tour'].run('stock')", "odoo.__DEBUG__.services['web_tour.tour'].tours.stock.ready", login='admin')
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
<template id="stock_assets_backend" name="mrp_workorder assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/stock/static/tests/route_tour.js"></script>
|
||||
<script type="text/javascript" src="/stock/static/src/js/stock_traceability_report_backend.js"></script>
|
||||
<script type="text/javascript" src="/stock/static/src/js/stock_traceability_report_widgets.js"></script>
|
||||
<link rel="stylesheet" type="text/scss" href="/stock/static/src/scss/stock_traceability_report.scss"/>
|
||||
|
||||
@@ -14,7 +14,6 @@ var trusted_origin = utils.build_origin(trusted_protocol, trusted_host);
|
||||
|
||||
// Patch the editor's behavior when it is launched inside an iframe.
|
||||
if (window.self !== window.top) {
|
||||
$(document.body).addClass('o_in_iframe'); // in order to apply css rules
|
||||
|
||||
// And now we chain some deferred to `save` and `cancel` in order to inform
|
||||
// the report's client action that the actions are done.
|
||||
@@ -60,29 +59,6 @@ if (window.self !== window.top) {
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Allow to send commands to the webclient when the editor is disabled.
|
||||
if (window.location.search.indexOf('enable_editor') === -1) {
|
||||
// `do_action` command
|
||||
$('[res-id][res-model][view-type]')
|
||||
.wrap('<a/>')
|
||||
.attr('href', '#')
|
||||
.on('click', function (ev) {
|
||||
ev.preventDefault();
|
||||
var action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': $(this).attr('view-type'),
|
||||
'view_mode': $(this).attr('view-mode') || $(this).attr('view-type'),
|
||||
'res_id': Number($(this).attr('res-id')),
|
||||
'res_model': $(this).attr('res-model'),
|
||||
'views': [[$(this).attr('view-id') || false, $(this).attr('view-type')]],
|
||||
};
|
||||
window.parent.postMessage({
|
||||
'message': 'report:do_action',
|
||||
'action': action,
|
||||
}, trusted_origin);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
options.registry.many2one.include({
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
odoo.define('report', function (require) {
|
||||
'use strict';
|
||||
|
||||
require('web.dom_ready');
|
||||
var utils = require('report.utils');
|
||||
|
||||
if (window.self === window.top) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(document.body).addClass('o_in_iframe'); // in order to apply css rules
|
||||
|
||||
var web_base_url = $('html').attr('web-base-url');
|
||||
var trusted_host = utils.get_host_from_url(web_base_url);
|
||||
var trusted_protocol = utils.get_protocol_from_url(web_base_url);
|
||||
var trusted_origin = utils.build_origin(trusted_protocol, trusted_host);
|
||||
|
||||
// Allow to send commands to the webclient when the editor is disabled.
|
||||
if (window.location.search.indexOf('enable_editor') === -1) {
|
||||
// `do_action` command
|
||||
$('[res-id][res-model][view-type]')
|
||||
.wrap('<a/>')
|
||||
.attr('href', '#')
|
||||
.on('click', function (ev) {
|
||||
ev.preventDefault();
|
||||
var action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': $(this).attr('view-type'),
|
||||
'view_mode': $(this).attr('view-mode') || $(this).attr('view-type'),
|
||||
'res_id': Number($(this).attr('res-id')),
|
||||
'res_model': $(this).attr('res-model'),
|
||||
'views': [[$(this).attr('view-id') || false, $(this).attr('view-type')]],
|
||||
};
|
||||
window.parent.postMessage({
|
||||
'message': 'report:do_action',
|
||||
'action': action,
|
||||
}, trusted_origin);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -25,6 +25,12 @@
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/layout_background.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/layout_boxed.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/layout_clean.scss"/>
|
||||
|
||||
<script type="text/javascript" src="web/static/src/js/services/session.js"/>
|
||||
<script type="text/javascript" src="/web/static/src/js/report/utils.js"/>
|
||||
<script type="text/javascript" src="/web_editor/static/src/js/content/body_manager.js"/>
|
||||
<script type="text/javascript" src="/web_editor/static/src/js/root_widget.js"/>
|
||||
<script type="text/javascript" src='/web/static/src/js/report/report.js' />
|
||||
</template>
|
||||
|
||||
<template id="report_assets_pdf">
|
||||
@@ -37,7 +43,6 @@
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/report.editor.scss"/>
|
||||
|
||||
<script type="text/javascript" src="/web/static/src/js/services/session.js"/>
|
||||
<script type="text/javascript" src="/web/static/src/js/report/utils.js"/>
|
||||
<script type="text/javascript" src='/web/static/src/js/report/report.editor.js' />
|
||||
</template>
|
||||
|
||||
@@ -65,14 +70,13 @@
|
||||
<t t-call-assets="web_editor.assets_editor" t-js="false"/>
|
||||
<t t-call-assets="web.report_assets_editor" t-js="false"/>
|
||||
</t>
|
||||
<t t-call-assets="web.assets_common" t-css="false"/>
|
||||
<t t-call-assets="web.report_assets_common" t-css="false"/>
|
||||
<t t-if="editable">
|
||||
<t t-call-assets="web.assets_common" t-css="false"/>
|
||||
<t t-call-assets="web_editor.summernote" t-css="false"/>
|
||||
<t t-call-assets="web_editor.assets_editor" t-css="false"/>
|
||||
<t t-call-assets="web.report_assets_editor" t-css="false"/>
|
||||
|
||||
<script type="text/javascript" src="/web_editor/static/src/js/content/body_manager.js"/>
|
||||
<script type="text/javascript" src="/web_editor/static/src/js/root_widget.js"/>
|
||||
<script type="text/javascript" src="/web_editor/static/src/js/iframe.js"></script>
|
||||
<script t-if="enable_editor and inline_mode" type="text/javascript" src="/web_editor/static/src/js/inline.js"></script>
|
||||
</t>
|
||||
@@ -178,7 +182,7 @@
|
||||
<div class="clearfix mb8"/>
|
||||
</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">
|
||||
<div class="article o_report_layout_background">
|
||||
<t t-call="web.address_layout"/>
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
@@ -218,7 +222,7 @@
|
||||
</div>
|
||||
</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">
|
||||
<div class="article o_report_layout_boxed">
|
||||
<t t-call="web.address_layout"/>
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
@@ -257,7 +261,7 @@
|
||||
</div>
|
||||
</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">
|
||||
<div class="article o_report_layout_clean">
|
||||
<t t-call="web.address_layout"/>
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
@@ -305,7 +309,7 @@
|
||||
</div>
|
||||
</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">
|
||||
<div class="article o_report_layout_standard">
|
||||
<t t-call="web.address_layout"/>
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
@@ -386,16 +390,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="article" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id">
|
||||
<div class="article">
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="basic_layout">
|
||||
<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">
|
||||
<div class="article">
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -300,27 +300,31 @@ class IrActionsReport(models.Model):
|
||||
bodies = []
|
||||
res_ids = []
|
||||
|
||||
body_parent = root.xpath('//main')
|
||||
# Retrieve headers
|
||||
for node in root.xpath(match_klass.format('header')):
|
||||
body_parent = node.getparent()
|
||||
node.getparent().remove(node)
|
||||
header_node.append(node)
|
||||
|
||||
# Retrieve footers
|
||||
for node in root.xpath(match_klass.format('footer')):
|
||||
body_parent = node.getparent()
|
||||
node.getparent().remove(node)
|
||||
footer_node.append(node)
|
||||
|
||||
# Retrieve bodies
|
||||
for node in root.xpath(match_klass.format('article')):
|
||||
if node.get('data-oe-model') == self.model:
|
||||
body = layout.render(dict(subst=False, body=lxml.html.tostring(node), base_url=base_url))
|
||||
bodies.append(body)
|
||||
res_ids.append(int(node.get('data-oe-id', False)))
|
||||
for node in root.xpath(match_klass.format('page')):
|
||||
body = layout.render(dict(subst=False, body=lxml.html.tostring(node), base_url=base_url))
|
||||
bodies.append(body)
|
||||
if node.get('data-model') == self.model:
|
||||
res_ids.append(int(node.get('data-id', 0)))
|
||||
else:
|
||||
_logger.warning("The report's template '%s' does not contains the attributes 'data-oe-model' and 'data-oe-id' on the container (with class 'article')", self.name)
|
||||
raise UserError(_("The report's template is wrong, please contact your administrator"))
|
||||
res_ids.append(None)
|
||||
|
||||
if not bodies:
|
||||
_logger.warning("The report's template '%s' does not contains the container with the class 'article'", self.name)
|
||||
raise UserError(_("The report's template is wrong, please contact your administrator"))
|
||||
body = bytearray().join([lxml.html.tostring(c) for c in body_parent.getchildren()])
|
||||
bodies.append(body)
|
||||
|
||||
# Get paperformat arguments set in the root html tag. They are prioritized over
|
||||
# paperformat-record arguments.
|
||||
@@ -660,6 +664,10 @@ class IrActionsReport(models.Model):
|
||||
|
||||
bodies, html_ids, header, footer, specific_paperformat_args = self.with_context(context)._prepare_html(html)
|
||||
|
||||
if self.attachment and set(res_ids) != set(html_ids):
|
||||
raise UserError(_("The report's template '%s' is wrong, please contact your administrator. \n\n"
|
||||
"Can not separate file to save as attachment because the report's template does not contains the attributes 'data-model' and 'data-id' on the div with 'page' classname.") % self.name)
|
||||
|
||||
pdf_content = self._run_wkhtmltopdf(
|
||||
bodies,
|
||||
header=header,
|
||||
|
||||
Reference in New Issue
Block a user