From 3a883e67287cbf0c93a746bc2080b897f2c87bfa Mon Sep 17 00:00:00 2001 From: sed-odoo Date: Tue, 4 Sep 2018 08:34:41 +0200 Subject: [PATCH] [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 --- addons/account/models/company.py | 2 - addons/account/models/res_config_settings.py | 2 - addons/account/views/account_invoice_view.xml | 2 +- addons/account/views/report_invoice.xml | 16 +-- .../views/res_config_settings_views.xml | 13 --- addons/l10n_ch/__manifest__.py | 1 + addons/l10n_ch/models/res_bank.py | 91 ++++++++++++++++++ addons/l10n_ch/report/swissqr_report.xml | 88 +++++++++++++++++ .../l10n_ch/static/src/img/CH-Cross_7mm.png | Bin 0 -> 14703 bytes .../static/src/scss/report_swissqr.css | 84 ++++++++++++++++ addons/l10n_ch/tests/__init__.py | 3 +- addons/l10n_ch/tests/test_l10n_ch_swissqr.py | 20 ++++ addons/payment/models/payment_acquirer.py | 1 + .../views/payment_portal_templates.xml | 28 +++--- addons/payment/views/payment_views.xml | 1 + addons/website_sale/views/templates.xml | 8 +- odoo/addons/base/models/res_bank.py | 4 +- 17 files changed, 320 insertions(+), 44 deletions(-) create mode 100644 addons/l10n_ch/report/swissqr_report.xml create mode 100755 addons/l10n_ch/static/src/img/CH-Cross_7mm.png create mode 100644 addons/l10n_ch/static/src/scss/report_swissqr.css create mode 100644 addons/l10n_ch/tests/test_l10n_ch_swissqr.py diff --git a/addons/account/models/company.py b/addons/account/models/company.py index 48e4fb9dd90..2f49a85962e 100644 --- a/addons/account/models/company.py +++ b/addons/account/models/company.py @@ -67,8 +67,6 @@ Best Regards,''')) default='invoice_number', help='You can set here the default communication that will appear on customer invoices, once validated, to help the customer to refer to that particular invoice when making the payment.') qr_code = fields.Boolean(string='Display SEPA QR code') - qr_code_payment_journal_id = fields.Many2one('account.journal', string='SEPA QR Code Bank Journal account') - qr_code_valid = fields.Boolean(string='Has all required arguments', related="qr_code_payment_journal_id.bank_account_id.qr_code_valid") invoice_is_email = fields.Boolean('Email by default', default=True) invoice_is_print = fields.Boolean('Print by default', default=True) diff --git a/addons/account/models/res_config_settings.py b/addons/account/models/res_config_settings.py index 54c1d91ad53..56948a1d738 100644 --- a/addons/account/models/res_config_settings.py +++ b/addons/account/models/res_config_settings.py @@ -87,8 +87,6 @@ class ResConfigSettings(models.TransientModel): don't want to reconcile all the past payments with bank statements.""") qr_code = fields.Boolean(string='Display SEPA QR code', related='company_id.qr_code') - qr_code_payment_journal_id = fields.Many2one('account.journal', related='company_id.qr_code_payment_journal_id', string="Payment Journal", domain="['&',('type', '=', 'bank'), ('currency_id.name','=','EUR')]") - qr_code_valid = fields.Boolean(string='Has all required arguments', related="qr_code_payment_journal_id.bank_account_id.qr_code_valid") invoice_is_print = fields.Boolean(string='Print', related='company_id.invoice_is_print') invoice_is_email = fields.Boolean(string='Send Email', related='company_id.invoice_is_email') diff --git a/addons/account/views/account_invoice_view.xml b/addons/account/views/account_invoice_view.xml index d85ce5d7e62..0cd723f1ff9 100644 --- a/addons/account/views/account_invoice_view.xml +++ b/addons/account/views/account_invoice_view.xml @@ -579,7 +579,6 @@ - @@ -588,6 +587,7 @@ + diff --git a/addons/account/views/report_invoice.xml b/addons/account/views/report_invoice.xml index feffb6646df..46006427155 100644 --- a/addons/account/views/report_invoice.xml +++ b/addons/account/views/report_invoice.xml @@ -179,13 +179,15 @@

-

- Scan me with your banking app.

- -

-

- The SEPA QR Code informations are not set correctly.
-

+
+

+ Scan me with your banking app.

+ +

+

+ The SEPA QR Code informations are not set correctly.
+

+
diff --git a/addons/account/views/res_config_settings_views.xml b/addons/account/views/res_config_settings_views.xml index 939c35538c6..1938c747513 100644 --- a/addons/account/views/res_config_settings_views.xml +++ b/addons/account/views/res_config_settings_views.xml @@ -280,19 +280,6 @@
Add a payment QR code to your invoices
-
-
-
-
-
- A bank journal must be set to use the SEPA QR code -
- -
- Please check that following information are set on the journal: bank account, BIC, EUR as currency. -
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 @@ + + + + + + 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 0000000000000000000000000000000000000000..41d3c5f9478099e4305c25c898f56ef0494d041d GIT binary patch literal 14703 zcmeI3PmB{)9LHxhXk5Txl!G2{7!E?Po#}Mj-Hz>$rL(vj99+ALbfZybXI^(Fv@_$( zEbVS2Y#;=P7>yx8LKF`MgCue>5yF8)B2i=1pd2J1MlVK8G%@jjG4ahzxBa!-^1yI> z&9?pRd*AQx_vZ8dbS`snbj#Drdmrm%7-sp%uu{O^CxUkm{$OfU#acntI6 zrr_Q4$l?4T!wd-Kc+oGa&q%smiED;EiQ=`26S$b{hMJ@4JCM&#qAAnLaetgU&#|VF zOWmiGLiofttKX-B6wKIUA=FXnv>I#Dd_1sc2qOF*n*zO7A}QohO+otrM!zY-*(4syDUemY;*x3 zo6iTUDiu$$YgJ7*t$^&hEZ=Tx&UrHj zUDq{a^iNLqmqLiD3hj$hE4Drp?3j9L;|@~;`=Zp(x+XsDv@*~HqrcM<9nvf+HEeo# zjn0C#i&t=Xo{gbbT3Z)7%4T>jIhyOCVAtiig>Bb3$QtKV;0brHr0KzlChNhbKt`5t zX>AUx;jG)t|2u(EeqirjqdJYOTWzQ)RjBHo#Is9&Rddnc6kZdzyWgYCt=9GuSw8p` zD4VU55w*6?(ZZ@-Jg3>k-iwrYk4Nrr-ksvCxWlK7xuS% zCkS5H-|lT%w_2tzi}iAC&A=6$!O`I`hHn>;oKB^ZsVv{#-0m1PYp7gQOuUP|U~MMS z>AI=Cr=xSCs}uCpozZlGpZ0JzqgH(5fbUuO#!XA>L^n3XT@IR;be9vubhHShL5wLb zA|fcDxF8K;OmPtrK>@`DX%J(Ii--scC@x5Y7*kwCL{LC+K^nxE;vyn~0*VXLAjT9I z5fKznT#yDarnrcRpn&3nG>9?9MMMMz6c?mHj43W6A}FA^APr(naS;(g0mTJr5Mzpq zhzJTOE=YqIQ(QzuP(X1(8pN35A|iqUiVM;p#uOJ35fo5dkOncPxQK|LfZ~EQh%v=Q zL<9vC7owsiM3}( zl)>@ZmEVtSe(+OanNr!m>y5*wy!YLCzxb#5(2vXZPP}KV%uVbR-#zo;g|}8ddbRM| zx}jA!loXf#VDHIN@%r}rpNnmK;y`i5O>tH|vHHeO%6Nn?@f!#m9d~w&hijumx z>&@$>e@>36fAikb^#ymu?6IrA9FvyLU;gUmwF|Eg9F;fkIrG``-><&*)Yk7lf9-4G zz)P=cbMpCp`!heRKDBD-o#XzcBQta5xP1BO$A`Y$y>|D;)Auq5PYpiI%VC0DoWme*gdg literal 0 HcmV?d00001 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' diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 3da56665b26..64e7f02436b 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -143,6 +143,7 @@ class PaymentAcquirer(models.Model): fees_dom_var = fields.Float('Variable domestic fees (in percents)') fees_int_fixed = fields.Float('Fixed international fees') fees_int_var = fields.Float('Variable international fees (in percents)') + qr_code = fields.Boolean('Use SEPA QR Code') # TDE FIXME: remove that brol module_id = fields.Many2one('ir.module.module', string='Corresponding Module') diff --git a/addons/payment/views/payment_portal_templates.xml b/addons/payment/views/payment_portal_templates.xml index 0676107c698..bac8783fcea 100644 --- a/addons/payment/views/payment_portal_templates.xml +++ b/addons/payment/views/payment_portal_templates.xml @@ -100,12 +100,14 @@ -
-

Or scan me with your banking app.

- -
-
-

The SEPA QR Code informations are not set correctly.

+
+
+

Or scan me with your banking app.

+ +
+
+

The SEPA QR Code informations are not set correctly.

+
Back to My Account @@ -137,12 +139,14 @@
Communication:
-
-

Or scan me with your banking app.

- -
-
-

The SEPA QR Code informations are not set correctly.

+
+
+

Or scan me with your banking app.

+ +
+
+

The SEPA QR Code informations are not set correctly.

+
-
-
+
+

Or scan me with your banking app.

- +
-
+

The SEPA QR Code informations are not set correctly.

diff --git a/odoo/addons/base/models/res_bank.py b/odoo/addons/base/models/res_bank.py index d009e3ce278..4b5147ed0fe 100644 --- a/odoo/addons/base/models/res_bank.py +++ b/odoo/addons/base/models/res_bank.py @@ -129,11 +129,11 @@ class ResPartnerBank(models.Model): @api.multi def _validate_qr_code_arguments(self): - for bank in self: + for bank in self: if bank.currency_id.name == False: currency = bank.company_id.currency_id else: - currency = bank.currency_id + currency = bank.currency_id bank.qr_code_valid = (bank.bank_bic and bank.company_id.name and bank.acc_number