package com.fitbank.installment;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import com.fitbank.common.ApplicationDates;
import com.fitbank.common.Helper;
import com.fitbank.common.QuotaBean;
import com.fitbank.common.RequestData;
import com.fitbank.common.exception.FitbankException;
import com.fitbank.common.helper.CalculationBase;
import com.fitbank.common.helper.Constant;
import com.fitbank.common.helper.Dates;
import com.fitbank.fin.helper.FinancialParameters;
import com.fitbank.hb.persistence.gene.Tsystemparametercompany;
import com.fitbank.hb.persistence.gene.TsystemparametercompanyKey;
/**
* Clase que se encarga de generar tabla de amortizacion gradual.
*
*
* - Permte generar tablas de amortizacion con fecha de inicio de pagos
* futuras, considerando intereses deudores.
* - Permite dia fijo de pago.
* - Si se envia el valor de la cuota calcula el numero de cuotas a generar.
* - Si se envia el numeor de cuotas calcula el valor de la cuota fija.
*
*
* @author Soft WareHouse S.A.
*/
public class GradualInstallment extends AbstractQuota {
private BigDecimal capitalinquotas = Constant.BD_ZERO;
/** Interese acumulado de cuotas deudoras. */
private BigDecimal debtorinterest = Constant.BD_ZERO;
/**
* Valor de cuota fija ajustada.
*/
private BigDecimal fixquota = Constant.BD_ZERO;
/**
* Indica si se realizo o no el ajuste de una cuota fija.
*/
private boolean adjustquota = true;
private boolean manualQuota = false;
@Override
public void calculate(InstallmentTable pQuotaTable) throws Exception {
quotaTable = pQuotaTable;
reducedcapital = quotaTable.getAmount();
quotanumber = pQuotaTable.getBegincalculationperiod();
fixquota = quotaTable.getFixquota();
if (!quotaTable.isFixedQuota()) {
// calcula el numero de cuotas, cuando se conoce el plazo de la
// operacion.
super.calculateTotalperiod(pQuotaTable);
}
// Calulate grace quotas.
this.generateGrace();
boolean end = false;
if (!quotaTable.isFixedQuota()) {
this.obtainFixquota(end);
// Ajuste de valor de cuota fija si es una sola cuota no se ajusta
if (adjustquota
&& pQuotaTable.isAdjustquota()
&& (quotanumber - quotaTable.getBegincalculationperiod()) != 0) {
this.adjustQuote();
}
} else {
while (!end) {
super.globalIntrest = Constant.BD_ZERO;
super.calculatePayDate(quotaTable);
if (quotaTable.getAmount().subtract(capitalinquotas)
.compareTo(fixquota) < 0) {
end = true;
}
// calcula intereses, comiones,seguros para la cuota.
super.processByCategory(debtorinterest, false);
this.calculateCuota(end);
quotanumber++;
}
/* Se resta 1 al numero de cuotas */
quotaTable.setTotalperiod(--quotanumber);
}
}
/**
* Calcula tabla de pago dado una cuota fija.
*
* @throws Exception
*/
protected void calculateCuota(boolean pEnd) throws Exception {
BigDecimal capital = Constant.BD_ZERO;
// Si el valor total de intereses y comisiones excede del valor de la
// cuota fija
if (super.globalIntrest.compareTo(this.fixquota) > 0) {
// Se acumula el valor de intereses y comisiones excedente de la
// cuota y se asume como el
// valor de intereses y comisiones de la cuota el valor de la cuota
this.debtorinterest = this.debtorinterest.add(this.globalIntrest
.subtract(this.fixquota));
this.globalIntrest = this.fixquota;
// Si se tiene un valor acumulado de intereses y comisiones
// proveniente de cuotas anteriores
} else if (this.debtorinterest.compareTo(Constant.BD_ZERO) > 0) {
BigDecimal aux = this.fixquota.subtract(this.globalIntrest);
BigDecimal value;
if (this.debtorinterest.compareTo(aux) > 0) {
value = aux;
} else {
value = this.debtorinterest;
}
this.debtorinterest = this.debtorinterest.subtract(value);
this.globalIntrest = this.globalIntrest.add(value);
}
// Distribuye el interes deudor si exitiera.
this.distributeDebtorInterest(pEnd);
// Si se trata de la ultima cuota
if (pEnd) {
// Define el valor de Intereses mas Comisiones de la cuota por si
// hay un residuo en la ultima cuota
// Carga todo el capital reducido al capital de la cuota
capital = this.reducedcapital;
// this.reducedcapital = this.reducedcapital.subtract(this.capital);
} else {
// Obtiene el valor de capital de la cuota
capital = this.fixquota.subtract(this.globalIntrest);
}
// validar limite de dia inicial
if (super.globalIntrest.compareTo(capital) > 0) {
validateInitialDay(getParameter());
}
super.addQuota(this.quotanumber, this.reducedcapital, capital, true);
if (!pEnd) {
reducedcapital = reducedcapital.subtract(capital);
}
capitalinquotas = capitalinquotas.add(capital);
}
/**
* Distribuye el interes, comision, seguros hasta completar el valor de la
* cuota.
*
* @param pEnd
* Indica si es la ultinma cuota de la tabla de pagos.
* @throws Exception
*/
private void distributeDebtorInterest(boolean pEnd) throws Exception {
BigDecimal intrestToDistribute = this.globalIntrest;
BigDecimal totalIntrest;
Iterator> itr = this.mCategoryRates.values()
.iterator();
while (itr.hasNext()) {
for (CategoryRate categoryRate : itr.next()) {
totalIntrest = categoryRate.getIntrest().add(
categoryRate.getDebtorinterest());
// Si el valor total de interes o comision de la categoria
// excede el valor total definido de interes a distribuir y no
// se trata de la ultima cuota
if (totalIntrest.compareTo(intrestToDistribute) > 0 && !pEnd) {
// Se acumula el valor de interes excedente de la categoria
// y se asume como el valor de interes de la categoria el
// valor total definido de interes a distribuir
categoryRate.setDebtorinterest(totalIntrest
.subtract(intrestToDistribute));
categoryRate.setIntrest(intrestToDistribute);
} else {
categoryRate.setIntrest(totalIntrest);
categoryRate.setDebtorinterest(totalIntrest
.subtract(categoryRate.getIntrest()));
}
// Se obtiene el nuevo valor de interes a distribuir
intrestToDistribute = intrestToDistribute.subtract(categoryRate
.getIntrest());
}
}// end while
}
/**
* Genera cuotas de interes por el numero de periodos de gracia definidos
* para la tabla.
*
* @throws Exception
*/
private void generateGrace() throws Exception {
if (quotaTable.getGraceperiod() > 0) {
for (int i = 0; i < quotaTable.getGraceperiod(); i++) {
super.calculatePayDate(quotaTable);
// this.calculateIntrestCommission(quotaTable,reducedcapital);
super.processByCategory(false);
super.addQuota(quotanumber, reducedcapital, Constant.BD_ZERO,
true);
quotanumber++;
}
}
}
/**
* Si el valor de la ultima cuota es diferente del valor de la cuota fija,
* se obtiene un nuevo valor de cuota fija con aproimaciones sucesivas.
*
* @throws Exception
*/
private void adjustQuote() throws Exception {
if (this.getLastquote().compareTo(quotaTable.getFixquota()) != 0) {
BigDecimal quotavalue = null;
if (FinancialParameters.getInstance()
.getBooleanValue("nominalrate")) {
AdjustFixedInstallment adjustFixedQuota = new AdjustFixedInstallment();
quotavalue = adjustFixedQuota.calculate(quotaTable);
// quotavalue =
// quotavalue.add(quotaTable.getOtherChargesQuota());
} else {
AdjustFixedInstallmentCapitalize adjustFixedQuota = new AdjustFixedInstallmentCapitalize();
quotavalue = adjustFixedQuota.calculate(quotaTable);
// quotavalue =
// quotavalue.add(quotaTable.getOtherChargesQuota());
}
quotaTable.setFixquota(quotavalue);
quotaTable.getQuotasBean().clear();
quotaTable.getQuotaCategoryBean().clear();
super.setPreviouspaydate(null);
super.setNextpaydate(null);
super.totaldays = 0;
this.adjustquota = false;
quotanumber = quotaTable.getBegincalculationperiod();
this.calculate(quotaTable);
}
}
/**
* Entrega el valor de la ultima cuota.
*
* @return
* @throws Exception
*/
private BigDecimal getLastquote() throws Exception {
QuotaBean quotaBean = null;
for (QuotaBean quota : quotaTable.getQuotasBean()) {
quotaBean = quota;
}
BigDecimal valuequota = quotaBean.getCapital()
.add(quotaBean.getInteres())
.add(quotaBean.getComision().add(quotaBean.getCargo()));
valuequota = valuequota.add(quotaBean.getAcciones());
return valuequota;
}
/**
* Cambia los parametros de las cuota, para generar una tabla especial.
*
* @throws Exception
*/
private void changeParameters() throws Exception {
Map> mdata = quotaTable.getMspecial();
if (mdata == null) {
return;
}
Map mquota = mdata.get(this.quotanumber);
if (mquota == null) {
return;
}
this.manualQuota = true;
this.fixquota = (BigDecimal) mquota.get("cuota");
super.nextpaydate = (Date) mquota.get("fvencimiento");
super.totaldays = super.totaldays - super.daysperquota;
Dates dates = null;
if (quotaTable.isFixday()
&& !(quotaTable.getPayday() != null && quotaTable.getPayday()
.compareTo(31) == 0)) {
dates = new Dates(this.nextpaydate, CalculationBase.B360360);
} else {
dates = new Dates(this.nextpaydate, quotaTable.getCalculationBase());
}
Dates inicialday = new Dates(this.previouspaydate,
quotaTable.getCalculationBase());
int days = dates.substract(inicialday);
this.setDaysperquota(days);
}
/**
* Entrega el valor de la cuota fija, si no tiene una la calcula.
*
* @throws Exception
*/
private void calculateFixquota() throws Exception {
FixedInstallment fixCuota = new FixedInstallment(
quotaTable.getCalculationBase());
Integer quotas = quotaTable.getTotalperiod() - quotanumber
+ Constant.BD_ONE_INTEGER;
this.fixquota = fixCuota
.calculate(this.reducedcapital, quotaTable.getFinalrate(),
quotaTable.getTotalFixedValue(),
quotaTable.getDaysperperiod(), quotas,
quotaTable.getCurrency());
}
/**
* Recupera valor de cuota Fija
*
* @param end
* @return
* @throws Exception
*/
private boolean obtainFixquota(boolean end) throws Exception {
int size = quotaTable.getTotalperiod() - quotaTable.getGraceperiod();
size = size + quotaTable.getBegincalculationperiod() - 1;
for (int i = quotaTable.getBegincalculationperiod(); i <= size; i++) {
// BigDecimal quotavalue = this.fixquota;
super.globalIntrest = Constant.BD_ZERO;
super.calculatePayDate(quotaTable);
if (manualQuota) {
manualQuota = false;
this.calculateFixquota();
}
// cambia condiciones de cuotas especiales.
this.changeParameters();
if (i == size) {
end = true;
}
// calcula intereses, comiones,seguros para la cuota.
super.processByCategory(debtorinterest, false);
this.calculateCuota(end);
// this.fixquota = quotavalue;
if (super.stop) {
break;
}
quotanumber++;
}
return end;
}
/**
* Metodo para validar el limite de dias inicial
*
* @param diasEx
*/
private void validateInitialDay(boolean daysExceed) {
if (daysExceed) {
throw new FitbankException("COL047",
"DIAS DE PAGO INICIAL EXCEDE EL LIMITE PERMITIDO");
}
}
/**
* Recuperar Parametro del Sistema para controlar el limite de dia de pago
* inicial
*
* @param diasEx
* @return
*/
private boolean getParameter() {
TsystemparametercompanyKey key = new TsystemparametercompanyKey(
RequestData.getDetail().getCompany(), "DIAPAGOEXCEDE",
ApplicationDates.DEFAULT_EXPIRY_TIMESTAMP);
Tsystemparametercompany tsystemparametercompany = (Tsystemparametercompany) Helper
.getSession().get(Tsystemparametercompany.class, key);
return tsystemparametercompany != null
&& tsystemparametercompany.getValortexto() != null
&& tsystemparametercompany.getValortexto().compareTo("1") == 0;
}
}