fix for MIFOSX-884

This commit is contained in:
Vishwas Babu A J
2014-03-01 02:37:12 -08:00
parent 85193881b5
commit fb6c5440dc
2 changed files with 245 additions and 232 deletions
@@ -295,7 +295,6 @@ public class Loan extends AbstractPersistable<Long> {
@Column(name = "max_outstanding_loan_balance", scale = 6, precision = 19, nullable = false) @Column(name = "max_outstanding_loan_balance", scale = 6, precision = 19, nullable = false)
private BigDecimal maxOutstandingLoanBalance; private BigDecimal maxOutstandingLoanBalance;
@LazyCollection(LazyCollectionOption.FALSE) @LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private Set<LoanDisbursementDetails> disbursementDetails = new HashSet<LoanDisbursementDetails>(); private Set<LoanDisbursementDetails> disbursementDetails = new HashSet<LoanDisbursementDetails>();
@@ -305,7 +304,6 @@ public class Loan extends AbstractPersistable<Long> {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private final Set<LoanTermVariations> loanTermVariations = new HashSet<LoanTermVariations>(); private final Set<LoanTermVariations> loanTermVariations = new HashSet<LoanTermVariations>();
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType, public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
@@ -317,7 +315,7 @@ public class Loan extends AbstractPersistable<Long> {
final Boolean syncDisbursementWithMeeting = null; final Boolean syncDisbursementWithMeeting = null;
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct, return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount, loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
disbursementDetails,maxOutstandingLoanBalance); disbursementDetails, maxOutstandingLoanBalance);
} }
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType, public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
@@ -325,14 +323,14 @@ public class Loan extends AbstractPersistable<Long> {
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
final Set<LoanDisbursementDetails> disbursementDetails,final BigDecimal maxOutstandingLoanBalance) { final Set<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance) {
final LoanStatus status = null; final LoanStatus status = null;
final CodeValue loanPurpose = null; final CodeValue loanPurpose = null;
final Set<LoanCollateral> collateral = null; final Set<LoanCollateral> collateral = null;
final Client client = null; final Client client = null;
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct, return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount, loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
disbursementDetails,maxOutstandingLoanBalance); disbursementDetails, maxOutstandingLoanBalance);
} }
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group, public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
@@ -340,13 +338,13 @@ public class Loan extends AbstractPersistable<Long> {
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
final Set<LoanDisbursementDetails> disbursementDetails,final BigDecimal maxOutstandingLoanBalance) { final Set<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance) {
final LoanStatus status = null; final LoanStatus status = null;
final CodeValue loanPurpose = null; final CodeValue loanPurpose = null;
final Set<LoanCollateral> collateral = null; final Set<LoanCollateral> collateral = null;
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct, return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount, loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
disbursementDetails,maxOutstandingLoanBalance); disbursementDetails, maxOutstandingLoanBalance);
} }
protected Loan() { protected Loan() {
@@ -473,8 +471,6 @@ public class Loan extends AbstractPersistable<Long> {
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
loanCharge.update(this); loanCharge.update(this);
final BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); final BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
@@ -907,12 +903,15 @@ public class Loan extends AbstractPersistable<Long> {
public void updateLoanCharges(final Set<LoanCharge> loanCharges) { public void updateLoanCharges(final Set<LoanCharge> loanCharges) {
List<Long> existingCharges = fetchAllLoanChargeIds(); List<Long> existingCharges = fetchAllLoanChargeIds();
/** Process new and updated charges **/
for (final LoanCharge loanCharge : loanCharges) { for (final LoanCharge loanCharge : loanCharges) {
LoanCharge charge = loanCharge; LoanCharge charge = loanCharge;
if(loanCharge.getId() == null){ // add new charges
if (loanCharge.getId() == null) {
loanCharge.update(this); loanCharge.update(this);
this.charges.add(loanCharge); this.charges.add(loanCharge);
}else{ } else {
charge = fetchLoanChargesById(charge.getId()); charge = fetchLoanChargesById(charge.getId());
existingCharges.remove(charge.getId()); existingCharges.remove(charge.getId());
} }
@@ -933,7 +932,9 @@ public class Loan extends AbstractPersistable<Long> {
charge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(), charge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
totalChargeAmt); totalChargeAmt);
} }
for(Long id:existingCharges){
/** Updated deleted charges **/
for (Long id : existingCharges) {
fetchLoanChargesById(id).setActive(false); fetchLoanChargesById(id).setActive(false);
} }
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
@@ -1245,17 +1246,19 @@ public class Loan extends AbstractPersistable<Long> {
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount)) { if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount)) {
this.fixedEmiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName); this.fixedEmiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
} }
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName, this.maxOutstandingLoanBalance)) { if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
this.maxOutstandingLoanBalance = command.bigDecimalValueOfParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName); this.maxOutstandingLoanBalance)) {
this.maxOutstandingLoanBalance = command
.bigDecimalValueOfParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName);
} }
if (this.disbursementDetails.isEmpty()) { if (this.disbursementDetails.isEmpty()) {
final String errorMessage = "For this loan product, disbursement details must be provided"; final String errorMessage = "For this loan product, disbursement details must be provided";
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
} }
if(disbursementDetails.size() > loanProduct.maxTrancheCount()){ if (disbursementDetails.size() > loanProduct.maxTrancheCount()) {
final String errorMessage = "Number of tranche shouldn't be greter than "+loanProduct.maxTrancheCount(); final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage, throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
loanProduct.maxTrancheCount(),disbursementDetails.size()); loanProduct.maxTrancheCount(), disbursementDetails.size());
} }
} else { } else {
this.disbursementDetails.clear(); this.disbursementDetails.clear();
@@ -1265,7 +1268,6 @@ public class Loan extends AbstractPersistable<Long> {
return actualChanges; return actualChanges;
} }
private void updateDisbursementDetails(final JsonCommand command, final Map<String, Object> actualChanges) { private void updateDisbursementDetails(final JsonCommand command, final Map<String, Object> actualChanges) {
List<Long> list = fetchDisbursementIds(); List<Long> list = fetchDisbursementIds();
@@ -1415,7 +1417,6 @@ public class Loan extends AbstractPersistable<Long> {
updateLoanSchedule(loanSchedule); updateLoanSchedule(loanSchedule);
LoanStatus from = null; LoanStatus from = null;
if (this.loanStatus != null) { if (this.loanStatus != null) {
from = LoanStatus.fromInt(this.loanStatus); from = LoanStatus.fromInt(this.loanStatus);
@@ -1685,9 +1686,9 @@ public class Loan extends AbstractPersistable<Long> {
return ids; return ids;
} }
public void disburse(final LoanScheduleGeneratorFactory loanScheduleFactory, final AppUser currentUser, public void disburse(final LoanScheduleGeneratorFactory loanScheduleFactory, final AppUser currentUser, final JsonCommand command,
final JsonCommand command, final ApplicationCurrency currency, final List<Long> existingTransactionIds, final ApplicationCurrency currency, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final List<Long> existingReversedTransactionIds, final Map<String, Object> actualChanges, final PaymentDetail paymentDetail, final Map<String, Object> actualChanges, final PaymentDetail paymentDetail,
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
final WorkingDays workingDays, final boolean allowTransactionsOnHoliday, final boolean allowTransactionsOnNonWorkingDay) { final WorkingDays workingDays, final boolean allowTransactionsOnHoliday, final boolean allowTransactionsOnNonWorkingDay) {
@@ -1698,15 +1699,16 @@ public class Loan extends AbstractPersistable<Long> {
LoanStatus.fromInt(this.loanStatus)); LoanStatus.fromInt(this.loanStatus));
boolean isMultiTrancheDisburse = false; boolean isMultiTrancheDisburse = false;
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
if(LoanStatus.fromInt(this.loanStatus).isActive() && isAllTranchesNotDisbursed()){ if (LoanStatus.fromInt(this.loanStatus).isActive() && isAllTranchesNotDisbursed()) {
LoanDisbursementDetails details = fetchLastDisburseDetail(); LoanDisbursementDetails details = fetchLastDisburseDetail();
if(details != null){ if (details != null) {
lastDusburseDate = details.actualDisbursementDate(); lastDusburseDate = details.actualDisbursementDate();
} }
if(actualDisbursementDate.toDate().before(lastDusburseDate)){ if (actualDisbursementDate.toDate().before(lastDusburseDate)) {
final String errorMsg = "Loan can't be disbursed before "+lastDusburseDate; final String errorMsg = "Loan can't be disbursed before " + lastDusburseDate;
throw new LoanDisbursalException(errorMsg, "actualdisbursementdate.before.lastdusbursedate", lastDusburseDate,actualDisbursementDate.toDate()); throw new LoanDisbursalException(errorMsg, "actualdisbursementdate.before.lastdusbursedate", lastDusburseDate,
actualDisbursementDate.toDate());
} }
isMultiTrancheDisburse = true; isMultiTrancheDisburse = true;
} }
@@ -1718,38 +1720,37 @@ public class Loan extends AbstractPersistable<Long> {
this.loanStatus = statusEnum.getValue(); this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
Money disburseAmount = this.loanRepaymentScheduleDetail.getPrincipal().zero(); Money disburseAmount = this.loanRepaymentScheduleDetail.getPrincipal().zero();
BigDecimal principalDisbursed = command.bigDecimalValueOfParameterNamed(LoanApiConstants.principalDisbursedParameterName); BigDecimal principalDisbursed = command.bigDecimalValueOfParameterNamed(LoanApiConstants.principalDisbursedParameterName);
if(this.actualDisbursementDate == null){ if (this.actualDisbursementDate == null) {
this.actualDisbursementDate = actualDisbursementDate.toDate(); this.actualDisbursementDate = actualDisbursementDate.toDate();
} }
BigDecimal diff = BigDecimal.ZERO; BigDecimal diff = BigDecimal.ZERO;
Collection<LoanDisbursementDetails> details = fetchUndisbursedDetail(); Collection<LoanDisbursementDetails> details = fetchUndisbursedDetail();
if(principalDisbursed == null){ if (principalDisbursed == null) {
disburseAmount = this.loanRepaymentScheduleDetail.getPrincipal(); disburseAmount = this.loanRepaymentScheduleDetail.getPrincipal();
if(!details.isEmpty()){ if (!details.isEmpty()) {
disburseAmount = disburseAmount.zero(); disburseAmount = disburseAmount.zero();
for(LoanDisbursementDetails disbursementDetails:details){ for (LoanDisbursementDetails disbursementDetails : details) {
disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate()); disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate());
disburseAmount = disburseAmount.plus(disbursementDetails.principal()); disburseAmount = disburseAmount.plus(disbursementDetails.principal());
} }
} }
}else{ } else {
disburseAmount = disburseAmount.plus(principalDisbursed); disburseAmount = disburseAmount.plus(principalDisbursed);
if(details.isEmpty()){ if (details.isEmpty()) {
diff = this.loanRepaymentScheduleDetail.getPrincipal().minus(principalDisbursed).getAmount(); diff = this.loanRepaymentScheduleDetail.getPrincipal().minus(principalDisbursed).getAmount();
}else{ } else {
for(LoanDisbursementDetails disbursementDetails:details){ for (LoanDisbursementDetails disbursementDetails : details) {
disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate()); disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate());
diff = diff.add(disbursementDetails.principal().subtract(principalDisbursed)); diff = diff.add(disbursementDetails.principal().subtract(principalDisbursed));
disbursementDetails.updatePrincipal(principalDisbursed); disbursementDetails.updatePrincipal(principalDisbursed);
} }
} }
this.loanRepaymentScheduleDetail.setPrincipal(this.loanRepaymentScheduleDetail.getPrincipal().minus(diff).getAmount()); this.loanRepaymentScheduleDetail.setPrincipal(this.loanRepaymentScheduleDetail.getPrincipal().minus(diff).getAmount());
if(diff.compareTo(BigDecimal.ZERO) == -1){ if (diff.compareTo(BigDecimal.ZERO) == -1) {
final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved amount "; final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved amount ";
throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.amount", principalDisbursed, throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.amount", principalDisbursed,
this.loanRepaymentScheduleDetail.getPrincipal().getAmount()); this.loanRepaymentScheduleDetail.getPrincipal().getAmount());
@@ -1766,15 +1767,17 @@ public class Loan extends AbstractPersistable<Long> {
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay); validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays); validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
handleDisbursementTransaction(paymentDetail, actualDisbursementDate, txnExternalId,disburseAmount); handleDisbursementTransaction(paymentDetail, actualDisbursementDate, txnExternalId, disburseAmount);
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName); BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
boolean isEmiAmountChanged = false; boolean isEmiAmountChanged = false;
if(this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0){ if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),actualDisbursementDate.toDate(),emiAmount,this); LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
actualDisbursementDate.toDate(), emiAmount, this);
this.loanTermVariations.add(loanVariationTerms); this.loanTermVariations.add(loanVariationTerms);
isEmiAmountChanged = true; isEmiAmountChanged = true;
} }
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || diff.compareTo(BigDecimal.ZERO) !=0 || isEmiAmountChanged) { if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || diff.compareTo(BigDecimal.ZERO) != 0
|| isEmiAmountChanged) {
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
holidays, workingDays); holidays, workingDays);
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate); updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
@@ -1788,22 +1791,23 @@ public class Loan extends AbstractPersistable<Long> {
actualDisbursementDate); actualDisbursementDate);
this.loanTransactions.add(interestAppliedTransaction); this.loanTransactions.add(interestAppliedTransaction);
// changedTransactionDetail = reprocessTransactionForDisbursement(changedTransactionDetail); // changedTransactionDetail =
// reprocessTransactionForDisbursement(changedTransactionDetail);
} }
} }
public ChangedTransactionDetail reprocessTransactionForDisbursement() { public ChangedTransactionDetail reprocessTransactionForDisbursement() {
ChangedTransactionDetail changedTransactionDetail = null; ChangedTransactionDetail changedTransactionDetail = null;
for(LoanCharge loanCharge:this.charges){ for (LoanCharge loanCharge : this.charges) {
if (loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()) { if (loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()) {
final Set<LoanInstallmentCharge> chargePerInstallments = generateInstallmentLoanCharges(loanCharge); final Set<LoanInstallmentCharge> chargePerInstallments = generateInstallmentLoanCharges(loanCharge);
loanCharge.addLoanInstallmentCharges(chargePerInstallments); loanCharge.addLoanInstallmentCharges(chargePerInstallments);
} }
} }
if(this.loanProduct.isMultiDisburseLoan()){ if (this.loanProduct.isMultiDisburseLoan()) {
if(!this.actualDisbursementDate.equals(fetchLastDisburseDetail().getDisbursementDate())){ if (!this.actualDisbursementDate.equals(fetchLastDisburseDetail().getDisbursementDate())) {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategy);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
@@ -1817,37 +1821,35 @@ public class Loan extends AbstractPersistable<Long> {
updateLoanSummaryDerivedFields(); updateLoanSummaryDerivedFields();
} }
return changedTransactionDetail; return changedTransactionDetail;
} }
private Collection<LoanDisbursementDetails> fetchUndisbursedDetail(){ private Collection<LoanDisbursementDetails> fetchUndisbursedDetail() {
Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<LoanDisbursementDetails>(); Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<LoanDisbursementDetails>();
Date date = null; Date date = null;
for(LoanDisbursementDetails disbursementDetail:this.disbursementDetails){ for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
if(disbursementDetail.actualDisbursementDate() == null){ if (disbursementDetail.actualDisbursementDate() == null) {
if(date == null || disbursementDetail.expectedDisbursementDate().equals(date)){ if (date == null || disbursementDetail.expectedDisbursementDate().equals(date)) {
disbursementDetails.add(disbursementDetail); disbursementDetails.add(disbursementDetail);
date =disbursementDetail.expectedDisbursementDate(); date = disbursementDetail.expectedDisbursementDate();
}else if(disbursementDetail.expectedDisbursementDate().before(date)){ } else if (disbursementDetail.expectedDisbursementDate().before(date)) {
disbursementDetails.clear(); disbursementDetails.clear();
disbursementDetails.add(disbursementDetail); disbursementDetails.add(disbursementDetail);
date =disbursementDetail.expectedDisbursementDate(); date = disbursementDetail.expectedDisbursementDate();
} }
} }
} }
return disbursementDetails; return disbursementDetails;
} }
private LoanDisbursementDetails fetchLastDisburseDetail(){ private LoanDisbursementDetails fetchLastDisburseDetail() {
LoanDisbursementDetails details = null; LoanDisbursementDetails details = null;
Date date = this.actualDisbursementDate; Date date = this.actualDisbursementDate;
if(date != null){ if (date != null) {
for(LoanDisbursementDetails disbursementDetail:this.disbursementDetails){ for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
if(disbursementDetail.actualDisbursementDate() != null){ if (disbursementDetail.actualDisbursementDate() != null) {
if(disbursementDetail.actualDisbursementDate().after(date) || if (disbursementDetail.actualDisbursementDate().after(date) || disbursementDetail.actualDisbursementDate().equals(date)) {
disbursementDetail.actualDisbursementDate().equals(date)){ date = disbursementDetail.actualDisbursementDate();
date =disbursementDetail.actualDisbursementDate();
details = disbursementDetail; details = disbursementDetail;
} }
} }
@@ -1856,29 +1858,29 @@ public class Loan extends AbstractPersistable<Long> {
return details; return details;
} }
private BigDecimal getDisbursedAmount(){ private BigDecimal getDisbursedAmount() {
BigDecimal principal = BigDecimal.ZERO; BigDecimal principal = BigDecimal.ZERO;
for(LoanDisbursementDetails disbursementDetail:this.disbursementDetails){ for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
if(disbursementDetail.actualDisbursementDate() != null){ if (disbursementDetail.actualDisbursementDate() != null) {
principal = principal.add(disbursementDetail.principal()); principal = principal.add(disbursementDetail.principal());
} }
} }
return principal; return principal;
} }
private void removeDisbursementDetail(){ private void removeDisbursementDetail() {
Set<LoanDisbursementDetails> details = new HashSet<LoanDisbursementDetails>(this.disbursementDetails); Set<LoanDisbursementDetails> details = new HashSet<LoanDisbursementDetails>(this.disbursementDetails);
for(LoanDisbursementDetails disbursementDetail:details){ for (LoanDisbursementDetails disbursementDetail : details) {
if(disbursementDetail.actualDisbursementDate() == null){ if (disbursementDetail.actualDisbursementDate() == null) {
this.disbursementDetails.remove(disbursementDetail); this.disbursementDetails.remove(disbursementDetail);
} }
} }
} }
private boolean isDisbursementAllowed(){ private boolean isDisbursementAllowed() {
boolean isAllowed = false; boolean isAllowed = false;
for(LoanDisbursementDetails disbursementDetail:this.disbursementDetails){ for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
if(disbursementDetail.actualDisbursementDate() == null){ if (disbursementDetail.actualDisbursementDate() == null) {
isAllowed = true; isAllowed = true;
break; break;
} }
@@ -1886,10 +1888,10 @@ public class Loan extends AbstractPersistable<Long> {
return isAllowed; return isAllowed;
} }
private boolean atleastOnceDisbursed(){ private boolean atleastOnceDisbursed() {
boolean isDisbursed = false; boolean isDisbursed = false;
for(LoanDisbursementDetails disbursementDetail:this.disbursementDetails){ for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
if(disbursementDetail.actualDisbursementDate() != null){ if (disbursementDetail.actualDisbursementDate() != null) {
isDisbursed = true; isDisbursed = true;
break; break;
} }
@@ -1926,26 +1928,27 @@ public class Loan extends AbstractPersistable<Long> {
} }
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<LoanTermVariationsData>(); final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<LoanTermVariationsData>();
boolean isDefaultEmiAmountReq = true; boolean isDefaultEmiAmountReq = true;
for(LoanTermVariations variationTerms:this.loanTermVariations){ for (LoanTermVariations variationTerms : this.loanTermVariations) {
if(variationTerms.getTermType().isEMIAmountVariation()){ if (variationTerms.getTermType().isEMIAmountVariation()) {
if(variationTerms.getTermApplicableFrom().equals(this.getDisbursementDate().toDate())){ if (variationTerms.getTermApplicableFrom().equals(this.getDisbursementDate().toDate())) {
isDefaultEmiAmountReq =false; isDefaultEmiAmountReq = false;
} }
loanVariationTermsData.add(variationTerms.toData()); loanVariationTermsData.add(variationTerms.toData());
} }
} }
if(isDefaultEmiAmountReq){ if (isDefaultEmiAmountReq) {
LoanTermVariationsData data = new LoanTermVariationsData(null, LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), this.getDisbursementDate(), this.fixedEmiAmount); LoanTermVariationsData data = new LoanTermVariationsData(null,
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), this.getDisbursementDate(), this.fixedEmiAmount);
loanVariationTermsData.add(data); loanVariationTermsData.add(data);
} }
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, getDisbursementDate(), loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
getExpectedFirstRepaymentOnDate(), calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
this.maxOutstandingLoanBalance,loanVariationTermsData); loanVariationTermsData);
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms,charges(), final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, charges(),
isHolidayEnabled, holidays, workingDays); isHolidayEnabled, holidays, workingDays);
updateLoanSchedule(loanSchedule); updateLoanSchedule(loanSchedule);
@@ -1954,8 +1957,8 @@ public class Loan extends AbstractPersistable<Long> {
private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn, private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn,
final String txnExternalId, Money disburseAmt) { final String txnExternalId, Money disburseAmt) {
// track disbursement transaction // track disbursement transaction
final LoanTransaction disbursementTransaction = LoanTransaction.disbursement(getOffice(), final LoanTransaction disbursementTransaction = LoanTransaction.disbursement(getOffice(), disburseAmt, paymentDetail, disbursedOn,
disburseAmt, paymentDetail, disbursedOn, txnExternalId); txnExternalId);
disbursementTransaction.updateLoan(this); disbursementTransaction.updateLoan(this);
this.loanTransactions.add(disbursementTransaction); this.loanTransactions.add(disbursementTransaction);
@@ -1972,13 +1975,14 @@ public class Loan extends AbstractPersistable<Long> {
* "APPLY Charge" transactions are created for all other fees ( which * "APPLY Charge" transactions are created for all other fees ( which
* are created during disbursal but not repaid) * are created during disbursal but not repaid)
**/ **/
if(disbursedOn.toDate().equals(this.actualDisbursementDate)){ if (disbursedOn.toDate().equals(this.actualDisbursementDate)) {
Money disbursentMoney = Money.zero(getCurrency()); Money disbursentMoney = Money.zero(getCurrency());
final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getOffice(), disbursentMoney, null, disbursedOn, final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getOffice(), disbursentMoney, null, disbursedOn,
null); null);
for (final LoanCharge charge : charges()) { for (final LoanCharge charge : charges()) {
if (charge.isDueAtDisbursement()) { if (charge.isDueAtDisbursement()) {
if (totalFeeChargesDueAtDisbursement.isGreaterThanZero() && !charge.getChargePaymentMode().isPaymentModeAccountTransfer()) { if (totalFeeChargesDueAtDisbursement.isGreaterThanZero()
&& !charge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
charge.markAsFullyPaid(); charge.markAsFullyPaid();
// Add "Loan Charge Paid By" details to this transaction // Add "Loan Charge Paid By" details to this transaction
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount()); final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount());
@@ -2004,14 +2008,13 @@ public class Loan extends AbstractPersistable<Long> {
getApprovedOnDate()); getApprovedOnDate());
} }
if(getExpectedFirstRepaymentOnDate() != null && disbursedOn.isAfter(getExpectedFirstRepaymentOnDate())) if (getExpectedFirstRepaymentOnDate() != null && disbursedOn.isAfter(getExpectedFirstRepaymentOnDate())) {
{ final String errorMessage = "submittedOnDate cannot be after the loans expectedFirstRepaymentOnDate: "
final String errorMessage = "submittedOnDate cannot be after the loans expectedFirstRepaymentOnDate: " + getExpectedFirstRepaymentOnDate().toString(); + getExpectedFirstRepaymentOnDate().toString();
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.after.expected.first.repayment.date", errorMessage, disbursedOn, throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.after.expected.first.repayment.date", errorMessage,
getExpectedFirstRepaymentOnDate()); disbursedOn, getExpectedFirstRepaymentOnDate());
} }
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSED, disbursedOn); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSED, disbursedOn);
if (disbursedOn.isAfter(new LocalDate())) { if (disbursedOn.isAfter(new LocalDate())) {
@@ -2065,13 +2068,13 @@ public class Loan extends AbstractPersistable<Long> {
this.disbursedBy = null; this.disbursedBy = null;
boolean isDisbueseAmtChanged = !this.approvedPrincipal.equals(this.loanRepaymentScheduleDetail.getPrincipal()); boolean isDisbueseAmtChanged = !this.approvedPrincipal.equals(this.loanRepaymentScheduleDetail.getPrincipal());
this.loanRepaymentScheduleDetail.setPrincipal(this.approvedPrincipal); this.loanRepaymentScheduleDetail.setPrincipal(this.approvedPrincipal);
if(this.loanProduct.isMultiDisburseLoan()){ if (this.loanProduct.isMultiDisburseLoan()) {
for(final LoanDisbursementDetails details:this.disbursementDetails){ for (final LoanDisbursementDetails details : this.disbursementDetails) {
details.updateActualDisbursementDate(null); details.updateActualDisbursementDate(null);
details.resetPrincipal(); details.resetPrincipal();
} }
} }
boolean isEmiAmountChanged = this.loanTermVariations.size()>0; boolean isEmiAmountChanged = this.loanTermVariations.size() > 0;
updateLoanToPreDisbursalState(); updateLoanToPreDisbursalState();
if (isScheduleRegenerateRequired || isDisbueseAmtChanged || isEmiAmountChanged) { if (isScheduleRegenerateRequired || isDisbueseAmtChanged || isEmiAmountChanged) {
// clear off actual disbusrement date so schedule regeneration // clear off actual disbusrement date so schedule regeneration
@@ -2262,9 +2265,9 @@ public class Loan extends AbstractPersistable<Long> {
} }
} }
if(this.loanProduct.isMultiDisburseLoan() && adjustedTransaction == null){ if (this.loanProduct.isMultiDisburseLoan() && adjustedTransaction == null) {
BigDecimal totalDisbursed = getDisbursedAmount(); BigDecimal totalDisbursed = getDisbursedAmount();
if(totalDisbursed.compareTo(this.summary.getTotalPrincipalRepaid())<0){ if (totalDisbursed.compareTo(this.summary.getTotalPrincipalRepaid()) < 0) {
final String errorMessage = "The transaction cannot be done before the loan disbursement: " final String errorMessage = "The transaction cannot be done before the loan disbursement: "
+ getApprovedOnDate().toString(); + getApprovedOnDate().toString();
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.done.before.disbursement", errorMessage); throw new InvalidLoanStateTransitionException("transaction", "cannot.be.done.before.disbursement", errorMessage);
@@ -2324,8 +2327,7 @@ public class Loan extends AbstractPersistable<Long> {
boolean isAllChargesPaid = true; boolean isAllChargesPaid = true;
for (final LoanCharge loanCharge : this.charges) { for (final LoanCharge loanCharge : this.charges) {
if (loanCharge.isActive() if (loanCharge.isActive() && !(loanCharge.isPaid() || loanCharge.isWaived())) {
&& !(loanCharge.isPaid() || loanCharge.isWaived())) {
isAllChargesPaid = false; isAllChargesPaid = false;
break; break;
} }
@@ -2511,7 +2513,7 @@ public class Loan extends AbstractPersistable<Long> {
return changedTransactionDetail; return changedTransactionDetail;
} }
public ChangedTransactionDetail undoWrittenOff(final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds){ public ChangedTransactionDetail undoWrittenOff(final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO); validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
@@ -2579,15 +2581,14 @@ public class Loan extends AbstractPersistable<Long> {
public ChangedTransactionDetail closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, public ChangedTransactionDetail closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final AppUser currentUser,final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency, final AppUser currentUser, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
final WorkingDays workingDays) { final WorkingDays workingDays) {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategy);
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loanScheduleFactory, currency,
workingDays, loanRepaymentScheduleTransactionProcessor); calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor);
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING); validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING);
@@ -2639,22 +2640,23 @@ public class Loan extends AbstractPersistable<Long> {
updateLoanSummaryDerivedFields(); updateLoanSummaryDerivedFields();
} }
if(changedTransactionDetail == null){ if (changedTransactionDetail == null) {
changedTransactionDetail = new ChangedTransactionDetail(); changedTransactionDetail = new ChangedTransactionDetail();
} }
changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction); changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction);
return changedTransactionDetail; return changedTransactionDetail;
} }
private ChangedTransactionDetail closeDisbursements(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency, private ChangedTransactionDetail closeDisbursements(final LoanScheduleGeneratorFactory loanScheduleFactory,
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays, final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
final WorkingDays workingDays, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) { final List<Holiday> holidays, final WorkingDays workingDays,
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
ChangedTransactionDetail changedTransactionDetail = null; ChangedTransactionDetail changedTransactionDetail = null;
if(isDisbursementAllowed() && atleastOnceDisbursed()){ if (isDisbursementAllowed() && atleastOnceDisbursed()) {
this.loanRepaymentScheduleDetail.setPrincipal(getDisbursedAmount()); this.loanRepaymentScheduleDetail.setPrincipal(getDisbursedAmount());
removeDisbursementDetail(); removeDisbursementDetail();
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
holidays, workingDays); workingDays);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
@@ -2670,11 +2672,11 @@ public class Loan extends AbstractPersistable<Long> {
return changedTransactionDetail; return changedTransactionDetail;
} }
private LoanTransaction findlatestTransaction(){ private LoanTransaction findlatestTransaction() {
LoanTransaction transaction = null; LoanTransaction transaction = null;
for(LoanTransaction loanTransaction:this.loanTransactions){ for (LoanTransaction loanTransaction : this.loanTransactions) {
if(!loanTransaction.isReversed() && if (!loanTransaction.isReversed()
(transaction == null || transaction.getTransactionDate().isBefore(loanTransaction.getTransactionDate()))){ && (transaction == null || transaction.getTransactionDate().isBefore(loanTransaction.getTransactionDate()))) {
transaction = loanTransaction; transaction = loanTransaction;
} }
} }
@@ -2687,7 +2689,6 @@ public class Loan extends AbstractPersistable<Long> {
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
final WorkingDays workingDays) { final WorkingDays workingDays) {
validateAccountStatus(LoanEvent.LOAN_CLOSED); validateAccountStatus(LoanEvent.LOAN_CLOSED);
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
@@ -2713,8 +2714,8 @@ public class Loan extends AbstractPersistable<Long> {
} }
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategy);
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loanScheduleFactory, currency,
workingDays, loanRepaymentScheduleTransactionProcessor); calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor);
LoanTransaction loanTransaction = null; LoanTransaction loanTransaction = null;
if (isOpen()) { if (isOpen()) {
@@ -2768,7 +2769,7 @@ public class Loan extends AbstractPersistable<Long> {
} }
} }
if(changedTransactionDetail == null){ if (changedTransactionDetail == null) {
changedTransactionDetail = new ChangedTransactionDetail(); changedTransactionDetail = new ChangedTransactionDetail();
} }
changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction); changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction);
@@ -2866,10 +2867,9 @@ public class Loan extends AbstractPersistable<Long> {
return status().isActive(); return status().isActive();
} }
private boolean isAllTranchesNotDisbursed(){ private boolean isAllTranchesNotDisbursed() {
return this.loanProduct.isMultiDisburseLoan() return this.loanProduct.isMultiDisburseLoan()
&& (LoanStatus.fromInt(this.loanStatus).isActive() && (LoanStatus.fromInt(this.loanStatus).isActive() || LoanStatus.fromInt(this.loanStatus).isApproved())
||LoanStatus.fromInt(this.loanStatus).isApproved())
&& isDisbursementAllowed(); && isDisbursementAllowed();
} }
@@ -2916,7 +2916,7 @@ public class Loan extends AbstractPersistable<Long> {
return disbursementDate; return disbursementDate;
} }
public LocalDate getExpectedDisbursedOnLocalDateForTemplate(){ public LocalDate getExpectedDisbursedOnLocalDateForTemplate() {
LocalDate expectedDisbursementDate = null; LocalDate expectedDisbursementDate = null;
if (this.expectedDisbursementDate != null) { if (this.expectedDisbursementDate != null) {
@@ -2924,20 +2924,20 @@ public class Loan extends AbstractPersistable<Long> {
} }
Collection<LoanDisbursementDetails> details = fetchUndisbursedDetail(); Collection<LoanDisbursementDetails> details = fetchUndisbursedDetail();
if(!details.isEmpty()){ if (!details.isEmpty()) {
for(LoanDisbursementDetails disbursementDetails:details){ for (LoanDisbursementDetails disbursementDetails : details) {
expectedDisbursementDate = new LocalDate(disbursementDetails.expectedDisbursementDate()); expectedDisbursementDate = new LocalDate(disbursementDetails.expectedDisbursementDate());
} }
} }
return expectedDisbursementDate; return expectedDisbursementDate;
} }
public BigDecimal getDisburseAmountForTemplate(){ public BigDecimal getDisburseAmountForTemplate() {
BigDecimal principal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount(); BigDecimal principal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount();
Collection<LoanDisbursementDetails> details = fetchUndisbursedDetail(); Collection<LoanDisbursementDetails> details = fetchUndisbursedDetail();
if(!details.isEmpty()){ if (!details.isEmpty()) {
principal =BigDecimal.ZERO; principal = BigDecimal.ZERO;
for(LoanDisbursementDetails disbursementDetails:details){ for (LoanDisbursementDetails disbursementDetails : details) {
principal = principal.add(disbursementDetails.principal()); principal = principal.add(disbursementDetails.principal());
} }
} }
@@ -2959,9 +2959,9 @@ public class Loan extends AbstractPersistable<Long> {
private boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) { private boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) {
boolean isRegenerationRequired = false; boolean isRegenerationRequired = false;
if(this.loanProduct.isMultiDisburseLoan()){ if (this.loanProduct.isMultiDisburseLoan()) {
LoanDisbursementDetails details = fetchLastDisburseDetail(); LoanDisbursementDetails details = fetchLastDisburseDetail();
if(!details.expectedDisbursementDate().equals(details.actualDisbursementDate())){ if (!details.expectedDisbursementDate().equals(details.actualDisbursementDate())) {
isRegenerationRequired = true; isRegenerationRequired = true;
} }
} }
@@ -3399,8 +3399,6 @@ public class Loan extends AbstractPersistable<Long> {
} }
} }
public Group group() { public Group group() {
return this.group; return this.group;
} }
@@ -3414,9 +3412,7 @@ public class Loan extends AbstractPersistable<Long> {
} }
public Integer getLoanProductLoanCounter() { public Integer getLoanProductLoanCounter() {
if(this.loanProductCounter == null){ if (this.loanProductCounter == null) { return 0; }
return 0;
}
return this.loanProductCounter; return this.loanProductCounter;
} }
@@ -3533,9 +3529,9 @@ public class Loan extends AbstractPersistable<Long> {
public Set<LoanCharge> charges() { public Set<LoanCharge> charges() {
Set<LoanCharge> loanCharges = new HashSet<LoanCharge>(); Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
if(this.charges != null){ if (this.charges != null) {
for(LoanCharge charge:this.charges){ for (LoanCharge charge : this.charges) {
if(charge.isActive()){ if (charge.isActive()) {
loanCharges.add(charge); loanCharges.add(charge);
} }
} }
@@ -3585,8 +3581,8 @@ public class Loan extends AbstractPersistable<Long> {
} }
break; break;
case LOAN_DISBURSED: case LOAN_DISBURSED:
if ((!(isApproved() && isNotDisbursed()) && !this.loanProduct.isMultiDisburseLoan()) || if ((!(isApproved() && isNotDisbursed()) && !this.loanProduct.isMultiDisburseLoan())
(this.loanProduct.isMultiDisburseLoan() && !isAllTranchesNotDisbursed())) { || (this.loanProduct.isMultiDisburseLoan() && !isAllTranchesNotDisbursed())) {
final String defaultUserMessage = "Loan Disbursal is not allowed. Loan Account is not in approved and not disbursed state."; final String defaultUserMessage = "Loan Disbursal is not allowed. Loan Account is not in approved and not disbursed state.";
final ApiParameterError error = ApiParameterError.generalError( final ApiParameterError error = ApiParameterError.generalError(
"error.msg.loan.disbursal.account.is.not.approve.not.disbursed.state", defaultUserMessage); "error.msg.loan.disbursal.account.is.not.approve.not.disbursed.state", defaultUserMessage);
@@ -3698,41 +3694,42 @@ public class Loan extends AbstractPersistable<Long> {
return this.disbursementDetails; return this.disbursementDetails;
} }
public ChangedTransactionDetail updateDisbursementDateForTranche(final LoanDisbursementDetails disbursementDetails,final JsonCommand command,final List<Long> existingTransactionIds, public ChangedTransactionDetail updateDisbursementDateForTranche(final LoanDisbursementDetails disbursementDetails,
final List<Long> existingReversedTransactionIds, final Map<String, Object> actualChanges, final JsonCommand command, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency, final Map<String, Object> actualChanges, final LoanScheduleGeneratorFactory loanScheduleFactory,
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays, final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
final WorkingDays workingDays){ final List<Holiday> holidays, final WorkingDays workingDays) {
validateAccountStatus(LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE); validateAccountStatus(LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE);
final LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName); final LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName);
disbursementDetails.updateExpectedDisbursementDate(expectedDisbursementDate.toDate()); disbursementDetails.updateExpectedDisbursementDate(expectedDisbursementDate.toDate());
actualChanges.put(LoanApiConstants.disbursementDateParameterName, command.stringValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName)); actualChanges.put(LoanApiConstants.disbursementDateParameterName,
actualChanges.put(LoanApiConstants.disbursementIdParameterName, command.stringValueOfParameterNamed(LoanApiConstants.disbursementIdParameterName)); command.stringValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName));
actualChanges.put(LoanApiConstants.disbursementIdParameterName,
command.stringValueOfParameterNamed(LoanApiConstants.disbursementIdParameterName));
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
holidays, workingDays); workingDays);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategy);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges()); getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
charges());
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
mapEntry.getValue().updateLoan(this); mapEntry.getValue().updateLoan(this);
this.loanTransactions.add(mapEntry.getValue()); this.loanTransactions.add(mapEntry.getValue());
} }
return changedTransactionDetail; return changedTransactionDetail;
} }
public BigDecimal retriveLastEmiAmount() {
public BigDecimal retriveLastEmiAmount(){
BigDecimal emiAmount = this.fixedEmiAmount; BigDecimal emiAmount = this.fixedEmiAmount;
Date startDate = this.getDisbursementDate().toDate(); Date startDate = this.getDisbursementDate().toDate();
for(LoanTermVariations loanTermVariations:this.loanTermVariations){ for (LoanTermVariations loanTermVariations : this.loanTermVariations) {
if(loanTermVariations.getTermType().isEMIAmountVariation() && if (loanTermVariations.getTermType().isEMIAmountVariation() && !startDate.after(loanTermVariations.getTermApplicableFrom())) {
!startDate.after(loanTermVariations.getTermApplicableFrom())){
startDate = loanTermVariations.getTermApplicableFrom(); startDate = loanTermVariations.getTermApplicableFrom();
emiAmount = loanTermVariations.getTermValue(); emiAmount = loanTermVariations.getTermValue();
} }
@@ -184,7 +184,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
if (clientId != null) { if (clientId != null) {
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId, loanProduct.getId()); cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId, loanProduct.getId());
} else if (groupId != null) { } else if (groupId != null) {
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue(), loanProduct.getId()); cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue(),
loanProduct.getId());
} }
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator, this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
loanProduct, cycleNumber); loanProduct, cycleNumber);
@@ -271,21 +272,39 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
loanId); } loanId); }
final Set<LoanCharge> existingCharges = existingLoanApplication.charges(); final Set<LoanCharge> existingCharges = existingLoanApplication.charges();
Map<Long,LoanChargeData> chargesMap = new HashMap<Long, LoanChargeData>(); Map<Long, LoanChargeData> chargesMap = new HashMap<Long, LoanChargeData>();
for(LoanCharge charge:existingCharges){ for (LoanCharge charge : existingCharges) {
LoanChargeData chargeData = new LoanChargeData(charge.getId(),charge.getDueLocalDate() ,charge.amountOrPercentage()); LoanChargeData chargeData = new LoanChargeData(charge.getId(), charge.getDueLocalDate(), charge.amountOrPercentage());
chargesMap.put(charge.getId(), chargeData); chargesMap.put(charge.getId(), chargeData);
} }
/**
* Stores all charges which are passed in during modify loan
* application
**/
final Set<LoanCharge> possiblyModifedLoanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson()); final Set<LoanCharge> possiblyModifedLoanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson());
/** Boolean determines if any charge has been modified **/
boolean isChargeModified = false; boolean isChargeModified = false;
for(LoanCharge loanCharge : possiblyModifedLoanCharges){
if(loanCharge.getId() == null){ /**
* If there are any charges already present, which are now not
* passed in as a part of the request, deem the charges as modified
**/
if (!possiblyModifedLoanCharges.containsAll(existingCharges)) {
isChargeModified = true;
}
/**
* If any new charges are added or values of existing charges are
* modified
**/
for (LoanCharge loanCharge : possiblyModifedLoanCharges) {
if (loanCharge.getId() == null) {
isChargeModified = true; isChargeModified = true;
}else{ } else {
LoanChargeData chargeData = chargesMap.get(loanCharge.getId()); LoanChargeData chargeData = chargesMap.get(loanCharge.getId());
if(loanCharge.amountOrPercentage().compareTo(chargeData.amountOrPercentage()) != 0 || if (loanCharge.amountOrPercentage().compareTo(chargeData.amountOrPercentage()) != 0
(loanCharge.isSpecifiedDueDate() && !loanCharge.getDueLocalDate().equals(chargeData.getDueDate()))){ || (loanCharge.isSpecifiedDueDate() && !loanCharge.getDueLocalDate().equals(chargeData.getDueDate()))) {
isChargeModified = true; isChargeModified = true;
} }
} }
@@ -338,7 +357,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
if (clientId != null) { if (clientId != null) {
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId, loanProduct.getId()); cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId, loanProduct.getId());
} else if (groupId != null) { } else if (groupId != null) {
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue(), loanProduct.getId()); cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue(),
loanProduct.getId());
} }
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator, this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
loanProduct, cycleNumber); loanProduct, cycleNumber);
@@ -728,17 +748,13 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
submittedOnDate.toString(), closeDate.toString()); submittedOnDate.toString(), closeDate.toString());
} }
if(expectedFirstRepaymentOnDate != null && submittedOnDate.isAfter(expectedFirstRepaymentOnDate)) if (expectedFirstRepaymentOnDate != null && submittedOnDate.isAfter(expectedFirstRepaymentOnDate)) {
{
defaultUserMessage = "submittedOnDate cannot be after the loans expectedFirstRepaymentOnDate."; defaultUserMessage = "submittedOnDate cannot be after the loans expectedFirstRepaymentOnDate.";
throw new LoanApplicationDateException("submitted.on.date.cannot.be.after.the.loan.expected.first.repayment.date", defaultUserMessage, throw new LoanApplicationDateException("submitted.on.date.cannot.be.after.the.loan.expected.first.repayment.date",
submittedOnDate.toString(), expectedFirstRepaymentOnDate.toString()); defaultUserMessage, submittedOnDate.toString(), expectedFirstRepaymentOnDate.toString());
} }
} }
private void checkClientOrGroupActive(final Loan loan) { private void checkClientOrGroupActive(final Loan loan) {
final Client client = loan.client(); final Client client = loan.client();
if (client != null) { if (client != null) {