[FIX] l10n_ch: fix QR-bill pdf report so that it matches the spec

Using it as a parf of the regular invoice pdf report was wrong, as this report is subject to the margins defined in its paper format; causing issues as for the rendering of the QR report, which must be displayed at a precise location (just like ISR). wkhtmltopdf doesn't allow having different margin values in the same file, so we did just as for ISR, and now generate QR-bill in a distinct pdf.

[IMP] l10n_ch: add receipt part to QR-bill pdf report

closes odoo/odoo#50765

X-original-commit: 99b5306545b945810e8259f2cc447b933d316778
Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
Signed-off-by: oco-odoo <oco-odoo@users.noreply.github.com>
This commit is contained in:
oco-odoo
2020-05-07 08:18:35 +00:00
parent 6c05214f90
commit e997bf14b3
7 changed files with 323 additions and 132 deletions
+64
View File
@@ -575,3 +575,67 @@ msgid ""
"\n"
" - fill the 'bank account' field of the invoice with the postal to be used to receive the related payment. A default account will be automatically set for all invoices created after you defined a postal account for your company."
msgstr ""
#. module: l10n_ch
#: model:ir.model.fields,help:l10n_ch.field_mail_template__attachment_ids
msgid "You may attach files to this template, to be added to all emails created from this template"
msgstr ""
#. module: l10n_ch
#: model:ir.model.fields,field_description:l10n_ch.field_res_bank__zip
#: model:ir.model.fields,field_description:l10n_ch.field_res_company__zip
msgid "Zip"
msgstr ""
#. module: l10n_ch
#: model:ir.model.fields,help:l10n_ch.field_account_invoice__amount_by_group
msgid "type: [(name, amount, base, formated amount, formated base)]"
msgstr ""
#: code:addons/l10n_ch/models/account_invoice.py:179
#, python-format
msgid "Cannot generate the QR-bill. Please check you have configured the address of your company and debtor. If you are using a QR-IBAN, also check the invoice's payment reference is a QR reference."
msgstr ""
#. module: l10n_ch
#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_swissqr_template
msgid "QR-bill for invoice"
msgstr ""
#. module: l10n_ch
#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_swissqr_template
msgid "<span class=\"swissqr_text title title_zone\">QR-bill Payment Receipt Part</span><br/>\n"
"\n"
" <span class=\"swissqr_text title\">Payable to</span><br/>"
msgstr ""
#. module: l10n_ch
#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_swissqr_template
msgid "<span class=\"swissqr_text title\">Payable to</span><br/>"
msgstr ""
#. module: l10n_ch
#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_swissqr_template
msgid "<br/>\n"
"\n"
" <span class=\"swissqr_text title\">Reference</span><br/>"
msgstr ""
#. module: l10n_ch
#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_swissqr_template
msgid "<span class=\"swissqr_text title\">Payable by</span><br/>"
msgstr ""
#. module: l10n_ch
#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_swissqr_template
msgid "<span class=\"swissqr_text title\">Payable by</span><br/>"
msgstr ""
#. module: l10n_ch
#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_swissqr_template
msgid "<span class=\"swissqr_text title\">Acceptance point</span>"
msgstr ""
#. module: l10n_ch
#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_swissqr_template
msgid "<span class=\"swissqr_text title title_zone\">QR-bill Payment Part</span><br/>"
+12 -9
View File
@@ -4,7 +4,7 @@
import re
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
from odoo.exceptions import ValidationError, UserError
from odoo.tools.float_utils import float_split_str
from odoo.tools.misc import mod10r
@@ -165,14 +165,6 @@ class AccountMove(models.Model):
"""
return float_split_str(self.amount_residual, 2)
def display_swiss_qr_code(self):
""" Returns whether or not to print the QR-bills additional page when
generation an invoice pdf.
"""
self.ensure_one()
return self.display_qr_code and \
self.partner_bank_id._eligible_for_qr_code('ch_qr', self.partner_id, self.currency_id)
def isr_print(self):
""" Triggered by the 'Print ISR' button.
"""
@@ -188,6 +180,17 @@ class AccountMove(models.Model):
- associate this bank with a postal reference for the currency used in this invoice\n
- fill the 'bank account' field of the invoice with the postal to be used to receive the related payment. A default account will be automatically set for all invoices created after you defined a postal account for your company."""))
def print_ch_qr_bill(self):
""" Triggered by the 'Print QR-bill' button.
"""
self.ensure_one()
if not self.partner_bank_id._eligible_for_qr_code('ch_qr', self.partner_id, self.currency_id):
raise UserError(_("Cannot generate the QR-bill. Please check you have configured the address of your company and debtor. If you are using a QR-IBAN, also check the invoice's payment reference is a QR reference."))
self.l10n_ch_isr_sent = True
return self.env.ref('l10n_ch.l10n_ch_qr_report').report_action(self)
def action_invoice_sent(self):
# OVERRIDE
rslt = super(AccountMove, self).action_invoice_sent()
+23 -10
View File
@@ -25,18 +25,31 @@ class MailTemplate(models.Model):
res_ids = [res_ids]
multi_mode = False
for record in self.env[self.model].browse(res_ids):
if record.l10n_ch_isr_valid:
# We add an attachment containing the ISR
report_name = 'ISR-' + self._render_field('report_name', record.ids, compute_lang=True)[record.id] + '.pdf'
if self.model == 'account.move':
for record in self.env[self.model].browse(res_ids):
if record.l10n_ch_isr_valid:
# We add an attachment containing the ISR
inv_print_name = self._render_field('report_name', record.ids, compute_lang=True)[record.id]
pdf = self.env.ref('l10n_ch.l10n_ch_isr_report').render_qweb_pdf(record.ids)[0]
pdf = base64.b64encode(pdf)
report_name = 'ISR-' + self._render_field('report_name', record.ids, compute_lang=True)[record.id] + '.pdf'
isr_report_name = 'ISR-' + inv_print_name + '.pdf'
qr_report_name = 'QR-bill-' + inv_print_name + '.pdf'
isr_pdf = self.env.ref('l10n_ch.l10n_ch_isr_report').render_qweb_pdf(record.ids)[0]
isr_pdf = base64.b64encode(isr_pdf)
qr_pdf = self.env.ref('l10n_ch.l10n_ch_qr_report').render_qweb_pdf(record.ids)[0]
qr_pdf = base64.b64encode(qr_pdf)
new_attachments = [(isr_report_name, isr_pdf), (qr_report_name, qr_pdf)]
record_dict = multi_mode and result[record.id] or result
attachments_list = record_dict.get('attachments', False)
record_dict = multi_mode and result[record.id] or result
attachments_list = record_dict.get('attachments', False)
if attachments_list:
attachments_list.append((report_name, pdf))
attachments_list.extend(new_attachments)
else:
record_dict['attachments'] = [(report_name, pdf)]
record_dict['attachments'] = new_attachments
return result
+89 -24
View File
@@ -1,17 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="l10n_ch_swissqr_template" inherit_id="account.report_invoice_document">
<xpath expr="//div[@id='qrcode']" position="attributes">
<attribute name="t-if">not o.display_swiss_qr_code() and o.display_qr_code</attribute>
</xpath>
<xpath expr="//div[@id='qrcode']" position="after">
<link type="text/css" rel="stylesheet" href="/l10n_ch/static/src/scss/report_swissqr.css"/>
<div class="swissqr_content" t-if="o.display_swiss_qr_code()">
<div class="swissqr_body" t-if="o.partner_bank_id._eligible_for_qr_code('ch_qr', o.partner_id, o.currency_id)">
<report id="l10n_ch_qr_report"
model="account.move"
report_type="qweb-pdf"
string="QR-bill"
name="l10n_ch.qr_report_main"
file="l10n_ch.qr_report_main"
attachment="'QR-bill-' + object.name + '.pdf'"
menu="False"
print_report_name="'QR-bill-%s' % object.name"
paperformat="l10n_ch.paperformat_euro_no_margin"/>
<template id="assets_common_qr" inherit_id="web.report_assets_common">
<xpath expr="." position="inside">
<link type="text/scss" rel="stylesheet" href="/l10n_ch/static/src/scss/report_swissqr.scss"/>
</xpath>
</template>
<template id="l10n_ch_swissqr_template">
<t t-call="web.external_layout">
<!-- add class to body tag -->
<script>document.body.className += " l10n_ch_qr";</script>
<t t-set="formated_amount" t-value="('%.2f' % (o.amount_residual)).split(' ')[0].replace(',',' ')"/>
<div class="swissqr_title">
<h1>QR-bill for invoice <t t-esc="invoice.name"/></h1>
</div>
<div class="swissqr_content">
<div class="swissqr_receipt">
<div id="title_zone" class="swissqr_column_left title_zone">
<span class="swissqr_text title title_zone">QR-bill payment part</span><br/>
<span class="swissqr_text title title_zone">QR-bill Payment Receipt Part</span><br/>
<span class="swissqr_text title">Payable to</span><br/>
<span class="swissqr_text content" t-field="o.partner_bank_id.acc_number"/><br/>
<span class="swissqr_text content" t-field="o.company_id.name"/><br/>
<span class="swissqr_text content" t-field="o.company_id.country_id.code"/>
<span class="swissqr_text content" t-field="o.company_id.zip"/>
<span class="swissqr_text content" t-field="o.company_id.city"/><br/>
<span class="swissqr_text title">Reference</span><br/>
<t t-if="o.partner_bank_id.currency_id.name == 'EUR'">
<span class="swissqr_text content" t-field="o.partner_bank_id.bank_id.l10n_ch_postal_eur"/><br/>
</t>
<t t-if="o.partner_bank_id.currency_id.name == 'CHF'">
<span class="swissqr_text content" t-field="o.partner_bank_id.bank_id.l10n_ch_postal_chf"/><br/>
</t>
<span class="swissqr_text title">Payable by</span><br/>
<span class="swissqr_text content" t-field="o.partner_id.name"/><br/>
<span class="swissqr_text content" t-field="o.partner_id.country_id.code"/>
<span class="swissqr_text content" t-field="o.partner_id.zip"/>
<span class="swissqr_text content" t-field="o.partner_id.city"/><br/>
<div>
<span class="swissqr_text title">Amount</span><br/>
<span class="swissqr_text content" t-esc="formated_amount"/>
<span class="swissqr_text content" t-field="o.currency_id.name"/>
</div>
<span class="swissqr_text title">Acceptance point</span>
</div>
</div>
<div class="swissqr_body">
<div id="title_zone" class="swissqr_column_left title_zone">
<span class="swissqr_text title title_zone">QR-bill Payment Part</span><br/>
</div>
<div id="procedure_zone" class="swissqr_column_left procedure_zone">
@@ -22,17 +80,15 @@
<img class="swissqr" t-att-src="o.partner_bank_id.build_qr_code_url(o.amount_residual, o.ref or o.name, o.payment_reference, o.currency_id, o.partner_id, 'ch_qr')"/>
<div id="indications_zone" class="swissqr_column_right indication_zone">
<span class="swissqr_text title">Account</span><br/>
<span class="swissqr_text title">Payable to</span><br/>
<span class="swissqr_text content" t-field="o.partner_bank_id.acc_number"/><br/>
<span class="swissqr_text title">Creditor</span><br/>
<span class="swissqr_text content" t-field="o.company_id.name"/><br/>
<span class="swissqr_text content" t-field="o.company_id.street"/><br/>
<span class="swissqr_text content" t-field="o.company_id.country_id.code"/>
<span class="swissqr_text content" t-field="o.company_id.zip"/>
<span class="swissqr_text content" t-field="o.company_id.city"/><br/>
<span class="swissqr_text title">Reference number</span><br/>
<span class="swissqr_text title">Reference</span><br/>
<t t-if="o.partner_bank_id.currency_id.name == 'EUR'">
<span class="swissqr_text content" t-field="o.partner_bank_id.l10n_ch_isr_subscription_eur"/><br/>
</t>
@@ -43,7 +99,7 @@
<span class="swissqr_text title">Additional information</span><br/>
<span class="swissqr_text content" t-field="o.ref"/><br/>
<span class="swissqr_text title">Debtor</span><br/>
<span class="swissqr_text title">Payable by</span><br/>
<span class="swissqr_text content" t-field="o.partner_id.name"/><br/>
<span class="swissqr_text content" t-field="o.partner_id.street"> </span>
<span class="swissqr_text content" t-field="o.partner_id.street2"/><br/>
@@ -53,21 +109,30 @@
<span class="swissqr_text title">Due date</span><br/>
<span class="swissqr_text content" t-field="o.invoice_date_due"/><br/>
</div>
<div id="amount_zone" class="swissqr_column_left amount_zone">
<span class="swissqr_text title">Currency</span><br/>
<span class="swissqr_text content" t-field="o.currency_id.name"/>
</div>
<div id="amount_zone" class="swissqr_column_left amount_zone">
<span class="swissqr_text title">Currency</span><br/>
<span class="swissqr_text content" t-field="o.currency_id.name"/>
</div>
<div class="amount_zone amount">
<span class="swissqr_text title">Amount</span><br/>
<t t-set="formated_amount" t-value="('%.2f' % (o.amount_residual)).split(' ')[0].replace(',',' ')"/>
<span class="swissqr_text content" t-esc="formated_amount"/>
<div class="amount_zone amount">
<span class="swissqr_text title">Amount</span><br/>
<span class="swissqr_text content" t-esc="formated_amount"/>
</div>
</div>
</div>
</div>
</xpath>
</t>
</template>
<template id="l10n_ch.qr_report_main">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="invoice">
<t t-set="o" t-value="invoice"/>
<t t-call="l10n_ch.l10n_ch_swissqr_template"/>
</t>
</t>
</template>
</data>
</odoo>
@@ -1,89 +0,0 @@
@font-face {
font-family: ocrb;
src: url('../img/ocrb.ttf') format('truetype');
}
.swissqr_content {
page-break-before: always;
}
.swissqr_body {
position: relative;
background-color: white;
border-color:black;
border-width: 1pt 0pt 0pt 1pt;
border-style: solid;
height: 116mm;
width: 190mm;
top: 160mm;
left: 70mm;
}
.swissqr{
position: absolute;
height: 70mm;
width: 70mm;
top: 29.5mm;
left: 7mm;
}
.ch_cross{
position: absolute;
background-color: white;
height: 8.5mm;
width: 8.5mm;
top: 60.25mm;
left: 37.75mm;
}
.swissqr_text{
font-family: ocrb, Arial, Frutiger, Helvetica;
color: black;
}
.swissqr_text.title{
font-size: 9pt;
font-weight: bold;
}
.swissqr_text.title.title_zone{
font-size: 11pt;
font-weight: bold;
}
.swissqr_text.content{
font-size: 11pt;
}
.swissqr_column_left{
position: absolute;
left: 12mm;
}
.swissqr_column_right{
position: absolute;
left: 83mm;
max-width: 90mm;
}
.procedure_zone{
top: 15mm;
}
.title_zone{
top: 5mm;
}
.indication_zone{
top: 10mm;
}
.amount_zone{
position: absolute;
top: 106mm;
}
.amount{
position: absolute;
left: 41mm;
}
@@ -0,0 +1,116 @@
body.l10n_ch_qr {
padding:0;
/* Disable custom bakground */
.o_report_layout_background {
background: none;
min-height: 0;
}
.swissqr_title {
position: absolute;
padding: 15px;
padding-top: 150px;
}
.swissqr_content {
position: relative;
}
.swissqr_receipt {
position: absolute;
background_color: white;
border-color:black;
border-width: 1pt 1pt 1pt 1pt;
border-style: solid;
height: 131.25mm; /* 105 * 1.25 */
width: 77.5mm; /* 62 * 1.25 */
top: 240mm; /* 192 * 1.25 */
left: 0mm;
/*These measures are multiplied by 1.25 here to fit the canvas size used by our reporting engine (1.25 times bigger than A4)*/
}
.swissqr_body {
position: absolute;
background-color: white;
border-color:black;
border-width: 1pt 1pt 1pt 1pt;
border-style: solid;
height: 131.25mm; /* 105 * 1.25 */
width: 185mm; /* 148 * 1.25 */
top: 240mm; /* 192 * 1.25 */
left: 77.5mm; /* 62 * 1.25 */
/*These measures are multiplied by 1.25 here to fit the canvas size used by our reporting engine (1.25 times bigger than A4)*/
}
.swissqr {
position: absolute;
height: 70mm;
width: 70mm;
top: 29.5mm;
left: 7mm;
}
.ch_cross {
position: absolute;
background-color: white;
height: 8.5mm;
width: 8.5mm;
top: 60.25mm;
left: 37.75mm;
}
.swissqr_text {
font-family: OCR-B, Arial, Frutiger, Helvetica;
color: black;
}
.swissqr_text.title {
font-size: 9pt;
font-weight: bold;
}
.swissqr_text.title.title_zone {
font-size: 11pt;
font-weight: bold;
}
.swissqr_text.content {
font-size: 11pt;
}
.swissqr_column_left {
position: absolute;
left: 12mm;
}
.swissqr_column_right {
position: absolute;
left: 83mm;
max-width: 90mm;
}
.procedure_zone {
top: 15mm;
}
.title_zone {
top: 5mm;
}
.indication_zone {
top: 10mm;
}
.amount_zone {
position: absolute;
top: 106mm;
}
.amount {
position: absolute;
left: 41mm;
}
}
@@ -40,6 +40,25 @@
('l10n_ch_currency_name', 'not in', ['EUR', 'CHF'])]}"
groups="base.group_user"
/>
<button
name="print_ch_qr_bill"
string="Print QR-bill"
type="object"
attrs="{'invisible':['|', ('state', '!=', 'posted'),
'|', ('l10n_ch_isr_sent', '=', True),
('l10n_ch_currency_name', 'not in', ['EUR', 'CHF'])]}"
groups="base.group_user"
class="oe_highlight"
/>
<button
name="print_ch_qr_bill"
string="Print QR-bill"
type="object"
attrs="{'invisible':['|', ('state', '!=', 'posted'),
'|', ('l10n_ch_isr_sent', '=', False),
('l10n_ch_currency_name', 'not in', ['EUR', 'CHF'])]}"
groups="base.group_user"
/>
</xpath>
</field>
</record>