summaryrefslogtreecommitdiff
path: root/addons/l10n_ch
diff options
context:
space:
mode:
authorsed-odoo <sed@odoo.com>2018-09-04 08:34:41 +0200
committersed-odoo <sed@odoo.com>2018-09-20 09:41:06 +0200
commit3a883e67287cbf0c93a746bc2080b897f2c87bfa (patch)
tree86924a931dc0045143646cc0c1d0e198dc09339b /addons/l10n_ch
parentc59d6d2329494705a5f9dd5ef6a1b0e2c43f8e38 (diff)
[IMP] l10n_ch,account,website_sale: Add SwissQR Code on invoice and improve the SEPA QR Code usability
Purpose ======= - Add the SwissQR Code on the invoice in aim to replace the actual ISR - Improve the settings of the SEPA QR Code to make it more user friendly Specifications ============== - ln_ch: - Add a SwissQR Code on the invoice to fit the Switzerland QR-Bill Format - Use the "partner_bank_id" field to generate the QR Code - Add function to get the address number out of the field "street" and "street2" - account: - Remove the SEPA QR Code's journal settings from the general setting - Display the "partner_bank_id" field on the invoice "Other info" page - Use the "partner_bank_id" field to generate the QR Code - website_sale: - Add a check box on the payment acquirers to use the SEPA QR Code on the e-commerce - Use the payment acquirer "journal_id" field to generate the SEPA QR Code
Diffstat (limited to 'addons/l10n_ch')
-rw-r--r--addons/l10n_ch/__manifest__.py1
-rw-r--r--addons/l10n_ch/models/res_bank.py91
-rw-r--r--addons/l10n_ch/report/swissqr_report.xml88
-rwxr-xr-xaddons/l10n_ch/static/src/img/CH-Cross_7mm.pngbin0 -> 14703 bytes
-rw-r--r--addons/l10n_ch/static/src/scss/report_swissqr.css84
-rw-r--r--addons/l10n_ch/tests/__init__.py3
-rw-r--r--addons/l10n_ch/tests/test_l10n_ch_swissqr.py20
7 files changed, 286 insertions, 1 deletions
diff --git a/addons/l10n_ch/__manifest__.py b/addons/l10n_ch/__manifest__.py
index f833530cb1c..f300b2581a1 100644
--- a/addons/l10n_ch/__manifest__.py
+++ b/addons/l10n_ch/__manifest__.py
@@ -38,6 +38,7 @@ Here is how it works:
'data/account_fiscal_position_data.xml',
'data/account_chart_template_data.xml',
'report/isr_report.xml',
+ 'report/swissqr_report.xml',
'views/res_bank_view.xml',
'views/account_invoice_view.xml',
'views/res_config_settings_views.xml',
diff --git a/addons/l10n_ch/models/res_bank.py b/addons/l10n_ch/models/res_bank.py
index 8e3d9d4949d..c4ac741ded2 100644
--- a/addons/l10n_ch/models/res_bank.py
+++ b/addons/l10n_ch/models/res_bank.py
@@ -6,6 +6,7 @@ import re
from odoo import api, fields, models, _
from odoo.tools.misc import mod10r
+import werkzeug.urls
def _is_l10n_ch_postal(account_ref):
""" Returns True iff the string account_ref is a valid postal account number,
@@ -64,3 +65,93 @@ class ResPartnerBank(models.Model):
if _is_l10n_ch_postal(iban[-12:]):
return iban[-12:]
return None
+
+ def find_number(self, s):
+ # this regex match numbers like 1bis 1a
+ lmo = re.findall('([0-9]+[^ ]*)',s)
+ # no number found
+ if len(lmo) == 0:
+ return ''
+ # Only one number or starts with a number return the first one
+ if len(lmo) == 1 or re.match(r'^\s*([0-9]+[^ ]*)',s):
+ return lmo[0]
+ # else return the last one
+ if len(lmo) > 1:
+ return lmo[-1]
+ else:
+ return ''
+
+ @api.model
+ def build_swiss_code_url(self, amount, currency, date_due, debitor, ref_type, reference, comment):
+ communication = ""
+ if comment:
+ communication = (comment[:137] + '...') if len(comment) > 140 else comment
+
+ t_street_comp = '%s %s' % (self.company_id.street if (self.company_id.street != False) else '', self.company_id.street2 if (self.company_id.street2 != False) else '')
+ t_street_deb = '%s %s' % (debitor.street if (debitor.street != False) else '', debitor.street2 if (debitor.street2 != False) else '')
+ number = self.find_number(t_street_comp)
+ number_deb = self.find_number(t_street_deb)
+ if (t_street_comp == ' '):
+ t_street_comp = False
+ if (t_street_deb == ' '):
+ t_street_deb = False
+
+ qr_code_string = 'SPC\n0100\n1\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s' % (
+ self.acc_number,
+ self.company_id.name,
+ t_street_comp,
+ number,
+ self.company_id.zip,
+ self.company_id.city,
+ self.company_id.country_id.code,
+ amount,
+ currency,
+ date_due,
+ debitor.name,
+ t_street_deb,
+ number_deb,
+ debitor.zip,
+ debitor.city,
+ debitor.country_id.code,
+ ref_type,
+ reference,
+ communication)
+ qr_code_url = '/report/barcode/?type=%s&value=%s&width=%s&height=%s&humanreadable=1' % ('QR', werkzeug.url_quote_plus(qr_code_string), 256, 256)
+ return qr_code_url
+
+ @api.model
+ def validate_swiss_code_arguments(self, currency, debitor):
+
+ t_street_comp = '%s %s' % (self.company_id.street if (self.company_id.street != False) else '', self.company_id.street2 if (self.company_id.street2 != False) else '')
+ t_street_deb = '%s %s' % (debitor.street if (debitor.street != False) else '', debitor.street2 if (debitor.street2 != False) else '')
+ number = self.find_number(t_street_comp)
+ number_deb = self.find_number(t_street_deb)
+ if (t_street_comp == ' '):
+ t_street_comp = False
+ if (t_street_deb == ' '):
+ t_street_deb = False
+
+ if(currency.name == 'EUR'):
+ return (self.bank_id.l10n_ch_postal_eur and
+ self.company_id.zip and
+ self.company_id.city and
+ self.company_id.country_id.code and
+ (t_street_comp != False) and
+ (t_street_deb != False) and
+ debitor.zip and
+ debitor.city and
+ debitor.country_id.code and
+ (number != False) and (number_deb != False))
+ elif(currency.name == 'CHF'):
+ return (self.bank_id.l10n_ch_postal_chf and
+ self.company_id.zip and
+ self.company_id.city and
+ self.company_id.country_id.code and
+ (t_street_comp != False) and
+ (t_street_deb != False) and
+ debitor.zip and
+ debitor.city and
+ debitor.country_id.code and
+ (number != False) and (number_deb != False))
+ else:
+ return False
diff --git a/addons/l10n_ch/report/swissqr_report.xml b/addons/l10n_ch/report/swissqr_report.xml
new file mode 100644
index 00000000000..37dc2b0f6b4
--- /dev/null
+++ b/addons/l10n_ch/report/swissqr_report.xml
@@ -0,0 +1,88 @@
+<?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="after">
+ <link type="text/css" rel="stylesheet" href="/l10n_ch/static/src/scss/report_swissqr.css"/>
+ <div class="swissqr_content" t-if="o.partner_id.country_id.code == 'CH'">
+ <div class="swissqr_body" t-if="o.partner_bank_id.validate_swiss_code_arguments(o.partner_bank_id.currency_id, o.partner_id) and ((o.currency_id.name == 'EUR') or (o.currency_id.name == 'CHF'))">
+ <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">
+ <span class="swissqr_text title">Supports</span><br/>
+ <span class="swissqr_text content">Credit transfer</span>
+ </div>
+
+ <img class="swissqr" t-att-src="o.partner_bank_id.build_swiss_code_url(o.residual, o.currency_id.name, o.date_due, o.partner_id, 'QRR',
+ (o.partner_bank_id.bank_id.l10n_ch_postal_eur) if (o.partner_bank_id.currency_id.name == 'EUR') else o.partner_bank_id.bank_id.l10n_ch_postal_chf,
+ (o.reference) if (o.reference) else o.number)"/>
+ <img class="ch_cross" src="/l10n_ch/static/src/img/CH-Cross_7mm.png"/>
+
+ <div id="indications_zone" class="swissqr_column_right indication_zone">
+ <span class="swissqr_text title">Account</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/>
+ <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">Additional information</span><br/>
+ <span class="swissqr_text content" t-field="o.reference"/><br/>
+
+ <span class="swissqr_text title">Debtor</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/>
+ <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/>
+
+ <span class="swissqr_text title">Due date</span><br/>
+ <span class="swissqr_text content" t-field="o.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 class="amount_zone amount">
+ <span class="swissqr_text title">Amount</span><br/>
+ <t t-set="formated_amount" t-value="('%.2f' % (o.residual)).split(' ')[0].replace(',',' ')"/>
+ <span class="swissqr_text content" t-esc="formated_amount"/>
+ </div>
+ </div>
+ <div class="swissqr_body" t-if="(o.partner_bank_id.validate_swiss_code_arguments(o.partner_bank_id.currency_id, o.partner_id) == False) and ((o.currency_id.name == 'EUR') or (o.currency_id.name == 'CHF'))">
+ <div class="swissqr_column_left procedure_zone">
+ <span>Some of this informations are not set corrrectly to display the Swiss QR Code.<br/>
+ -Bank > CHF ISR reference<br/>
+ -Bank > EUR ISR reference<br/>
+ -Your company > Street with number<br/>
+ -Your company > City<br/>
+ -Your company > Zip<br/>
+ -Your company > Country<br/>
+ -Debtor > Street with number<br/>
+ -Debtor > City<br/>
+ -Debtor > Zip<br/>
+ -Debtor > Country
+ </span>
+ </div>
+ </div>
+ </div>
+ </xpath>
+ </template>
+ </data>
+</odoo>
diff --git a/addons/l10n_ch/static/src/img/CH-Cross_7mm.png b/addons/l10n_ch/static/src/img/CH-Cross_7mm.png
new file mode 100755
index 00000000000..41d3c5f9478
--- /dev/null
+++ b/addons/l10n_ch/static/src/img/CH-Cross_7mm.png
Binary files differ
diff --git a/addons/l10n_ch/static/src/scss/report_swissqr.css b/addons/l10n_ch/static/src/scss/report_swissqr.css
new file mode 100644
index 00000000000..9626c958887
--- /dev/null
+++ b/addons/l10n_ch/static/src/scss/report_swissqr.css
@@ -0,0 +1,84 @@
+.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: 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;
+}
diff --git a/addons/l10n_ch/tests/__init__.py b/addons/l10n_ch/tests/__init__.py
index f36cc8f091c..041c87e8043 100644
--- a/addons/l10n_ch/tests/__init__.py
+++ b/addons/l10n_ch/tests/__init__.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import test_l10n_ch_isr \ No newline at end of file
+from . import test_l10n_ch_isr
+from . import test_l10n_ch_swissqr
diff --git a/addons/l10n_ch/tests/test_l10n_ch_swissqr.py b/addons/l10n_ch/tests/test_l10n_ch_swissqr.py
new file mode 100644
index 00000000000..68e91ca6466
--- /dev/null
+++ b/addons/l10n_ch/tests/test_l10n_ch_swissqr.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.tests.common import TransactionCase
+from odoo.tests import tagged
+
+@tagged('post_install', '-at_install')
+class SwissQRTest(TransactionCase):
+
+ def test_abc_1(self):
+ assert self.env['res.partner.bank'].find_number('1 rue du bois communal') == '1'
+ assert self.env['res.partner.bank'].find_number('1bis rue du bois communal') == '1bis'
+ assert self.env['res.partner.bank'].find_number('rue du bois communal 1') == '1'
+ assert self.env['res.partner.bank'].find_number('rue du bois communal 1bis') == '1bis'
+ assert self.env['res.partner.bank'].find_number('1 rue du 9 mars 1962') == '1'
+ assert self.env['res.partner.bank'].find_number('rue du 9 mars 1962 1') == '1'
+ assert self.env['res.partner.bank'].find_number('1bis rue du 9 mars 1962') == '1bis'
+ assert self.env['res.partner.bank'].find_number('rue du 9 mars 1962 1bis') == '1bis'
+ assert self.env['res.partner.bank'].find_number('rue du 9 mars 1962') == '1962'
+ assert self.env['res.partner.bank'].find_number('rue du 9 mars 1962 4 boite 2') == '2'