From 75afbb159066775de67e28e79c9a96ba9d4084bb Mon Sep 17 00:00:00 2001
From: Kevin Deldycke <kevin@nexedi.com>
Date: Fri, 11 Mar 2005 14:12:29 +0000
Subject: [PATCH] better handling of bank account numbers (IBAN & BBAN)

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@2680 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/BankAccount.py | 165 +++++++++++++++++----------
 1 file changed, 107 insertions(+), 58 deletions(-)

diff --git a/product/ERP5/Document/BankAccount.py b/product/ERP5/Document/BankAccount.py
index eba8935c92a..bf3ec368f6f 100755
--- a/product/ERP5/Document/BankAccount.py
+++ b/product/ERP5/Document/BankAccount.py
@@ -31,16 +31,16 @@ from AccessControl import ClassSecurityInfo
 
 from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
 from Products.ERP5Type.Document.Folder import Folder
-
 from Products.ERP5.Document.Coordinate import Coordinate
 
 import string
 
+
 class BankAccount(Folder, Coordinate):
     """
     A bank account number holds a collection of numbers and codes (ex. SWIFT, RIB, etc.) which may be used to identify a bank account.
 
-    A Bank Account is owned by a Person or an Organisation. A Bank Account contain Agents with Agent Privileges used by the owner to delegate the management of the bank account to trusted third-party Persons. 
+    A Bank Account is owned by a Person or an Organisation. A Bank Account contain Agents with Agent Privileges used by the owner to delegate the management of the bank account to trusted third-party Persons.
 
     BankAccount inherits from Base and from the mix-in Coordinate.
     """
@@ -60,70 +60,119 @@ class BankAccount(Folder, Coordinate):
                       , PropertySheet.SimpleItem
                       , PropertySheet.Task
                       , PropertySheet.Resource
-		      , PropertySheet.BankAccount
+                      , PropertySheet.BankAccount
                       )
 
     # Declarative interfaces
     __implements__ = ( Interface.Coordinate )
 
-    security.declareProtected(Permissions.View, 'asText')
-    def asText(self):
+
+    security.declareProtected(Permissions.View, 'getIbanTextFormat')
+    def getIbanTextFormat(self):
+        """
+          Returns the standard IBAN text format
+        """
+        iban = self.getIban()
+        l = 4
+        s = "IBAN"
+        for i in range((len(iban) / l) + 1):
+          s += ' ' + iban[i*l : (i+1)*l]
+        return s
+
+
+    security.declareProtected(Permissions.View, 'getIbanTextFormat')
+    def getIban(self):
         """
-          Returns a string which identifies this bank account.
-          Use IBAN if available, then an id code (ex. RIB; SWIFT)
-          and finally a Bank / Branch / Number approach
+          The International Bank Account Number of this bank account.
+          IBAN is an international standard for identifying bank accounts worldwide.
         """
-        if self.iban != '':
-          return 'IBAN:%s' % self.iban
-        if self.bank_account_id != '':
-          return '%s:%s' % (self.bank_account_id_type, self.bank_account_id)
-        if self.bank_name != '':
-          return '%s / %s / %s' % (self.bank_name, self.bank_branch_name,
-                                                    self.bank_account_number)
-        return self.bank_account_number
-
-    security.declareProtected(Permissions.ModifyPortalContent, 'fromText')
-    def fromText(self, coordinate_text):
+        key          = self.getIbanKey()
+        country_code = self.getBankCountryCode()
+        bban         = self.getBban()
+        if key          == None: key          = ''
+        if country_code == None: country_code = ''
+        if bban         == None: bban         = ''
+        return (country_code + key + bban).upper()
+
+
+    security.declareProtected(Permissions.View, 'getIbanKey')
+    def getIbanKey(self):
         """
-          Tries to recognize the coordinate_text to update
-          this bank account
+          The IBAN key ensure the integry of the IBAN code.
+          It's calculated with the ISO 7064 method (known as "97-10 modulo").
         """
