diff --git a/addons/account/models/account_journal.py b/addons/account/models/account_journal.py index 70f7d98f303..485b707b85f 100644 --- a/addons/account/models/account_journal.py +++ b/addons/account/models/account_journal.py @@ -905,3 +905,11 @@ class AccountJournal(models.Model): """ Check if the payment method is available on this journal. """ self.ensure_one() return self.filtered_domain(self.env['account.payment.method']._get_payment_method_domain(payment_method_code)) + + def _process_reference_for_sale_order(self, order_reference): + ''' + returns the order reference to be used for the payment. + Hook to be overriden: see l10n_ch for an example. + ''' + self.ensure_one() + return order_reference diff --git a/addons/l10n_ch/models/account_invoice.py b/addons/l10n_ch/models/account_invoice.py index dc532b1b732..bf2fe51645b 100644 --- a/addons/l10n_ch/models/account_invoice.py +++ b/addons/l10n_ch/models/account_invoice.py @@ -30,16 +30,20 @@ class AccountMove(models.Model): self.ensure_one() if self.partner_bank_id.l10n_ch_qr_iban and self.l10n_ch_is_qr_valid and self.name: invoice_ref = re.sub(r'[^\d]', '', self.name) - # keep only the last digits if it exceed boundaries - ref_payload_len = L10N_CH_QRR_NUMBER_LENGTH - 1 - extra = len(invoice_ref) - ref_payload_len - if extra > 0: - invoice_ref = invoice_ref[extra:] - internal_ref = invoice_ref.zfill(ref_payload_len) - return mod10r(internal_ref) + return self._compute_qrr_number(invoice_ref) else: return False + @api.model + def _compute_qrr_number(self, invoice_ref): + # keep only the last digits if it exceed boundaries + ref_payload_len = L10N_CH_QRR_NUMBER_LENGTH - 1 + extra = len(invoice_ref) - ref_payload_len + if extra > 0: + invoice_ref = invoice_ref[extra:] + internal_ref = invoice_ref.zfill(ref_payload_len) + return mod10r(internal_ref) + def _get_invoice_reference_ch_invoice(self): """ This sets QRR reference number which is generated based on customer's `Bank Account` and set it as `Payment Reference` of the invoice when invoice's journal is using Switzerland's communication standard diff --git a/addons/l10n_ch/models/account_journal.py b/addons/l10n_ch/models/account_journal.py index 2fa30c15f0d..e17fadceb14 100644 --- a/addons/l10n_ch/models/account_journal.py +++ b/addons/l10n_ch/models/account_journal.py @@ -15,3 +15,16 @@ class AccountJournal(models.Model): invoice_reference_model = fields.Selection(selection_add=[ ('ch', 'Switzerland') ], ondelete={'ch': lambda recs: recs.write({'invoice_reference_model': 'odoo'})}) + + def _process_reference_for_sale_order(self, order_reference): + ''' + Returns the order reference to be used for the payment, respecting the QRR standard. + ''' + self.ensure_one() + if self.invoice_reference_model == 'ch': + # converting the sale order name into a unique number. Letters are converted to their base10 value + invoice_ref = "".join([a if a.isdigit() else str(ord(a)) for a in order_reference]) + # id_number = self.company_id.bank_ids.l10n_ch_postal or '' + order_reference = self.env['account.move']._compute_qrr_number(invoice_ref) + return order_reference + return super()._process_reference_for_sale_order(order_reference) diff --git a/addons/l10n_ch/tests/test_swissqr.py b/addons/l10n_ch/tests/test_swissqr.py index 3a57d36410b..f49da319b85 100644 --- a/addons/l10n_ch/tests/test_swissqr.py +++ b/addons/l10n_ch/tests/test_swissqr.py @@ -5,6 +5,7 @@ import time from odoo.addons.account.tests.common import AccountTestInvoicingCommon from odoo.exceptions import UserError from odoo.tests import tagged +from odoo.tools.misc import mod10r CH_IBAN = 'CH15 3881 5158 3845 3843 7' QR_IBAN = 'CH21 3080 8001 2345 6782 7' @@ -180,3 +181,40 @@ class TestSwissQR(AccountTestInvoicingCommon): self.invoice1.partner_bank_id = qriban_account self.invoice1.action_post() self.swissqr_generated(self.invoice1, ref_type="QRR") + + def test_swiss_order_reference_qrr_for_qr_code(self): + """ + Test that the order reference is correctly generated for QR-Code + We summon the skipTest if Sale is not installed (instead of creating a whole module for one test) + """ + if 'sale.order' not in self.env: + self.skipTest('`sale` is not installed') + + payment_custom = self.env['ir.module.module']._get('payment_custom') + if payment_custom.state != 'installed': + self.skipTest("payment_custom module is not installed") + + provider = self.env['payment.provider'].create({ + 'name': 'Test', + 'code': 'custom', + }) + invoice_journal = self.env['account.journal'].search( + [('type', '=', 'sale'), ('company_id', '=', self.env.company.id)], limit=1) + invoice_journal.write({'invoice_reference_model': 'ch'}) + order = self.env['sale.order'].create({ + 'name': "S00001", + 'partner_id': self.env['res.partner'].search([("name", '=', 'Partner')])[0].id, + 'order_line': [ + (0, 0, {'product_id': self.product_a.id, 'price_unit': 100}), + ], + }) + payment_transaction = self.env['payment.transaction'].create({ + 'provider_id': provider.id, + 'sale_order_ids': [order.id], + 'partner_id': self.env['res.partner'].search([("name", '=', 'Partner')])[0].id, + 'amount': 100, + 'currency_id': self.env.company.currency_id.id, + }) + payment_transaction._set_pending() + + self.assertEqual(order.reference, mod10r(order.reference[:-1])) diff --git a/addons/sale/models/payment_transaction.py b/addons/sale/models/payment_transaction.py index 221556cc541..93ac1caf059 100644 --- a/addons/sale/models/payment_transaction.py +++ b/addons/sale/models/payment_transaction.py @@ -17,11 +17,16 @@ class PaymentTransaction(models.Model): def _compute_sale_order_reference(self, order): self.ensure_one() if self.provider_id.so_reference_type == 'so_name': - return order.name + order_reference = order.name else: # self.provider_id.so_reference_type == 'partner' identification_number = order.partner_id.id - return '%s/%s' % ('CUST', str(identification_number % 97).rjust(2, '0')) + order_reference = '%s/%s' % ('CUST', str(identification_number % 97).rjust(2, '0')) + + invoice_journal = self.env['account.journal'].search([('type', '=', 'sale'), ('company_id', '=', self.env.company.id)], limit=1) + order_reference = invoice_journal._process_reference_for_sale_order(order_reference) + + return order_reference @api.depends('sale_order_ids') def _compute_sale_order_ids_nbr(self):