package com.fitbank.installment; import java.math.BigDecimal; import java.sql.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import com.fitbank.common.CategoryGroupTypes; import com.fitbank.common.QuotaBean; import com.fitbank.common.QuotaCategoryBean; import com.fitbank.common.helper.CalculationBase; import com.fitbank.common.helper.Constant; import com.fitbank.common.helper.Dates; import com.fitbank.common.properties.PropertiesHandler; import com.fitbank.fin.helper.FinancialHelper; import com.fitbank.fin.helper.Transaction; import com.fitbank.fin.tariff.Rate; import com.fitbank.hb.persistence.gene.Tcurrencyid; import com.fitbank.hb.persistence.gene.Tsubsystemcategorygroup; import com.fitbank.hb.persistence.trans.Titemdefinition; import com.fitbank.helper.HoldChecks; import com.fitbank.helper.InstallmentDates; /** * Clase que contiene rutinas generales para el calculo de una tabla de pagos. * * @author Soft Warehouse S.A. */ public abstract class AbstractQuota extends InstallmentDates { /** Interes global de una cuota incluye intereses y comisiones. */ protected BigDecimal globalIntrest = BigDecimal.ZERO; /** Numero de cuota que se genera */ protected int quotanumber = 0; /** * Map de categorias de interes, comision, seguros a calcular. El key es la * categoria de interes, comision, seguro a calcular. El objeto CategoryRate * tiene la tasa asociada a la categoria para calcular el valor del interes, * comion, impuesto, seguro ..etc ... */ protected Map> mCategoryRates; /** * Referencia al objeto que almacena datos utiles para la generacion de una * tabla de pagos. */ protected InstallmentTable quotaTable; /** Capital reducido de una cuota. */ protected BigDecimal reducedcapital = Constant.BD_ZERO; /** Codigo de categoria de capital. */ protected String capitalcategory; /** Codigo de grupo de balance aosociado a la categoria de capital */ protected String capitalbalancegroup; /** Numero de dias de la frecuencia de interes. */ protected Integer interestFrquencyDays; /** Indica si la tabla se genera con intereses anticipado. */ protected boolean isAdvancedInterest = false; /** * Indica si se calcula intyeres remanete por redondedo de decimales de una * cuota, el remanente se calcula con 6 decimales. */ protected boolean calcularemanete = false; /** Interes remanente de la cuota anterior. */ private BigDecimal interesremanente = Constant.BD_ZERO; /** * Metodo a travez del cual se invoca a la clase especifca para calculo de * tabla. * * @param pQuotaTable * Datos generles de la tabla de pagos. * @throws Exception */ public abstract void calculate(InstallmentTable pQuotaTable) throws Exception; /** * Metodo que obtiene una lista de tasas, impuestos, comiiones, seguros a * calcular en la cuota las categorias a calcular se obtienen: en la * solicitud de tsolicitudcategoriatasas, cuando se tiene la cuenta se * obtiene de tcuentacategoriatasas. * * @throws Exception */ protected void processByCategory(boolean advancedInterest) throws Exception { this.processByCategory(null, advancedInterest); } /** * Metodo que se encarga del calculo de valores que se provisionan, por * categoria de capital, ejemplo capital propio y capital externo. * * @param pDebtorInterest * Interes deudor acumualdo de las cuotas anteriores. * @throws Exception */ protected void processByCategory(BigDecimal pDebtorInterest, boolean advancedInterest) throws Exception { Map>> mRates = this.quotaTable .getMRates(); // Obtiene las categorias de capital ejemplo Capital propio y capital // externo. Iterator itr = mRates.keySet().iterator(); while (itr.hasNext()) { String key = itr.next(); StringTokenizer stk = new StringTokenizer(key, "^"); this.capitalcategory = (String) stk.nextElement(); this.capitalbalancegroup = (String) stk.nextElement(); Map> mCategoryRates = mRates.get(key); BigDecimal amount = this.reducedcapital; if (!PropertiesHandler.getConfig("financial").getBoolean( "nominalrate") && pDebtorInterest != null) { amount = amount.add(pDebtorInterest); } BigDecimal interest = this.calculateIntrestCommission( mCategoryRates, amount, advancedInterest); this.globalIntrest = this.globalIntrest.add(interest); } } /** * Calcula inteses y comisiones. * * @throws Exception */ protected BigDecimal calculateIntrestCommission( Map> p_mCategoryRates, BigDecimal pCapital, boolean advancedInterest) throws Exception { this.mCategoryRates = p_mCategoryRates; BigDecimal categoryIntrest; BigDecimal accumulatedIntrest = BigDecimal.ZERO; Iterator itr_key = this.mCategoryRates.keySet().iterator(); while (itr_key.hasNext()) { String key = itr_key.next(); List categoryRateList = this.mCategoryRates.get(key); categoryIntrest = BigDecimal.ZERO; for (CategoryRate categoryRate : categoryRateList) { if (categoryRate.getRate() != null && categoryRate.getRate().compareTo(BigDecimal.ZERO) > 0) { categoryIntrest = this.calculateValueRate(categoryRate, pCapital, categoryRate.getRate(), this.getDaysperquota(), advancedInterest); } categoryRate.setIntrest(categoryIntrest); accumulatedIntrest = accumulatedIntrest.add(categoryRate .getIntrest()); } } return accumulatedIntrest; } /** * Calcula intereses, comisiones, seguros de una cuota, cuyo calculo sea * similar al calculo de interes de una cuota. * * @param pAmount * Capital reducido. * @param pRate * Tasa a aplicar. * @param pDays * Numero de dias de la cuota. * @param advancedInterest * Indica si se calcula el valor de la cuota con interes * anticipafo.. * @return BigDecimal * @throws Exception */ protected BigDecimal calculateValueRate(CategoryRate pCategoryRate, BigDecimal pAmount, BigDecimal pRate, int pDays, boolean advancedInterest) throws Exception { BigDecimal interest = BigDecimal.ZERO; Rate rate = new Rate(this.quotaTable.getCalculationBase()); List> ldata = null; if (quotaTable.getAccountnumber() != null && quotaTable.subsystem.compareTo("05") == 0) { HoldChecks h = new HoldChecks(); ldata = h.getChecksToConfirm(quotaTable.getAccountnumber(), quotaTable.getCompany(), this.previouspaydate); } if (ldata == null || ldata.isEmpty()) { // manejo de saldos en cheques por confirmar se resta del capital // original. if (!advancedInterest) { interest = rate.calculateDividendinterest(pAmount, pRate, pDays, this.quotaTable.getCurrency()); } else { interest = rate.calculateDividendinterestIA(pAmount, pRate, this.interestFrquencyDays, pDays, this.quotaTable.getCurrency()); } } else { interest = this.calculeWithHold(rate, pAmount, pRate, pDays, ldata); } // Calculo del interes remanente. if (this.calcularemanete) { BigDecimal dif = rate.getInterescuota().subtract(interest); interesremanente = interesremanente.add(dif); Tcurrencyid tcurrencyid = FinancialHelper.getInstance() .getTcurrencyid(quotaTable.getCurrency()); BigDecimal remanente = interesremanente.divide(BigDecimal.ONE, tcurrencyid.getNumerodecimales(), BigDecimal.ROUND_HALF_UP); if (remanente.abs().compareTo(Constant.BD_ONE_HUNDREDTH) >= 0) { interesremanente = interesremanente.subtract(remanente); pCategoryRate.setInteresremanente(remanente); } } return interest; } /** * Metodo que calcula el interes considerando valores depositados en cheque. * * @param pRate * Referencia al objeto Rate. * @param pAmount * Capital reducido. * @param rate * tasa con la que se calcula los intereses. * @param pDays * Numero de dias de la cuota. * @param pLdata * Lista de valores en cheque con fecha de liberacion futura. * @return BigDecimal. * @throws Exception */ private BigDecimal calculeWithHold(Rate pRate, BigDecimal pAmount, BigDecimal rate, int pDays, List> pLdata) throws Exception { BigDecimal interest = BigDecimal.ZERO; int originaldays = pDays; Date inidate = this.previouspaydate; for (Map m : pLdata) { Date d = (Date) m.get("date"); int days = 1; if (originaldays != 1) { days = this.getHoldDays(d, inidate); } if (days > originaldays) { days = originaldays; } originaldays = originaldays - days; if (originaldays < 0) { originaldays = 0; } BigDecimal hold = this.getHoldAmount(pLdata, this.previouspaydate); BigDecimal prov = pRate.calculateDividendinterest( pAmount.subtract(hold), rate, days, this.quotaTable.getCurrency()); interest = interest.add(prov); } if (originaldays > 0) { BigDecimal prov = pRate.calculateDividendinterest(pAmount, rate, originaldays, this.quotaTable.getCurrency()); interest = interest.add(prov); } return interest; } /** * Entrega el valor de retenciones, cuaya fecha de liberacion es mayor o * igual a una fecha dada. * * @param pLdata * Monto de cheques con fecha de liberacion futura. * @param pDate * Fecha a obtener valores uturos. * @return {@link BigDecimal} * @throws Exception */ private BigDecimal getHoldAmount(List> pLdata, Date pDate) throws Exception { BigDecimal hold = BigDecimal.ZERO; for (Map m : pLdata) { Date d = (Date) m.get("date"); BigDecimal value = (BigDecimal) m.get("value"); if (d.compareTo(pDate) > 0) { hold = hold.add(value); } } return hold; } /** * Metodo que entrega el numero de dias entre la fecha de liberacion de * cheques y la fecha de inicio de la cuota. * * @param pHolddate * Fecha de liberacion de cheques. * @return int * @throws Exception */ private int getHoldDays(Date pHolddate, Date pIniDate) throws Exception { int days = 0; Dates to = new Dates(pHolddate, CalculationBase.B365365); Dates from = new Dates(pIniDate); days = to.substract(from); return days; } /** * Metodo que crea una cuota de un dia utilizada en la generacion de tablas * de interes anticipado, adicionalmente obtiene la categoria y grupo de * balance de capital. * * @param pQuotaTable * Datos generles de la tabla de pagos. * @throws Exception */ protected void addOnePayMetDay(InstallmentTable pQuotaTable) throws Exception { this.setNextpaydate(pQuotaTable.getAccountingdate()); this.setPreviouspaydate(pQuotaTable.getAccountingdate()); this.daysperquota = 1; // Obtiene la categoria de capial Map>> mRates = this.quotaTable .getMRates(); Iterator itr = mRates.keySet().iterator(); while (itr.hasNext()) { String key = itr.next(); StringTokenizer stk = new StringTokenizer(key, "^"); this.capitalcategory = (String) stk.nextElement(); this.capitalbalancegroup = (String) stk.nextElement(); } } /** * Encera la fecha de primer pago en cuotas con interes anticipado, con el * fin de respetar las condiciones de pago de la primera cuota, como fecha * de inicio de pagos. * * @param pQuotaTable * Datos generles de la tabla de pagos. * @throws Exception */ protected void removeOnePayMetDay(InstallmentTable pQuotaTable) throws Exception { this.setNextpaydate(null); this.setPreviouspaydate(null); this.daysperquota = 0; } /** * Metodo que recalcula el numero de cuotas, cuando se conoce el plazo de * una operacion. * * @param pQuotaTable * @throws Exception */ protected void calculateTotalperiod(InstallmentTable pQuotaTable) throws Exception { int size = quotaTable.getTotalperiod(); size = size + quotaTable.getBegincalculationperiod() - 1; stop = false; for (int i = quotaTable.getBegincalculationperiod(); i <= size; i++) { this.calculatePayDate(quotaTable); if (stop) { break; } quotanumber++; }// end for // Ajuste de valor de cuota fija // pQuotaTable.setTotalperiod(quotanumber); this.totaldays = 0; this.daysperquota = 0; this.previouspaydate = null; this.nextpaydate = null; this.stop = false; // encera nuevamente la cuota inicio. quotanumber = pQuotaTable.getBegincalculationperiod(); /* * if(pQuotaTable.getGraceperiod() > 0){ quotanumber = quotanumber + * pQuotaTable.getGraceperiod(); } */ } /** * Adiciona una cuota al calendario de pagos. * * @param pQuotaTable * Datos de tabla de pagos. * @param pQuotanumber * Numero de cuota. * @param pReducedcapita * Capital reducido. * @param pCapital * Capital de la cuota. * @param pIntrest * Interes de la cuota. * @param pCommision * Comision de la cuota. * @throws Exception */ public void addQuota(Integer pQuotanumber, BigDecimal pReducedcapita, BigDecimal pCapital, boolean isAddChrarges) throws Exception { // Genera la cuota de la solicitud QuotaBean quotabean = this.quotaTable.getAccountOrSolicitudeTypes() .getQuotaClass(this.quotaTable, pQuotanumber); quotabean.setCmoneda(this.quotaTable.getCurrency()); quotabean.setFvencimiento(this.getNextpaydate()); Dates previouspaydate = new Dates(this.previouspaydate, CalculationBase.B365365); Dates fvencimiento = new Dates(quotabean.getFvencimiento(), CalculationBase.B365365); quotabean.setNumerodiascalendario(fvencimiento .substract(previouspaydate)); quotabean.setNumerodiasprovision(this.getDaysperquota()); quotabean.setCapital(pCapital); quotabean.setCapitalreducido(pReducedcapita); this.addQuotaCategoryBean(quotabean, pQuotanumber, pReducedcapita, pCapital, false, isAddChrarges); this.quotaTable.addQuotaBean(quotabean); } /** * Adiciona registros por categoria a la tabla de pagos. si es Plazo * isAdvancedInterest=false se registra todas las cuotas * * @param pQuotaBean * Datos de la cabecera de la tabla de pagos. * @param pQuotanumber * Numero de cuota a generar. * @param pReducedcapita * Capital reducido de la operacion. * @param pCapital * Valor de capital de la cuota. * @throws Exception */ private void addQuotaCategoryBean(QuotaBean pQuotaBean, Integer pQuotanumber, BigDecimal pReducedcapita, BigDecimal pCapital, boolean isUpdate, boolean isAddChrarges) throws Exception { // Genera la categoria de Capital de la cuota de la solicitud QuotaCategoryBean quotaCategoryBean = this.quotaTable .getAccountOrSolicitudeTypes().getQuotaCategoryClass( this.quotaTable, pQuotanumber, this.capitalcategory, this.capitalbalancegroup); quotaCategoryBean.setValorcategoria(pCapital); quotaCategoryBean.setValordeudorcategoria(pReducedcapita); if (this.capitalcategory != null && pCapital.compareTo(Constant.BD_ZERO) > 0 && this.isAdvancedInterest) { this.quotaTable.addQuotaCategoryBean(quotaCategoryBean); } if (this.capitalcategory != null && !this.isAdvancedInterest) { this.quotaTable.addQuotaCategoryBean(quotaCategoryBean); } // Intereses this.addCalculesRates(pQuotaBean, pQuotanumber); // cargos fijos if (!isUpdate && isAddChrarges) { this.addCharges(pQuotaBean, pQuotanumber); } } /** * Metodo que adiciona categorias calculadas aplicando una tasa a la cuota. * * @param pQuotaBean * Cabecera de una tabla de amortizacion. * @param pQuotaCategoryBean * Detalle por categoria de la tabla de amortizacion. * @param pQuotanumber * Numero de cuota. * @throws Exception */ private void addCalculesRates(QuotaBean pQuotaBean, Integer pQuotanumber) throws Exception { if (this.mCategoryRates == null) { return; } // Genera las categorias de Intereses y Comisiones de la cuota de la // solicitud Iterator> itr_CategoryRate = this.mCategoryRates .values().iterator(); while (itr_CategoryRate.hasNext()) { for (CategoryRate categoryRates : itr_CategoryRate.next()) { QuotaCategoryBean pQuotaCategoryBean = this.quotaTable .getAccountOrSolicitudeTypes().getQuotaCategoryClass( this.quotaTable, pQuotanumber, categoryRates.getCategory(), categoryRates.getCbalanceGroup()); if (this.calcularemanete) { pQuotaCategoryBean.setValorcategoria(categoryRates .getIntrest().add( categoryRates.getInteresremanente())); } else { pQuotaCategoryBean.setValorcategoria(categoryRates .getIntrest()); } /* * Modificacion para copiar en valordeudorcategoria el mismo * valor del cargo para las categorias que son seguros */ if (categoryRates.getRateType().compareTo( CategoryGroupTypes.INSURANCE.name()) == 0) { pQuotaCategoryBean.setValordeudorcategoria(categoryRates .getIntrest()); } else { pQuotaCategoryBean.setValordeudorcategoria(categoryRates .getDebtorinterest()); } this.quotaTable.addQuotaCategoryBean(pQuotaCategoryBean); this.updateTquotasolicitude(pQuotaBean, categoryRates.getIntrest(), categoryRates.getRateType()); } } } /** * Metodo que adiciona cargos fijos a las cuotas.
* * @param pQuotaBean * Cabecera de una tabla de amortizacion. * @param pQuotaCategoryBean * Detalle por categoria de la tabla de amortizacion. * @param pQuotanumber * Numero de cuota. * @throws Exception */ private void addCharges(QuotaBean pQuotaBean, Integer pQuotanumber) throws Exception { Integer quotanumber = pQuotanumber; if (this.isAdvancedInterest) { ++quotanumber; } for (ChargeValues chargeAccount : this.quotaTable.getOtherCharges()) { this.addChargesByRegister(chargeAccount, pQuotanumber, pQuotaBean); } } private void addChargesByRegister(ChargeValues chargeAccount, Integer pQuotanumber, QuotaBean pQuotaBean) throws Exception { BigDecimal value = Constant.BD_ZERO; if (chargeAccount.getInCuota().compareTo("1") == 0 || chargeAccount.isDistribuyeencuotas()) { if (chargeAccount.isDistribuyeencuotas()) { // El valor que llega en el cargo se distribuye en las // cuotas del prestamo. value = this.getValue(chargeAccount); // chargeAccount.setValue(value); // para que solo haga una vez el calculo. // chargeAccount.setDistribuyeencuotas(false); } if (isContinue(chargeAccount, pQuotanumber)) { return; } // CategoryRate categoryRates = itr_CategoryRate.next(); Transaction transaccion = new Transaction( chargeAccount.getSubsistema(), chargeAccount.getTranasccion(), chargeAccount.getVersionTranasccion()); Titemdefinition item = transaccion.getTitemdefinition(chargeAccount .getRubro()); this.addQuotaCategoryBeanByCharges(pQuotanumber, value, chargeAccount, item); Tsubsystemcategorygroup tsubsystemcategorygroup = FinancialHelper .getInstance().getTsubsystemcategorygroup( item.getCategoria(), item.getCgrupobalance(), chargeAccount.getSubsistema(), chargeAccount.getCompania()); this.updateTquotasolicitude( pQuotaBean, value.compareTo(Constant.BD_ZERO) == 0 ? chargeAccount .getValue() : value, tsubsystemcategorygroup .getGrupocategoria()); } } private void addQuotaCategoryBeanByCharges(Integer pQuotanumber, BigDecimal value, ChargeValues chargeAccount, Titemdefinition item) throws Exception { QuotaCategoryBean pQuotaCategoryBean = this.quotaTable .getAccountOrSolicitudeTypes().getQuotaCategoryClass( this.quotaTable, pQuotanumber, item.getCategoria(), item.getCgrupobalance()); pQuotaCategoryBean .setValorcategoria(value.compareTo(Constant.BD_ZERO) == 0 ? chargeAccount.getValue() : value); pQuotaCategoryBean.setValordeudorcategoria(value .compareTo(Constant.BD_ZERO) == 0 ? chargeAccount.getValue() : value); this.quotaTable.addQuotaCategoryBean(pQuotaCategoryBean); } private BigDecimal getValue(ChargeValues chargeAccount) { BigDecimal value = Constant.BD_ZERO; int size = quotaTable.getTotalperiod() - quotaTable.getGraceperiod(); if (chargeAccount.getBeginQuota() != null && chargeAccount.getEndQuota() != null) { size = chargeAccount.getEndQuota() - chargeAccount.getBeginQuota() + 1; } value = chargeAccount.getValue().divide(new BigDecimal(size), 2, BigDecimal.ROUND_HALF_UP); if (size == this.quotanumber) { BigDecimal amount = value.multiply(new BigDecimal(size - 1)); value = chargeAccount.getValue().subtract(amount); } return value; } /** * Metodo que valida si se adiciona o no un cargo a la tabla, cuando esta * tiene una cuota de inicio y de finalizacion. * * @param pChargeValues * Registro de cargos a asociar a la tabla de amortizacion. * @param pQuotanumber * Numero de cuota. * @return boolean. * @throws Exception */ private boolean isContinue(ChargeValues pChargeValues, Integer pQuotanumber) throws Exception { if (pChargeValues.getBeginQuota() != null && pQuotanumber < pChargeValues.getBeginQuota()) { return true; } if (pChargeValues.getEndQuota() != null && pQuotanumber > pChargeValues.getEndQuota()) { return true; } return pChargeValues.getValue().compareTo(Constant.BD_ZERO) <= 0; } /** * Metodo que actualiza valores en la cuotas cuando se genera tablas con * interes anticipado. * * @param pQuotaNumber * Numero de cuota. * @param pReducedcapita * Capital reducido. * @param pCapital * Capital de la cuota. * @throws Exception */ public void updateQuota(Integer pQuotaNumber, BigDecimal pReducedcapita, BigDecimal pCapital) throws Exception { QuotaBean quotaBean = this.quotaTable.getQuotaBean(pQuotaNumber); this.daysperquota = quotaBean.getNumerodiasprovision(); quotaBean.setCapitalreducido(pReducedcapita); quotaBean.setCapital(pCapital); this.addQuotaCategoryBean(quotaBean, pQuotaNumber, pReducedcapita, pCapital, true, false); } /** * Completa valores de interes, comision, seguro, cargos en la tabla * tsolicitudcuotas. * * @param pTquotasolicitude * @param pCategoryRate */ private void updateTquotasolicitude(QuotaBean pQuotaBean, BigDecimal pValue, String pGroupType) { CategoryGroupTypes categoryGroupTypes = CategoryGroupTypes .valueOf(pGroupType); BigDecimal value = pValue; // pCategoryRate.getIntrest(); switch (categoryGroupTypes) { case INTEREST : BigDecimal interest = pQuotaBean.getInteres(); pQuotaBean.setInteres(interest.add(value)); break; case CHARGE : BigDecimal charge = pQuotaBean.getCargo(); pQuotaBean.setCargo(charge.add(value)); break; case INSURANCE : BigDecimal insurance = pQuotaBean.getSeguro(); pQuotaBean.setSeguro(insurance.add(value)); break; case COMMISSION : BigDecimal comission = pQuotaBean.getComision(); pQuotaBean.setComision(comission.add(value)); break; case SHARES : BigDecimal shares = pQuotaBean.getAcciones(); pQuotaBean.setAcciones(shares.add(value)); break; default : break; } } }