IDEMPIERE-1200 Check consistency of IBAN

This commit is contained in:
Nicolas Micoud 2017-02-17 21:14:00 +01:00
parent d5cba25082
commit b4f0213136
3 changed files with 132 additions and 0 deletions

View File

@ -21,6 +21,8 @@ import java.util.Properties;
import org.compiere.util.CCache;
import org.compiere.util.Env;
import org.compiere.util.IBAN;
import org.compiere.util.Util;
/**
@ -133,5 +135,23 @@ public class MBankAccount extends X_C_BankAccount
return insert_Accounting("C_BankAccount_Acct", "C_AcctSchema_Default", null);
return success;
} // afterSave
protected boolean beforeSave (boolean newRecord)
{
if (!Util.isEmpty(getIBAN())) {
setIBAN(getIBAN().trim().replace(" ", ""));
try {
if (!IBAN.isCheckDigitValid(getIBAN())) {
log.saveError("Error", "IBAN is invalid");
return false;
}
} catch (Exception e) {
log.saveError("Error", "IBAN is invalid");
return false;
}
}
return true;
}
} // MBankAccount

View File

@ -0,0 +1,67 @@
package org.compiere.util;
import java.math.BigInteger;
import java.util.ResourceBundle;
public class IBAN {
/**
* Determines if the given IBAN is valid based on the check digit.
* To validate the checksum:
* 1. Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid.
* 2. Move the four initial characters to the end of the string.
* 3. Replace the letters in the string with digits, expanding the string as necessary, such that A=10, B=11 and Z=35.
* 4. Convert the string to an integer and mod-97 the entire number.
* If the remainder is 1 you have a valid IBAN number.
* @param iban
* @return boolean indicating if specific IBAN has a valid check digit
*/
public static boolean isCheckDigitValid(String iban) {
if (null == iban) return false;
int validIBANLength = getValidIBANLength(iban);
if (validIBANLength < 4) return false;
if (iban.length() != validIBANLength) return false;
BigInteger numericIBAN = getNumericIBAN(iban, false);
int checkDigit = numericIBAN.mod(new BigInteger("97")).intValue();
return checkDigit == 1;
}
/**
* Using the IBAN.properties file gets the valid fixed length value for a country code.
* Only uses the first 2 characters of the given string.
* @param countryCode
* @return
*/
public static int getValidIBANLength(String countryCode) {
String code = countryCode.substring(0,2).toUpperCase();
String length = ResourceBundle.getBundle(IBAN.class.getCanonicalName()).getString("length."+code);
if (length == null) return -1;
return Integer.valueOf(length).intValue();
}
private static BigInteger getNumericIBAN(String iban, boolean isCheckDigitAtEnd) {
String endCheckDigitIBAN = iban;
if (!isCheckDigitAtEnd) {
//Move first four characters to end of string to put check digit at end
endCheckDigitIBAN = iban.substring(4) + iban.substring(0, 4);
}
StringBuffer numericIBAN = new StringBuffer();
for (int i = 0; i < endCheckDigitIBAN.length(); i++) {
if (Character.isDigit(endCheckDigitIBAN.charAt(i))) {
numericIBAN.append(endCheckDigitIBAN.charAt(i));
} else {
numericIBAN.append(10 + getAlphabetPosition(endCheckDigitIBAN.charAt(i)));
}
}
return new BigInteger(numericIBAN.toString());
}
private static int getAlphabetPosition(char letter) {
return Character.valueOf(Character.toUpperCase(letter)).compareTo(Character.valueOf('A'));
}
}

View File

@ -0,0 +1,45 @@
# IBAN related properties. Includes IBAN lengths for different countries
length.AD=24
length.AT=20
length.BE=16
length.BA=20
length.BG=22
length.CH=21
length.CY=28
length.CZ=24
length.DE=22
length.DK=18
length.EE=20
length.ES=24
length.FO=18
length.FI=18
length.FR=27
length.GB=22
length.GI=23
length.GL=18
length.GR=27
length.HU=28
length.HR=21
length.IE=22
length.IS=26
length.IT=27
length.LI=21
length.LT=20
length.LU=20
length.LV=21
length.MA=24
length.MC=27
length.MK=19
length.MT=31
length.NL=18
length.NO=15
length.PL=28
length.PT=25
length.RO=24
length.RS=22
length.SE=24
length.SI=19
length.SK=24
length.SM=27
length.TN=24
length.TR=26