From 04928f78b8361fb592341a957625a70cff76423f Mon Sep 17 00:00:00 2001 From: markus_bozem Date: Fri, 17 Feb 2017 21:14:57 +0100 Subject: [PATCH] IDEMPIERE-1200 Validate IBAN in C_BankAccount, C_BP_BankAccount, C_Payment, C_PaymentTransaction Config via IBAN_VALIDATION, Default is Y --- .../oracle/201701191158_IDEMPIERE-1200.sql | 10 ++ .../201701191158_IDEMPIERE-1200.sql | 7 + .../org/compiere/model/MBPBankAccount.java | 14 ++ .../src/org/compiere/model/MBankAccount.java | 41 +++--- .../src/org/compiere/model/MPayment.java | 13 ++ .../compiere/model/MPaymentTransaction.java | 13 ++ .../src/org/compiere/model/MSysConfig.java | 1 + .../src/org/compiere/util/IBAN.java | 136 ++++++++++-------- 8 files changed, 161 insertions(+), 74 deletions(-) create mode 100644 migration/i4.1/oracle/201701191158_IDEMPIERE-1200.sql create mode 100644 migration/i4.1/postgresql/201701191158_IDEMPIERE-1200.sql diff --git a/migration/i4.1/oracle/201701191158_IDEMPIERE-1200.sql b/migration/i4.1/oracle/201701191158_IDEMPIERE-1200.sql new file mode 100644 index 0000000000..13cd5a6e95 --- /dev/null +++ b/migration/i4.1/oracle/201701191158_IDEMPIERE-1200.sql @@ -0,0 +1,10 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IBAN validation IDEMPIERE-1200 +-- Jan 19, 2017 10:54:38 AM CET +INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200086,0,0,TO_DATE('2017-01-19 10:54:37','YYYY-MM-DD HH24:MI:SS'),TO_DATE('2017-01-19 10:54:37','YYYY-MM-DD HH24:MI:SS'),0,0,'Y','IBAN_VALIDATION','Y','Enables the validation of IBAN fields','D','S','1b324142-ae32-4cff-9ea9-792df3e4d142') +; + +SELECT register_migration_script('201701191158_IDEMPIERE-1200') FROM dual +; \ No newline at end of file diff --git a/migration/i4.1/postgresql/201701191158_IDEMPIERE-1200.sql b/migration/i4.1/postgresql/201701191158_IDEMPIERE-1200.sql new file mode 100644 index 0000000000..85bcdb0f1d --- /dev/null +++ b/migration/i4.1/postgresql/201701191158_IDEMPIERE-1200.sql @@ -0,0 +1,7 @@ +-- IBAN validation IDEMPIERE-1200 +-- Jan 19, 2017 10:54:38 AM CET +INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200086,0,0,TO_TIMESTAMP('2017-01-19 10:54:37','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2017-01-19 10:54:37','YYYY-MM-DD HH24:MI:SS'),0,0,'Y','IBAN_VALIDATION','Y','Enables the validation of IBAN fields','D','S','1b324142-ae32-4cff-9ea9-792df3e4d142') +; + +SELECT register_migration_script('201701191158_IDEMPIERE-1200') FROM dual +; \ No newline at end of file diff --git a/org.adempiere.base/src/org/compiere/model/MBPBankAccount.java b/org.adempiere.base/src/org/compiere/model/MBPBankAccount.java index f87b8192ef..424c557537 100644 --- a/org.adempiere.base/src/org/compiere/model/MBPBankAccount.java +++ b/org.adempiere.base/src/org/compiere/model/MBPBankAccount.java @@ -22,6 +22,9 @@ import java.util.Properties; import org.adempiere.util.PaymentUtil; import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.IBAN; +import org.compiere.util.Util; /** * BP Bank Account Model @@ -207,6 +210,17 @@ public class MBPBankAccount extends X_C_BP_BankAccount setCreditCardVV(encrpytedCvv); } + if (MSysConfig.getBooleanValue(MSysConfig.IBAN_VALIDATION, false, + Env.getContextAsInt(Env.getCtx(), "#AD_Client_ID"))) { + if (!Util.isEmpty(getIBAN())) { + setIBAN(IBAN.normalizeIBAN(getIBAN())); + if (!IBAN.isCheckDigitValid(getIBAN())) { + log.saveError("Error", "IBAN is invalid"); + return false; + } + } + } + return true; } // beforeSave diff --git a/org.adempiere.base/src/org/compiere/model/MBankAccount.java b/org.adempiere.base/src/org/compiere/model/MBankAccount.java index 9e7604519d..fc7522f0ed 100644 --- a/org.adempiere.base/src/org/compiere/model/MBankAccount.java +++ b/org.adempiere.base/src/org/compiere/model/MBankAccount.java @@ -123,6 +123,29 @@ public class MBankAccount extends X_C_BankAccount return msgreturn.toString(); } // getName + + /** + * Before Save + * @param newRecord new record + * @return success + */ + + protected boolean beforeSave(boolean newRecord) { + + if (MSysConfig.getBooleanValue(MSysConfig.IBAN_VALIDATION, false, + Env.getContextAsInt(Env.getCtx(), "#AD_Client_ID"))) { + if (!Util.isEmpty(getIBAN())) { + setIBAN(IBAN.normalizeIBAN(getIBAN())); + if (!IBAN.isCheckDigitValid(getIBAN())) { + log.saveError("Error", "IBAN is invalid"); + return false; + } + } + } + + return true; + } // beforeSave + /** * After Save * @param newRecord new record @@ -135,23 +158,5 @@ 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 diff --git a/org.adempiere.base/src/org/compiere/model/MPayment.java b/org.adempiere.base/src/org/compiere/model/MPayment.java index b495b1520a..5ef79f52e3 100644 --- a/org.adempiere.base/src/org/compiere/model/MPayment.java +++ b/org.adempiere.base/src/org/compiere/model/MPayment.java @@ -38,8 +38,10 @@ import org.compiere.process.ProcessInfo; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; +import org.compiere.util.IBAN; import org.compiere.util.Msg; import org.compiere.util.Trx; +import org.compiere.util.Util; import org.compiere.util.ValueNamePair; /** @@ -796,6 +798,17 @@ public class MPayment extends X_C_Payment } } + if (MSysConfig.getBooleanValue(MSysConfig.IBAN_VALIDATION, false, + Env.getContextAsInt(Env.getCtx(), "#AD_Client_ID"))) { + if (!Util.isEmpty(getIBAN())) { + setIBAN(IBAN.normalizeIBAN(getIBAN())); + if (!IBAN.isCheckDigitValid(getIBAN())) { + log.saveError("Error", "IBAN is invalid"); + return false; + } + } + } + return true; } // beforeSave diff --git a/org.adempiere.base/src/org/compiere/model/MPaymentTransaction.java b/org.adempiere.base/src/org/compiere/model/MPaymentTransaction.java index 5c9b59867a..c6738d6c5d 100644 --- a/org.adempiere.base/src/org/compiere/model/MPaymentTransaction.java +++ b/org.adempiere.base/src/org/compiere/model/MPaymentTransaction.java @@ -26,8 +26,10 @@ import org.compiere.process.DocAction; import org.compiere.process.ProcessCall; import org.compiere.process.ProcessInfo; import org.compiere.util.Env; +import org.compiere.util.IBAN; import org.compiere.util.Msg; import org.compiere.util.Trx; +import org.compiere.util.Util; /** * @@ -81,6 +83,17 @@ public class MPaymentTransaction extends X_C_PaymentTransaction implements Proce setCreditCardVV(encrpytedCvv); } + if (MSysConfig.getBooleanValue(MSysConfig.IBAN_VALIDATION, false, + Env.getContextAsInt(Env.getCtx(), "#AD_Client_ID"))) { + if (!Util.isEmpty(getIBAN())) { + setIBAN(IBAN.normalizeIBAN(getIBAN())); + if (!IBAN.isCheckDigitValid(getIBAN())) { + log.saveError("Error", "IBAN is invalid"); + return false; + } + } + } + return true; } diff --git a/org.adempiere.base/src/org/compiere/model/MSysConfig.java b/org.adempiere.base/src/org/compiere/model/MSysConfig.java index 57990670e8..6be031d1ab 100644 --- a/org.adempiere.base/src/org/compiere/model/MSysConfig.java +++ b/org.adempiere.base/src/org/compiere/model/MSysConfig.java @@ -84,6 +84,7 @@ public class MSysConfig extends X_AD_SysConfig public static final String ENABLE_PAYMENTBOX_BUTTON = "ENABLE_PAYMENTBOX_BUTTON"; public static final String GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS = "GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS"; public static final String HTML_REPORT_THEME = "HTML_REPORT_THEME"; + public static final String IBAN_VALIDATION = "IBAN_VALIDATION" ; public static final String Invoice_ReverseUseNewNumber = "Invoice_ReverseUseNewNumber"; public static final String JASPER_SWAP_MAX_PAGES = "JASPER_SWAP_MAX_PAGES"; public static final String LASTRUN_RECORD_COUNT = "LASTRUN_RECORD_COUNT"; diff --git a/org.adempiere.base/src/org/compiere/util/IBAN.java b/org.adempiere.base/src/org/compiere/util/IBAN.java index c8a58f4b0c..46a1a8e85c 100644 --- a/org.adempiere.base/src/org/compiere/util/IBAN.java +++ b/org.adempiere.base/src/org/compiere/util/IBAN.java @@ -5,63 +5,87 @@ 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); + + /** + * @param iban + * @return normalized IBAN + */ + + public static String normalizeIBAN(String iban) + { + if (iban!=null) + { + return iban.trim().replace(" ", "") ; + } + return null ; + } + /** + * 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) { + try { + if (null == iban) + return false; + int validIBANLength = getValidIBANLength(iban); + if (validIBANLength < 4) + return false; + if (iban.length() != validIBANLength) + return 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()); - } + BigInteger numericIBAN = getNumericIBAN(iban, false); - private static int getAlphabetPosition(char letter) { - return Character.valueOf(Character.toUpperCase(letter)).compareTo(Character.valueOf('A')); - } + int checkDigit = numericIBAN.mod(new BigInteger("97")).intValue(); + return checkDigit == 1; + } catch (Exception e) { + return false; + } + } + + /** + * 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')); + } }