Files
ash_cz_qr_payment/models/account_move.py
T
Tomas cbf5fe3450 Update: Add EUR currency support for Slovak companies
- Modified get_cz_qr_code_base64() to support both CZK and EUR
- Updated cz_to_iban() to better handle international IBANs (SK, etc.)
- SPAYD QR now uses actual invoice currency in CC: field
- Updated manifest description to reflect EUR support
- Updated README with setup instructions for Slovak companies

This allows Slovak companies with EUR accounts to generate SPAYD QR codes
for Czech customers, enabling cross-border EUR payments via mobile banking.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-08-25 00:58:06 +02:00

90 lines
3.3 KiB
Python

import qrcode
import base64
import io
from odoo import models, api
class AccountMove(models.Model):
_inherit = 'account.move'
def get_cz_qr_code_base64(self):
""" Returns Base64 PNG image for the invoice PDF """
self.ensure_one()
# Support both EUR and CZK currencies
# (for Slovak companies invoicing Czech customers in EUR)
allowed_currencies = ['CZK', 'EUR']
if self.currency_id.name not in allowed_currencies:
return False
# Find bank: 1. Matching currency, 2. No currency set (default)
bank = self.company_id.partner_id.bank_ids.filtered(lambda b: b.currency_id == self.currency_id)[:1]
if not bank:
bank = self.company_id.partner_id.bank_ids.filtered(lambda b: not b.currency_id)[:1]
if not bank or not bank.acc_number:
return False
# Build SPAYD string
# Note: cz_to_iban handles both CZ format (123-456/0800) and IBAN format (SK..., CZ..., etc.)
iban = self.cz_to_iban(bank.acc_number)
amount = '%.2f' % self.amount_residual
vs = ''.join(filter(str.isdigit, self.payment_reference or ''))[-10:]
currency = self.currency_id.name or 'EUR'
spayd = f"SPD*1.0*ACC:{iban}*AM:{amount}*CC:{currency}*X-VS:{vs}"
# Generate QR Image
qr = qrcode.make(spayd)
buf = io.BytesIO()
qr.save(buf, format="PNG")
return base64.b64encode(buf.getvalue()).decode('utf-8')
def cz_to_iban(self, acc_number):
"""
Converts Czech account number formats to IBAN.
Supports:
- Czech local format: '123-456/0800' or '456/0800'
- IBAN format: 'CZ6508000000001234567890', 'SK6509000000005123456789', etc.
Returns:
str: Valid IBAN (uppercase) or original input if conversion fails
"""
if not acc_number:
return ""
# Clean the input
clean_acc = acc_number.replace(" ", "").upper()
# If already an IBAN (any country), return it
# IBAN format: 2-letter country code + 2-digit checksum + account number
if len(clean_acc) >= 4 and clean_acc[:2].isalpha() and clean_acc[2:4].isdigit():
return clean_acc
try:
# Split into [prefix-main, bank_code]
parts = clean_acc.split('/')
if len(parts) != 2:
return clean_acc # Fallback for non-CZ format
bank_code = parts[1]
account_parts = parts[0].split('-')
if len(account_parts) == 2:
prefix = account_parts[0].zfill(6)
main = account_parts[1].zfill(10)
else:
prefix = "000000"
main = account_parts[0].zfill(10)
# 1. Start with Bank Code + Prefix + Main + Country Number (CZ=12, 35=Z) + 00
# Czech Republic code is 123500 (12 for C, 35 for Z)
check_string = f"{bank_code}{prefix}{main}123500"
# 2. Calculate checksum
checksum = 98 - (int(check_string) % 97)
checksum_str = str(checksum).zfill(2)
return f"CZ{checksum_str}{bank_code}{prefix}{main}"
except Exception:
return clean_acc