-        self.iban = ''
-        self.bank_name = ''
-        self.bank_branch_name = ''
-        self.bank_account_number = ''
-        self.bank_account_id_type = ''
-        self.bank_account_id = ''
-        if coordinate_text[0:4] == 'IBAN:':
-          self.iban = coordinate_text[5:]
-          self.reindexObject()
-          return
-        coordinate_parts = string.split(coordinate_text, ':')
-        if len(coordinate_parts) > 1:
-          self.bank_account_id_type = coordinate_parts[0]
-          self.bank_account_id = coordinate_parts[1]
-          self.reindexObject()
-          return
-        coordinate_parts = string.split(coordinate_text, '/')
-        if len(coordinate_parts) == 3:
-          self.bank_name = coordinate_parts[0]
-          self.bank_branch_name = coordinate_parts[1]
-          self.bank_account_number = coordinate_parts[2]
-          self.reindexObject()
-          return
-        self.bank_account_number = coordinate_text
-        self.reindexObject()
-
-    security.declareProtected(Permissions.View, 'standardTextFormat')
-    def standardTextFormat(self):
+        # Construct the alpha to number translation table
+        table = {}
+        for i in range(26):
+          table[chr(65+i)] = str(10+i)
+        # Calcul the key
+        country_code = self.getBankCountryCode() + '00'
+        s = self.getBban() + country_code
+        n = ''
+        for c in s:
+          if c.isalpha():
+            n += table[c.upper()]
+          if c.isdigit():
+            n += c
+        return str(98 - (int(n) % 97))
+
+
+    security.declareProtected(Permissions.View, 'getBban')
+    def getBban(self):
         """
-          Returns the standard text formats for bank accounts
+          The Basic Bank Account Number (BBAN) is the last part of the IBAN.
+          Usualy it correspond to the national bank account number.
         """
-        return ("""\
-IBAN: FR76 3002 7175 3900 0410 2760 135
-""", """\
-CIC / Calais / 3900 0410 2760
-""", """\
-RIB: FR76 3002 7175 3900 0410 2760 135
-""",
-)
+        bank   = self.getBankCode()
+        branch = self.getBranch()
+        ban    = self.getBankAccountNumber()
+        key    = self.getBbanKey()
+        if bank   == None: bank       = ''
+        if branch == None: branch     = ''
+        if ban    == None: account_id = ''
+        if key    == None: key        = ''
+        return (bank + branch + ban + key).upper()
+
+
+    security.declareProtected(Permissions.View, 'getBbanTextFormat')
+    def getBbanTextFormat(self, sep=' '):
+        """
+          Returns a BBAN text format
+        """
+        bank   = self.getBankCode()
+        branch = self.getBranch()
+        ban    = self.getBankAccountNumber()
+        key    = self.getBbanKey()
+        if bank   == None: bank       = ''
+        if branch == None: branch     = ''
+        if ban    == None: account_id = ''
+        if key    == None: key        = ''
+        return sep.join([bank, branch, ban, key]).upper()
+
+
+    security.declareProtected(Permissions.View, 'getBbanKey')
+    def getBbanKey(self):
+        """
+          The BBAN key ensure the integry of the BBAN code.
+          This is the french BBAN key algorithm.
+        """
+        def transcode(string):
+          letter = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+          digit  = '12345678912345678923456789'
+          for i in range(len(letter)):
+            string = string.replace(letter[i], digit[i])
+          return int(string)
+
+        bank   = self.getBankCode()
+        branch = self.getBranch()
+        ban    = self.getBankAccountNumber()
+        if bank   == None: bank   = ''
+        if branch == None: branch = ''
+        if ban    == None: ban    = ''
+
+        bank   += ('0' * (5 - len(bank)))
+        branch += ('0' * (5 - len(branch)))
+
+        s = (bank + branch + ban).upper()
+
+        return str(97 - ((transcode(s) * 100) % 97))
\ No newline at end of file
-- 
2.30.9