home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / TRIAL / JBUILDER / JVISBRKR.Z / CreditHistoryReviewer.java < prev    next >
Encoding:
Java Source  |  1998-05-08  |  12.0 KB  |  288 lines

  1. /*
  2.  * Copyright (c) 1997-1998 Borland International, Inc. All Rights Reserved.
  3.  * 
  4.  * This SOURCE CODE FILE, which has been provided by Borland as part
  5.  * of a Borland product for use ONLY by licensed users of the product,
  6.  * includes CONFIDENTIAL and PROPRIETARY information of Borland.  
  7.  *
  8.  * USE OF THIS SOFTWARE IS GOVERNED BY THE TERMS AND CONDITIONS 
  9.  * OF THE LICENSE STATEMENT AND LIMITED WARRANTY FURNISHED WITH
  10.  * THE PRODUCT.
  11.  *
  12.  * IN PARTICULAR, YOU WILL INDEMNIFY AND HOLD BORLAND, ITS RELATED
  13.  * COMPANIES AND ITS SUPPLIERS, HARMLESS FROM AND AGAINST ANY CLAIMS
  14.  * OR LIABILITIES ARISING OUT OF THE USE, REPRODUCTION, OR DISTRIBUTION
  15.  * OF YOUR PROGRAMS, INCLUDING ANY CLAIMS OR LIABILITIES ARISING OUT OF
  16.  * OR RESULTING FROM THE USE, MODIFICATION, OR DISTRIBUTION OF PROGRAMS
  17.  * OR FILES CREATED FROM, BASED ON, AND/OR DERIVED FROM THIS SOURCE
  18.  * CODE FILE.
  19.  */
  20. //--------------------------------------------------------------------------------------------------
  21. // CORBA Reference Application
  22. // Copyright (c) 1997 by Borland International, All Rights Reserved
  23. //
  24. // Contains logic necessary to issue credit cards.
  25. //--------------------------------------------------------------------------------------------------
  26.  
  27. package borland.reference.creditapproval.server;
  28.  
  29. import borland.sql.dataset.*;
  30. import borland.jbcl.dataset.*;
  31. import borland.jbcl.control.*;
  32. import java.util.*;
  33. import borland.reference.creditapproval.CORBAInterface.*;
  34.  
  35. /**
  36. * CreditHistoryReviewer reviews the credit history of a specified applicant.
  37. *
  38. * History is reviewed to determine if an applicant should be granted a cliffhanger
  39. * credit card, and if so, the amount of credit that should be extended.
  40. *
  41. * The following business rules are used:
  42. *
  43. * Credit Approval:
  44. *  1) Deny credit if 10% of all payments were late.
  45. *  2) Deny credit if any accounts are in 'COLLECTIONS'.
  46. *  3) Deny credit if the percentage of total monthly payments(TMP)
  47. *     to total monthly income(TMI) is greater than 40%.
  48. *
  49. * Credit Limit calculation:
  50. *  1) Calculate maximum monthly payment (MMP):
  51. *        MMP = (0.4 * TMI) - TMP
  52. *  2) Since monthly payment is 5% of total balance, the credit
  53. *     limit can be calculated by dividing the MMP by 5%.
  54. *      LIMIT = MMP / 0.05
  55. *  3) Make sure credit limit is in the range of $100 - $5000
  56. *    (This range is resourced to enable conversion into foreign currency)
  57. *
  58. */
  59. public class CreditHistoryReviewer implements DataModule{
  60.  
  61.   private boolean generateRandomData;
  62.   private static CreditHistoryReviewer myDM;
  63.   private static final String ACCOUNT_STATUS_COLLECTIONS = "COLLECTION";
  64.   private static final String ACCOUNT_STATUS_OPEN = "OPEN";
  65.  
  66.   Database acmeDb = new Database();
  67.   QueryDataSet queryCreditHistory = new QueryDataSet();
  68.   QueryDataSet queryHistoryIdLookupByName = new QueryDataSet();
  69.   QueryDataSet queryHistoryIdLookupByPid = new QueryDataSet();
  70.   ResourceBundle res = Res.getBundle("borland.reference.creditapproval.server.Res");
  71.  
  72.   public CreditHistoryReviewer( boolean randomData ) throws Exception {
  73.     generateRandomData = randomData;
  74.     try {
  75.       jbInit();
  76.     }
  77.     catch (Exception e) {
  78.       e.printStackTrace();
  79.       throw(e);
  80.     }
  81.   }
  82.  
  83.   private void jbInit() throws Exception{
  84.  
  85.     acmeDb.setConnection(new borland.sql.dataset.ConnectionDescriptor("jdbc:odbc:ACMECreditBureau", "SYSDBA", "masterkey", false, "sun.jdbc.odbc.JdbcOdbcDriver"));
  86.     acmeDb.setTransactionIsolation(java.sql.Connection.TRANSACTION_NONE);
  87.     acmeDb.setSQLDialect(borland.sql.dataset.SQLDialect.INTERBASE);
  88.  
  89.     ParameterRow historyParameters = new ParameterRow();
  90.     historyParameters.addColumn( "HISTORYID", borland.jbcl.util.Variant.INT );
  91.     queryCreditHistory.setReadOnly(true);
  92.     queryCreditHistory.setTableName("");
  93.     queryCreditHistory.setQuery(new borland.sql.dataset.QueryDescriptor(acmeDb, "SELECT TYPE, STATUS, MONTHLYPAYMENT, PASTDUE120, PASTDUE90, PASTDUE60,  PASTDUE30, TOTALPAYMENTS FROM ACCOUNT WHERE HISTORYID =:HISTORYID", historyParameters, false, Load.ALL));
  94.  
  95.     ParameterRow byNameParameters = new ParameterRow();
  96.     byNameParameters.addColumn( "APPLICANTDOB", borland.jbcl.util.Variant.DATE );
  97.     byNameParameters.addColumn( "APPLICANTFIRSTNAME", borland.jbcl.util.Variant.STRING );
  98.     byNameParameters.addColumn( "APPLICANTLASTNAME", borland.jbcl.util.Variant.STRING );
  99.     queryHistoryIdLookupByName.setReadOnly(true);
  100.     queryHistoryIdLookupByName.setQuery(new borland.sql.dataset.QueryDescriptor(acmeDb, "SELECT HISTORYID, FIRSTNAME, MI FROM CREDITHISTORY WHERE DOB = :APPLICANTDOB AND Upper( FIRSTNAME ) = :APPLICANTFIRSTNAME AND Upper( LASTNAME ) = :APPLICANTLASTNAME", byNameParameters, false, Load.ALL));
  101.  
  102.     ParameterRow byPidParameters = new ParameterRow();
  103.     byPidParameters.addColumn( "APPLICANTID", borland.jbcl.util.Variant.STRING );
  104.     queryHistoryIdLookupByPid.setDisplayErrors(true);
  105.     queryHistoryIdLookupByPid.setReadOnly(true);
  106.     queryHistoryIdLookupByPid.setQuery(new borland.sql.dataset.QueryDescriptor(acmeDb, "SELECT HISTORYID  FROM CREDITHISTORY  WHERE IDENTIFICATION =  :APPLICANTID", byPidParameters, false, Load.ALL));
  107.    }
  108.  
  109.   /**
  110.   *  Connect/Disconnect to/from the database based on 'connect' parameter.
  111.   */
  112.   void connect(boolean open) throws DataSetException {
  113.     if (open) {
  114.       // Try to connect every 5 seconds for a total of 30 seconds
  115.       if (!acmeDb.isOpen()) new TimedConnect(acmeDb, 2, 8 );
  116.     }
  117.     else {
  118.       if (acmeDb.isOpen()) {
  119.         try {
  120.           acmeDb.closeConnection();
  121.         } catch (Exception e) {
  122.           System.err.println( e );
  123.         }
  124.       }
  125.     }
  126.   }
  127.   /**
  128.   * Return singleton instance of the DataModule
  129.   */
  130.   static public CreditHistoryReviewer getDataModule() throws Exception {
  131.     if (myDM == null)
  132.       myDM = new CreditHistoryReviewer( false );
  133.     return myDM;
  134.   }
  135.  
  136.   public borland.sql.dataset.QueryDataSet getQueryCreditHistory() {
  137.     return queryCreditHistory;
  138.   }
  139.  
  140.   /**
  141.   * Queries the ACME Database to find a Credit History ID for the
  142.   * applicant. If one does not exist, the credit history for the applicant
  143.   * is not tracked by ACME and the applicant cannot be approved.
  144.   */
  145.   private int getCreditHistoryId( applicantInfoStruct appInfo )
  146.       throws Exception {
  147.     int historyId = -1;
  148.  
  149.      //return new CreditHistoryRandomData().generateRandomData( appInfo, acmeDb );
  150.  
  151.     // If an IDENTIFICATION number was not specified for the applicant,
  152.     // attempt to find them using their DOB and Name.
  153.     if (appInfo.PID.trim().length() == 0 ||
  154.         appInfo.PID.trim().compareTo("0") == 0) {
  155.         //ParameterRow parameters = queryHistoryIdLookupByName.getParameterRow();
  156.         ReadWriteRow   parameters = queryHistoryIdLookupByName.getParameterRow();
  157.         parameters.setDate( "APPLICANTDOB", java.sql.Date.valueOf( appInfo.DOB ) );
  158.         parameters.setString( "APPLICANTFIRSTNAME", appInfo.firstName.trim().toUpperCase() );
  159.         parameters.setString( "APPLICANTLASTNAME", appInfo.lastName.trim().toUpperCase() );
  160.         queryHistoryIdLookupByName.open();
  161.         queryHistoryIdLookupByName.executeQuery();
  162.  
  163.         // If the applicant is not in the ACME Database, throw an exception
  164.         if (queryHistoryIdLookupByName.getRowCount() == 0) {
  165.           if (generateRandomData) {
  166.             return new CreditHistoryRandomData().generateRandomData( appInfo, acmeDb );
  167.           }
  168.           else throw new CreditDeniedException(
  169.                  res.getString("Applicant_not_in_ACME"));
  170.         }
  171.         queryHistoryIdLookupByName.first();
  172.         historyId = queryHistoryIdLookupByName.getInt( "HISTORYID" );
  173.     }
  174.     // Attempt to find the applicant using their IDENTIFICATION number
  175.     else {
  176.         //ParameterRow parameters = queryHistoryIdLookupByPid.getParameterRow();
  177.         ReadWriteRow parameters = queryHistoryIdLookupByPid.getParameterRow();
  178.  
  179.         parameters.setString( "APPLICANTID", appInfo.PID );
  180.  
  181.         queryHistoryIdLookupByPid.open();
  182.         queryHistoryIdLookupByPid.executeQuery();
  183.  
  184.         // If the applicant is not in the ACME Database, throw an exception
  185.         if (queryHistoryIdLookupByPid.getRowCount() == 0) {
  186.           if (generateRandomData) {
  187.             return new CreditHistoryRandomData().generateRandomData( appInfo, acmeDb );
  188.           }
  189.           else throw new CreditDeniedException(
  190.                  res.getString("Applicant_not_in_ACME"));
  191.         }
  192.         queryHistoryIdLookupByPid.first();
  193.         historyId = queryHistoryIdLookupByPid.getInt( "HISTORYID" );
  194.     }
  195.     return historyId;
  196.   }
  197.  
  198.   /**
  199.   * Implements the Credit Approval and Limit Calculation business rules.
  200.   */
  201.   public creditApprovalStruct approveCredit(applicantInfoStruct appInfo)
  202.     throws Exception {
  203.  
  204.     creditApprovalStruct approval = new creditApprovalStruct();
  205.  
  206.     int historyId = getCreditHistoryId( appInfo );
  207.  
  208.     // Retrieve account information for applicant.
  209.     //ParameterRow parameters = queryCreditHistory.getParameterRow();
  210.     ReadWriteRow parameters = queryCreditHistory.getParameterRow();
  211.     parameters.setInt( "HISTORYID", historyId );
  212.  
  213.     queryCreditHistory.open();
  214.     queryCreditHistory.executeQuery();
  215.  
  216.     int rowCount = queryCreditHistory.getRowCount();
  217.     double totalMonthlyPayments = appInfo.rentMortgagePayment;
  218.     double maximumMonthlyPayment = 0f;
  219.     int latePaymentCount = 0;
  220.     int totalPaymentsMade = 0;
  221.  
  222.     // Loop through all accounts and calculate then following aggregates:
  223.     // 1) totalMonthlyPatments
  224.     // 2) Late Payment Count
  225.     // 3) Total Payments Made
  226.     for (int rowLoop = 0; rowLoop < rowCount; rowLoop++) {
  227.  
  228.       queryCreditHistory.goToRow( rowLoop );
  229.       String status = queryCreditHistory.getString( "STATUS" );
  230.  
  231.       // If any of the accounts are in COLLECTIONS, deny credit.
  232.       if ( status.compareTo(ACCOUNT_STATUS_COLLECTIONS) == 0 ) {
  233.         approval.approved = false;
  234.         throw new CreditDeniedException(res.getString("Account_in_Collections"));
  235.       }
  236.  
  237.       // Add the monthly payment to the total ONLY if the account
  238.       // is currently open.
  239.       if ( status.compareTo(ACCOUNT_STATUS_OPEN) == 0 ) {
  240.         totalMonthlyPayments += queryCreditHistory.getDouble( "MONTHLYPAYMENT" );
  241.       }
  242.       // Add to Payment and Late Payment counts
  243.       latePaymentCount += queryCreditHistory.getShort( "PASTDUE30" ) +
  244.                           queryCreditHistory.getShort( "PASTDUE60" ) +
  245.                           queryCreditHistory.getShort( "PASTDUE90" ) +
  246.                           queryCreditHistory.getShort( "PASTDUE120" );
  247.       totalPaymentsMade += queryCreditHistory.getShort( "TOTALPAYMENTS" );
  248.     }
  249.  
  250.     // If more than 10% of all payments were late, deny credit
  251.     if (totalPaymentsMade > 0) {
  252.       if ((float) latePaymentCount / (float) totalPaymentsMade > 0.10f) {
  253.         approval.approved = false;
  254.         throw new CreditDeniedException(res.getString("Too_many_late"));
  255.       }
  256.     }
  257.  
  258.     // Determine maximum amount the applicant can afford per month.
  259.     // Total Payments plus Credit Card payment cannot be more than 40% of total income.
  260.     maximumMonthlyPayment = 0.40f * appInfo.monthlyIncome - totalMonthlyPayments;
  261.  
  262.     approval.limit = maximumMonthlyPayment / 0.05f;
  263.  
  264.     double minCredit = ((Double) res.getObject("Minimum_Limit")).doubleValue();
  265.     // If the limit is less than the resourced Minimum Limit, deny credit
  266.     if (approval.limit < minCredit) {
  267.       approval.approved = false;
  268.       throw new CreditDeniedException(res.getString("You_do_not_qualify"));
  269.     }
  270.  
  271.     // Make sure credit limit is less than the Resourced Maximum Limit
  272.     double maxCredit = ((Double) res.getObject("Maximum_Limit")).doubleValue();
  273.     approval.limit = (approval.limit > maxCredit) ? maxCredit : (int) approval.limit;
  274.     approval.approved = true;
  275.     return approval;
  276.   }
  277.  
  278.   public borland.sql.dataset.QueryDataSet getQueryHistoryIdLookupByName() {
  279.     return queryHistoryIdLookupByName;
  280.   }
  281.  
  282.   public borland.sql.dataset.QueryDataSet getQueryHistoryIdLookupByPid() {
  283.     return queryHistoryIdLookupByPid;
  284.   }
  285.  
  286.  
  287. }
  288.