Skip to content

Commit

Permalink
[FIX] account,sale,l10n_ch_sale: create correct payment reference for…
Browse files Browse the repository at this point in the history
… swiss loca

Steps to reproduce:
- Install website_sale, contact, l10n_ch
- make sure your company is in Switzerland and has all the address information
- in settings, activate QR
- in Accounting/Journals/Bank:add a bank account (iban: CH4431999123000889012; qr-iban: CH11 3000 5228 1308 3501 F)
- install and enable the provider "Wire Transfer"
- From the Website, create an order and make sure the customer set an Invoicing address in Switzerland (address + Country)
- Validate the order
- In Website/unpaid orders: select your order and Confirm the order
- Create and confirm the invoice
- Print the invoice

Issue:
User Error is raised

Solution:
backport of #124331

opw-3615203

closes #147826

Signed-off-by: Yolann Sabaux (yosa) <yosa@odoo.com>
  • Loading branch information
yosa-odoo committed Jan 17, 2024
1 parent 40e0f65 commit 7778644
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 19 deletions.
8 changes: 8 additions & 0 deletions addons/account/models/account_journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,3 +972,11 @@ def _is_payment_method_available(self, payment_method_code):
if self.type not in journal_types:
return False
return True

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
38 changes: 21 additions & 17 deletions addons/l10n_ch/models/account_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,19 @@ def _get_isrb_id_number(self):
# number for Vendor bank accounts:
# - validation of format xx-yyyyy-c
# - validation of checksum
self.ensure_one()
return self.partner_bank_id.l10n_ch_postal or ''

@api.depends('name', 'partner_bank_id.l10n_ch_postal')
def _compute_l10n_ch_isr_number(self):
for record in self:
if (record.partner_bank_id.l10n_ch_qr_iban or record.l10n_ch_isr_subscription) and record.name:
invoice_ref = re.sub(r'\D', '', record.name)
record.l10n_ch_isr_number = record._compute_isr_number(invoice_ref)
else:
record.l10n_ch_isr_number = False

@api.model
def _compute_isr_number(self, invoice_ref):
"""Generates the ISR or QRR reference
An ISR references are 27 characters long.
Expand Down Expand Up @@ -118,22 +126,18 @@ def _compute_l10n_ch_isr_number(self):
(2) 12345678901234567890 | reference
(3) 1: control digit for identification number and reference
"""
for record in self:
if (record.partner_bank_id.l10n_ch_qr_iban or record.l10n_ch_isr_subscription) and record.name:
id_number = record._get_isrb_id_number()
if id_number:
id_number = id_number.zfill(l10n_ch_ISR_ID_NUM_LENGTH)
invoice_ref = re.sub('[^\d]', '', record.name)
# keep only the last digits if it exceed boundaries
full_len = len(id_number) + len(invoice_ref)
ref_payload_len = l10n_ch_ISR_NUMBER_LENGTH - 1
extra = full_len - ref_payload_len
if extra > 0:
invoice_ref = invoice_ref[extra:]
internal_ref = invoice_ref.zfill(ref_payload_len - len(id_number))
record.l10n_ch_isr_number = mod10r(id_number + internal_ref)
else:
record.l10n_ch_isr_number = False
id_number = self._get_isrb_id_number()
if id_number:
id_number = id_number.zfill(l10n_ch_ISR_ID_NUM_LENGTH)
# keep only the last digits if it exceed boundaries
full_len = len(id_number) + len(invoice_ref)
ref_payload_len = l10n_ch_ISR_NUMBER_LENGTH - 1
extra = full_len - ref_payload_len
if extra > 0:
invoice_ref = invoice_ref[extra:]
internal_ref = invoice_ref.zfill(ref_payload_len - len(id_number))

return mod10r(id_number + internal_ref)

@api.depends('l10n_ch_isr_number')
def _compute_l10n_ch_isr_number_spaced(self):
Expand Down
13 changes: 13 additions & 0 deletions addons/l10n_ch/models/account_journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 ISR
'''
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_isr_number(invoice_ref)
return order_reference
return super()._process_reference_for_sale_order(order_reference)
28 changes: 28 additions & 0 deletions addons/l10n_ch/tests/test_swissqr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from odoo.addons.account.tests.common import AccountTestInvoicingCommon
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'
Expand Down Expand Up @@ -182,3 +183,30 @@ def test_swissQR_qriban(self):
self.invoice1.partner_bank_id = qriban_account
self.invoice1.action_post()
self.swissqr_generated(self.invoice1, ref_type="QRR")

def test_swiss_order_reference_isr_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')

acquirer = self.env['payment.acquirer'].create({'name': 'Test',})
order = self.env['sale.order'].create({
'name': "S00001",
'partner_id': self.customer.id,
'order_line': [
(0, 0, {'product_id': self.product_a.id, 'price_unit': 100}),
],
})
payment_transaction = self.env['payment.transaction'].create({
'acquirer_id': acquirer.id,
'sale_order_ids': [order.id],
'partner_id': self.customer.id,
'amount': 100,
'currency_id': self.env.company.currency_id.id,
})
payment_transaction._set_pending()

self.assertEqual(order.reference, mod10r(order.reference[:-1]))
9 changes: 7 additions & 2 deletions addons/sale/models/payment_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ class PaymentTransaction(models.Model):
def _compute_sale_order_reference(self, order):
self.ensure_one()
if self.acquirer_id.so_reference_type == 'so_name':
return order.name
order_reference = order.name
else:
# self.acquirer_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):
Expand Down

0 comments on commit 7778644

Please sign in to comment.