feat: set options for IBAN fields (backport #49377) (#49413)

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
This commit is contained in:
mergify[bot]
2025-10-14 14:42:08 +00:00
committed by GitHub
parent 6f02362765
commit bd3a132868
7 changed files with 27 additions and 78 deletions

View File

@@ -132,7 +132,8 @@
"fieldtype": "Data", "fieldtype": "Data",
"in_list_view": 1, "in_list_view": 1,
"label": "IBAN", "label": "IBAN",
"length": 30 "length": 34,
"options": "IBAN"
}, },
{ {
"fieldname": "column_break_12", "fieldname": "column_break_12",
@@ -208,6 +209,7 @@
"label": "Disabled" "label": "Disabled"
} }
], ],
"grid_page_length": 50,
"links": [ "links": [
{ {
"group": "Transactions", "group": "Transactions",
@@ -250,7 +252,7 @@
"link_fieldname": "default_bank_account" "link_fieldname": "default_bank_account"
} }
], ],
"modified": "2024-10-30 09:41:14.113414", "modified": "2025-08-29 12:32:01.081687",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Account", "name": "Bank Account",
@@ -282,6 +284,7 @@
"write": 1 "write": 1
} }
], ],
"row_format": "Dynamic",
"search_fields": "bank,account", "search_fields": "bank,account",
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",

View File

@@ -9,7 +9,8 @@ from frappe.contacts.address_and_contact import (
load_address_and_contact, load_address_and_contact,
) )
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import comma_and, get_link_to_form from frappe.utils import comma_and, get_link_to_form, validate_iban
from frappe.utils.deprecations import deprecated
class BankAccount(Document): class BankAccount(Document):
@@ -52,7 +53,6 @@ class BankAccount(Document):
def validate(self): def validate(self):
self.validate_company() self.validate_company()
self.validate_iban()
self.validate_account() self.validate_account()
self.update_default_bank_account() self.update_default_bank_account()
@@ -72,34 +72,10 @@ class BankAccount(Document):
if self.is_company_account and not self.company: if self.is_company_account and not self.company:
frappe.throw(_("Company is manadatory for company account")) frappe.throw(_("Company is manadatory for company account"))
@deprecated
def validate_iban(self): def validate_iban(self):
""" """Kept for backward compatibility, will be removed in v16."""
Algorithm: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN validate_iban(self.iban, throw=True)
"""
# IBAN field is optional
if not self.iban:
return
def encode_char(c):
# Position in the alphabet (A=1, B=2, ...) plus nine
return str(9 + ord(c) - 64)
# remove whitespaces, upper case to get the right number from ord()
iban = "".join(self.iban.split(" ")).upper()
# Move country code and checksum from the start to the end
flipped = iban[4:] + iban[:4]
# Encode characters as numbers
encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped]
try:
to_check = int("".join(encoded))
except ValueError:
frappe.throw(_("IBAN is not valid"))
if to_check % 97 != 1:
frappe.throw(_("IBAN is not valid"))
def update_default_bank_account(self): def update_default_bank_account(self):
if self.is_default and not self.disabled: if self.is_default and not self.disabled:

View File

@@ -3,45 +3,8 @@
import unittest import unittest
import frappe
from frappe import ValidationError
# test_records = frappe.get_test_records('Bank Account') # test_records = frappe.get_test_records('Bank Account')
class TestBankAccount(unittest.TestCase): class TestBankAccount(unittest.TestCase):
def test_validate_iban(self): pass
valid_ibans = [
"GB82 WEST 1234 5698 7654 32",
"DE91 1000 0000 0123 4567 89",
"FR76 3000 6000 0112 3456 7890 189",
]
invalid_ibans = [
# wrong checksum (3rd place)
"GB72 WEST 1234 5698 7654 32",
"DE81 1000 0000 0123 4567 89",
"FR66 3000 6000 0112 3456 7890 189",
]
bank_account = frappe.get_doc({"doctype": "Bank Account"})
try:
bank_account.validate_iban()
except AttributeError:
msg = "BankAccount.validate_iban() failed for empty IBAN"
self.fail(msg=msg)
for iban in valid_ibans:
bank_account.iban = iban
try:
bank_account.validate_iban()
except ValidationError:
msg = f"BankAccount.validate_iban() failed for valid IBAN {iban}"
self.fail(msg=msg)
for not_iban in invalid_ibans:
bank_account.iban = not_iban
msg = f"BankAccount.validate_iban() accepted invalid IBAN {not_iban}"
with self.assertRaises(ValidationError, msg=msg):
bank_account.validate_iban()

View File

@@ -146,6 +146,7 @@
"fieldname": "iban", "fieldname": "iban",
"fieldtype": "Data", "fieldtype": "Data",
"label": "IBAN", "label": "IBAN",
"options": "IBAN",
"read_only": 1 "read_only": 1
}, },
{ {
@@ -216,7 +217,7 @@
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-09-25 23:44:12.473583", "modified": "2025-09-26 00:38:17.584694",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Guarantee", "name": "Bank Guarantee",

View File

@@ -223,7 +223,8 @@
{ {
"fieldname": "bank_party_iban", "fieldname": "bank_party_iban",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Party IBAN (Bank Statement)" "label": "Party IBAN (Bank Statement)",
"options": "IBAN"
}, },
{ {
"fieldname": "bank_party_account_number", "fieldname": "bank_party_account_number",
@@ -238,7 +239,7 @@
"grid_page_length": 50, "grid_page_length": 50,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-09-26 17:06:29.207673", "modified": "2025-10-14 11:53:45.908169",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Transaction", "name": "Bank Transaction",

View File

@@ -226,7 +226,8 @@
"fetch_from": "bank_account.iban", "fetch_from": "bank_account.iban",
"fieldname": "iban", "fieldname": "iban",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"label": "IBAN" "label": "IBAN",
"options": "IBAN"
}, },
{ {
"fetch_from": "bank_account.branch_code", "fetch_from": "bank_account.branch_code",
@@ -443,11 +444,12 @@
"label": "Phone Number" "label": "Phone Number"
} }
], ],
"grid_page_length": 50,
"in_create": 1, "in_create": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-12-27 21:29:10.361894", "modified": "2025-08-29 11:52:48.555415",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Request", "name": "Payment Request",
@@ -482,8 +484,9 @@
"write": 1 "write": 1
} }
], ],
"row_format": "Dynamic",
"show_preview_popup": 1, "show_preview_popup": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [] "states": []
} }

View File

@@ -814,7 +814,8 @@
"depends_on": "eval:doc.salary_mode == 'Bank'", "depends_on": "eval:doc.salary_mode == 'Bank'",
"fieldname": "iban", "fieldname": "iban",
"fieldtype": "Data", "fieldtype": "Data",
"label": "IBAN" "label": "IBAN",
"options": "IBAN"
} }
], ],
"icon": "fa fa-user", "icon": "fa fa-user",
@@ -822,7 +823,7 @@
"image_field": "image", "image_field": "image",
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2025-07-04 08:29:34.347269", "modified": "2025-08-29 11:52:12.819878",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Employee", "name": "Employee",
@@ -864,6 +865,7 @@
"write": 1 "write": 1
} }
], ],
"row_format": "Dynamic",
"search_fields": "employee_name", "search_fields": "employee_name",
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_field": "modified", "sort_field": "modified",
@@ -871,4 +873,4 @@
"states": [], "states": [],
"title_field": "employee_name", "title_field": "employee_name",
"track_changes": 1 "track_changes": 1
} }