[FIX] l10n_ch_sale: create correct payment reference for 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

Cause:
With Swiss QR code you need to have a valid payment reference. That is, an ISR reference such as in
https://github.com/odoo/odoo/blob/c6631df1c5b0b6d4c2268a826ca150edd0ca653e/addons/l10n_ch/models/account_invoice.py#L90

But when you create an order from the website, it creates an automatic reference "SO0001" which, when converted to an invoice, stays the same.
Since the reference is prepoluted, the `_compute_l10n_ch_isr_number` will not be triggered and therefore will raise an error when trying to print the invoice.

Solution:
Check if the Customer Invoice Journal uses the swiss reference model, then we know that the swiss loca is installed and can call the correct function to compute the reference/

Note:
In the test we check that `payment_custom` is installed. It is because the `_set_pending` method checks taht the payment_provider.code is "custom"="wire_transfer" in which case the sale order reference is computed
https://github.com/odoo/odoo/blob/b4ed9537895ecee48ed146add4257af0aebeb3f4/addons/sale/models/payment_transaction.py#L54-L56

opw-3334534

closes odoo/odoo#127338

X-original-commit: dfa4844e57b67d16b181e1e58813cdbbc7efa956
Signed-off-by: Yolann Sabaux (yosa) <yosa@odoo.com>
Signed-off-by: Olivier Colson (oco) <oco@odoo.com>
Co-authored-by: flvr-odoo <flvr@odoo.com>
This commit is contained in:
Yolann Sabaux
2023-07-20 03:56:26 +02:00
committed by oco-odoo
co-authored by flvr-odoo
parent fd2fb212c5
commit 7cc4b6cfe8
5 changed files with 77 additions and 9 deletions
+8
View File
@@ -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
+11 -7
View File
@@ -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
+13
View File
@@ -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)
+38
View File
@@ -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]))
+7 -2
View File
@@ -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):