/** * */ package com.fitbank.installment; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import com.fitbank.common.QuotaBean; import com.fitbank.common.helper.CalculationBase; import com.fitbank.common.logger.FitbankLogger; import com.fitbank.fin.helper.FinancialHelper; import com.fitbank.hb.persistence.acco.loan.Tquotasaccount; import com.fitbank.hb.persistence.gene.Tcurrencyid; import com.fitbank.hb.persistence.soli.Tquotasolicitude; /** * Ajuste de cuota fija con aproximaciones sucesivas. * @author BANTEC Inc. * @version 2.0 */ public class AdjustFixedInstallment { /** * Base de calculo que se aplicado a la cuota. */ private CalculationBase base ; /** * Numero de dias al anio de la base de calculo. */ private BigDecimal basedays; /** * Tasa total del credito incluye comisiones. */ private BigDecimal rate ; /** * Capital original del credito. */ private BigDecimal capital ; /** * Valor de la cuota. */ private BigDecimal quotavalue; /** * Datos generales de una tabla de pagos. */ private InstallmentTable quotaTable; /** * Lista de cuotas no pagadas. */ private List auxquotas; /** * Numero de cuotas deudoras. */ private Integer debtorquotes ; /** * Factor acumulativo utilizado en calculo de cuotas en aproximaciones sucesivas. */ private BigDecimal totalfactor = BigDecimal.ZERO; /** * Datos de tmonedasid, tiene el numero de decimales de la manoneda. */ private Tcurrencyid tcurrencyid; /** * Ajusta el valor de la cuota fija, si el valor de la ultima cuota es mayor a la cuota fija. * @param pQuotaTable * @return * @throws Exception */ public BigDecimal calculate(InstallmentTable pQuotaTable) throws Exception{ quotaTable = pQuotaTable; quotavalue = quotaTable.getFixquota(); capital = quotaTable.getAmount(); rate = quotaTable.getTotalrate().divide(new BigDecimal(100),6,BigDecimal.ROUND_HALF_UP); base = quotaTable.getCalculationBase(); basedays = new BigDecimal(base.getYearBase()); debtorquotes = 0; tcurrencyid = FinancialHelper.getInstance().getTcurrencyid(quotaTable.getCurrency()); this.fillAuxiliarQuota(pQuotaTable.getQuotasBean()); //primera aproximacion sin interes deudor this.firstApproximation(); //Busca cuotas que tengan interes deudor. this.findDebtorquotas(); //segunda aproximacion considera interes deudor. this.secondAproximation(); //aproximacion final this.lastAproximation(); return quotavalue; } /** * Crea una lista de cuotas no pagadas y los factores que sumados me permiten hallar el valor presente de una anualidad que no toma en * consideraci�n per�odos de inter�s deudor.. * @param tquotas Lista de cuotas original. * @throws Exception */ private void fillAuxiliarQuota(List lQuotaBean) throws Exception{ auxquotas = new ArrayList(); int dayaccum = 0; int quotanumber = 1; int grace = quotaTable.getGraceperiod(); for(QuotaBean quota: lQuotaBean){ if(quota.getFpago() == null ){ //&& quota.getCapital().compareTo(new BigDecimal(0)) >= 0){ if(quota instanceof Tquotasolicitude){ if(grace >= ((Tquotasolicitude)quota).getPk().getSubcuenta()){ continue; } }else{ if(grace >= ((Tquotasaccount)quota).getPk().getSubcuenta()){ continue; } } dayaccum += quota.getNumerodiasprovision(); AuxiliarInstallment auxq = new AuxiliarInstallment(quota,dayaccum); auxq.setQuotanumber(quotanumber); auxquotas.add(auxq); quotanumber++; } } } /** * Calculo de la primera aproximacion sin tomar en cuenta el interes deudor. * @throws Exception */ private void firstApproximation()throws Exception{ BigDecimal factor = BigDecimal.ONE; //totalfactor := nvl(factor,0) + totalfactor; for(AuxiliarInstallment quota :auxquotas){ factor = this.getFactor(quota.getQuotaBean().getNumerodiasprovision(),factor); totalfactor = totalfactor.add(factor); } //quotavalue := capital/totalfactor; quotavalue = capital.divide(totalfactor,12,BigDecimal.ROUND_HALF_UP); FitbankLogger.getLogger().debug("Cuota primera aproximacion "+this.quotavalue); } //Segunda aproximacion cuando tiene intereses dudores. private void secondAproximation() throws Exception{ //Factor que me permite hallar el valor presente de una anualidad en el periodo que toma en cuenta periodos con interes deudor. FitbankLogger.getLogger().debug("Segunda aproximacion cuotas deudoras "+this.debtorquotes); BigDecimal factor = BigDecimal.ONE; int auxdebtorquotas = this.debtorquotes + 2; AuxiliarInstallment quota = this.getAuxiliarQuota(auxdebtorquotas); if(quota == null) { return; } factor = this.getFactor(quota.getQuotaBean().getNumerodiasprovision(),BigDecimal.ONE); totalfactor = factor; auxdebtorquotas = this.debtorquotes + 3; for (int i = (auxdebtorquotas - 1); i < auxquotas.size(); i++) { AuxiliarInstallment auxquota = auxquotas.get(i); factor = this.getFactor(auxquota.getQuotaBean().getNumerodiasprovision(),factor); totalfactor = totalfactor.add(factor); } quota = this.getAuxiliarQuota(this.debtorquotes + 1); this.calculateValuequota(quota.getAccumulateddays()); FitbankLogger.getLogger().debug("Cuota segunda aproximacion "+this.quotavalue); } /** * Ultimaaproximaccion para encontrar el valor de una cuota fija. * @throws Exception */ private void lastAproximation() throws Exception{ int j,p = 0; BigDecimal factor = BigDecimal.ONE; while (quotavalue.multiply(totalfactor).subtract(capital).compareTo(BigDecimal.ZERO) > 0) { j = p; debtorquotes = debtorquotes + 2*j + 1; factor = this.getFactor(debtorquotes); totalfactor = BigDecimal.ZERO; for (int i = debtorquotes+2; i < auxquotas.size(); i++) { factor = this.getFactor(auxquotas.get(i).getQuotaBean().getNumerodiasprovision(),factor); totalfactor = totalfactor.add(factor); } AuxiliarInstallment quota = this.getAuxiliarQuota(debtorquotes+1); if(quota == null) { return; } this.calculateValuequota(quota.getQuotaBean().getNumerodiasprovision()); if(quotavalue.multiply(totalfactor).subtract(capital).compareTo(BigDecimal.ZERO) > 0){ debtorquotes = debtorquotes - (2*j + 2); factor = this.getFactor(debtorquotes); totalfactor = BigDecimal.ZERO; for (int i = debtorquotes+2; i < auxquotas.size(); i++) { factor = this.getFactor(auxquotas.get(i).getQuotaBean().getNumerodiasprovision(),factor); totalfactor = totalfactor.add(factor); } AuxiliarInstallment auxquota = this.getAuxiliarQuota(debtorquotes+1); this.calculateValuequota(auxquota.getAccumulateddays()); } p++; }//end while quotavalue = quotavalue.divide(BigDecimal.ONE,this.tcurrencyid.getNumerodecimales(),BigDecimal.ROUND_HALF_UP); FitbankLogger.getLogger().debug("Cuota tercera aproximacion "+this.quotavalue); } /** * Cacula el valor de una cuota con periodos deudores. */ private void calculateValuequota(Integer pQuotadays) throws Exception{ //cuota := (capital*(1+(Vtasa/diasbase*Vnumdias))) / (1+Vcuotasdeudoras+VsumFactor); //cuota = capital *(i)/j BigDecimal i = rate.divide(basedays,12,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(pQuotadays)); i = capital.multiply(i.add(BigDecimal.ONE)) ; BigDecimal j = new BigDecimal(1+this.debtorquotes).add(this.totalfactor); quotavalue = i.divide(j,12,BigDecimal.ROUND_HALF_UP); } /** * Entrega un factor para calcular valores de cuotas en aproximaciones sucesivas. * @param pQuotadays Numero de dias de una cuota. * @param pFactor Factor previo. * @return auxfactor * @throws Exception */ private BigDecimal getFactor(Integer pQuotadays,BigDecimal pFactor) throws Exception{ BigDecimal auxfactor = BigDecimal.ZERO; // factor := (daysyear/(daysyear+pRate*cuotadays))*factor; //(daysyear+pRate*cuotadays) auxfactor = rate.multiply(new BigDecimal(pQuotadays)).add(this.basedays); auxfactor = basedays.divide(auxfactor,12,BigDecimal.ROUND_HALF_UP); auxfactor = auxfactor.multiply(pFactor); return auxfactor; } private BigDecimal getFactor(int pDebtorquotes) throws Exception{ BigDecimal auxfactor = BigDecimal.ZERO; //factor := diasbase/(diasbase+(Vtasa*(Vcuotasdeudoras+2))); auxfactor = basedays.add(rate.multiply(new BigDecimal(pDebtorquotes+2))); auxfactor = basedays.divide(auxfactor,12,BigDecimal.ROUND_HALF_UP); return auxfactor; } /** * Entrega el numero de cuotas que tienen interes deudor. * @param pAuxquotas * @return * @throws Exception */ private void findDebtorquotas() throws Exception{ BigDecimal aux = BigDecimal.ZERO; int j = 1; for (AuxiliarInstallment auxquota : auxquotas) { //if (((Vcapital*Vtasa/diasbase)*Vnumdiasj) - (quotanumber * Vcuota)) < 0 then aux = capital.multiply(rate).divide(new BigDecimal(base.getYearBase()),12,BigDecimal.ROUND_HALF_UP); aux = aux.multiply(new BigDecimal( auxquota.getAccumulateddays())); aux =aux.subtract(quotavalue.multiply(new BigDecimal(j) )); if(aux.compareTo(BigDecimal.ZERO) < 0){ debtorquotes = j -1; break; } j++; } } /** * Entrega una cuota auxiliar dado un numero de cuota. * @param pAuxquotas Lista de cuotas auxiliares no pagadas. * @param pQuotanumber Numero de cuota a buscar. * @return * @throws Exception */ private AuxiliarInstallment getAuxiliarQuota(Integer pQuotanumber) throws Exception{ AuxiliarInstallment auxiliarQuota = null; for(AuxiliarInstallment quota : auxquotas){ if(quota.getQuotanumber().compareTo(pQuotanumber) == 0){ auxiliarQuota = quota; break; } } return auxiliarQuota; } }