diff options
| author | sed-odoo <sed@odoo.com> | 2018-09-04 08:34:41 +0200 |
|---|---|---|
| committer | sed-odoo <sed@odoo.com> | 2018-09-20 09:41:06 +0200 |
| commit | 3a883e67287cbf0c93a746bc2080b897f2c87bfa (patch) | |
| tree | 86924a931dc0045143646cc0c1d0e198dc09339b /addons/l10n_ch | |
| parent | c59d6d2329494705a5f9dd5ef6a1b0e2c43f8e38 (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__.py | 1 | ||||
| -rw-r--r-- | addons/l10n_ch/models/res_bank.py | 91 | ||||
| -rw-r--r-- | addons/l10n_ch/report/swissqr_report.xml | 88 | ||||
| -rwxr-xr-x | addons/l10n_ch/static/src/img/CH-Cross_7mm.png | bin | 0 -> 14703 bytes | |||
| -rw-r--r-- | addons/l10n_ch/static/src/scss/report_swissqr.css | 84 | ||||
| -rw-r--r-- | addons/l10n_ch/tests/__init__.py | 3 | ||||
| -rw-r--r-- | addons/l10n_ch/tests/test_l10n_ch_swissqr.py | 20 |
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 Binary files differnew file mode 100755 index 00000000000..41d3c5f9478 --- /dev/null +++ b/addons/l10n_ch/static/src/img/CH-Cross_7mm.png 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' |
