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
@@ -285,27 +285,25 @@ public class Loan extends AbstractPersistable<Long> {
private LoanLifecycleStateMachine loanLifecycleStateMachine; private LoanLifecycleStateMachine loanLifecycleStateMachine;
@Transient @Transient
private LoanSummaryWrapper loanSummaryWrapper; private LoanSummaryWrapper loanSummaryWrapper;
@Column(name = "approved_principal", scale = 6, precision = 19, nullable = false) @Column(name = "approved_principal", scale = 6, precision = 19, nullable = false)
private BigDecimal approvedPrincipal; private BigDecimal approvedPrincipal;
@Column(name = "fixed_emi_amount", scale = 6, precision = 19, nullable = true) @Column(name = "fixed_emi_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal fixedEmiAmount; private BigDecimal fixedEmiAmount;
@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>();
@OrderBy(value = "termApplicableFrom, id") @OrderBy(value = "termApplicableFrom, id")
@LazyCollection(LazyCollectionOption.FALSE) @LazyCollection(LazyCollectionOption.FALSE)
@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());
@@ -1233,29 +1234,31 @@ public class Loan extends AbstractPersistable<Long> {
actualChanges.put(loanTermFrequencyTypeParamName, newTermPeriodFrequencyType.getValue()); actualChanges.put(loanTermFrequencyTypeParamName, newTermPeriodFrequencyType.getValue());
this.termPeriodFrequencyType = newValue; this.termPeriodFrequencyType = newValue;
} }
final String principalParamName = "principal"; final String principalParamName = "principal";
if (command.isChangeInBigDecimalParameterNamed(principalParamName, this.approvedPrincipal)) { if (command.isChangeInBigDecimalParameterNamed(principalParamName, this.approvedPrincipal)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(principalParamName); final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(principalParamName);
this.approvedPrincipal = newValue; this.approvedPrincipal = newValue;
} }
if (loanProduct.isMultiDisburseLoan()) { if (loanProduct.isMultiDisburseLoan()) {
updateDisbursementDetails(command, actualChanges); updateDisbursementDetails(command, actualChanges);
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);
@@ -1479,7 +1480,7 @@ public class Loan extends AbstractPersistable<Long> {
totalChargeAmt); totalChargeAmt);
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
} }
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
// validate if disbursement date is a holiday or a non-working day // validate if disbursement date is a holiday or a non-working day
@@ -1494,9 +1495,9 @@ public class Loan extends AbstractPersistable<Long> {
public Map<String, Object> loanApplicationRejection(final AppUser currentUser, final JsonCommand command, public Map<String, Object> loanApplicationRejection(final AppUser currentUser, final JsonCommand command,
final LoanLifecycleStateMachine loanLifecycleStateMachine) { final LoanLifecycleStateMachine loanLifecycleStateMachine) {
validateAccountStatus(LoanEvent.LOAN_REJECTED); validateAccountStatus(LoanEvent.LOAN_REJECTED);
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(); final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, LoanStatus.fromInt(this.loanStatus)); final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, LoanStatus.fromInt(this.loanStatus));
@@ -1593,7 +1594,7 @@ public class Loan extends AbstractPersistable<Long> {
final LoanLifecycleStateMachine loanLifecycleStateMachine) { final LoanLifecycleStateMachine loanLifecycleStateMachine) {
validateAccountStatus(LoanEvent.LOAN_APPROVED); validateAccountStatus(LoanEvent.LOAN_APPROVED);
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(); final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, LoanStatus.fromInt(this.loanStatus)); final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, 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);
@@ -1787,23 +1790,24 @@ public class Loan extends AbstractPersistable<Long> {
final LoanTransaction interestAppliedTransaction = LoanTransaction.applyInterest(getOffice(), this, interestApplied, final LoanTransaction interestAppliedTransaction = LoanTransaction.applyInterest(getOffice(), this, interestApplied,
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();
@@ -1812,84 +1816,82 @@ public class Loan extends AbstractPersistable<Long> {
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);
} }
} }
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;
} }
} }
} }
} }
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;
} }
} }
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());
@@ -2003,14 +2007,13 @@ public class Loan extends AbstractPersistable<Long> {
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.before.approval.date", errorMessage, disbursedOn, throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.before.approval.date", errorMessage, disbursedOn,
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);
@@ -2050,7 +2053,7 @@ public class Loan extends AbstractPersistable<Long> {
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) { final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
validateAccountStatus(LoanEvent.LOAN_DISBURSAL_UNDO); validateAccountStatus(LoanEvent.LOAN_DISBURSAL_UNDO);
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(); final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus); final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus);
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus); final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus);
@@ -2065,18 +2068,18 @@ 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
// uses expected date. // uses expected date.
regenerateRepaymentSchedule(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, regenerateRepaymentSchedule(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate,
isHolidayEnabled, holidays, workingDays); isHolidayEnabled, holidays, workingDays);
} }
@@ -2121,7 +2124,7 @@ public class Loan extends AbstractPersistable<Long> {
public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction, public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds, final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) { final List<Long> existingReversedTransactionIds) {
validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER); validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
@@ -2261,10 +2264,10 @@ public class Loan extends AbstractPersistable<Long> {
loanTransaction.getAmount(loanCurrency()), totalInterestOutstandingOnLoan.getAmount()); loanTransaction.getAmount(loanCurrency()), totalInterestOutstandingOnLoan.getAmount());
} }
} }
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;
} }
@@ -2510,9 +2512,9 @@ 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());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
@@ -2579,18 +2581,17 @@ 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);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING,
LoanStatus.fromInt(this.loanStatus)); LoanStatus.fromInt(this.loanStatus));
@@ -2624,7 +2625,7 @@ public class Loan extends AbstractPersistable<Long> {
final String errorMessage = "The date on which a loan is written off cannot be in the future."; final String errorMessage = "The date on which a loan is written off cannot be in the future.";
throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, writtenOffOnLocalDate); throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, writtenOffOnLocalDate);
} }
loanTransaction = LoanTransaction.writeoff(this, getOffice(), writtenOffOnLocalDate, txnExternalId); loanTransaction = LoanTransaction.writeoff(this, getOffice(), writtenOffOnLocalDate, txnExternalId);
final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, this.loanTransactions); final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, this.loanTransactions);
if (!isLastTransaction) { if (!isLastTransaction) {
@@ -2639,23 +2640,24 @@ 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(),
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges()); allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges());
@@ -2669,12 +2671,12 @@ 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;
} }
} }
@@ -2683,13 +2685,12 @@ public class Loan extends AbstractPersistable<Long> {
public ChangedTransactionDetail close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, public ChangedTransactionDetail close(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 LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency, 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) {
validateAccountStatus(LoanEvent.LOAN_CLOSED); validateAccountStatus(LoanEvent.LOAN_CLOSED);
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
@@ -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()) {
@@ -2767,8 +2768,8 @@ public class Loan extends AbstractPersistable<Long> {
throw new InvalidLoanStateTransitionException("close", "loan.is.overpaid", errorMessage, totalLoanOverpayment.toString()); throw new InvalidLoanStateTransitionException("close", "loan.is.overpaid", errorMessage, totalLoanOverpayment.toString());
} }
} }
if(changedTransactionDetail == null){ if (changedTransactionDetail == null) {
changedTransactionDetail = new ChangedTransactionDetail(); changedTransactionDetail = new ChangedTransactionDetail();
} }
changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction); changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction);
@@ -2825,7 +2826,7 @@ public class Loan extends AbstractPersistable<Long> {
private boolean isApproved() { private boolean isApproved() {
return status().isApproved(); return status().isApproved();
} }
private boolean isNotDisbursed() { private boolean isNotDisbursed() {
return !isDisbursed(); return !isDisbursed();
} }
@@ -2865,11 +2866,10 @@ public class Loan extends AbstractPersistable<Long> {
private boolean isOpen() { private boolean isOpen() {
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();
} }
@@ -2915,29 +2915,29 @@ 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) {
expectedDisbursementDate = new LocalDate(this.expectedDisbursementDate); expectedDisbursementDate = new LocalDate(this.expectedDisbursementDate);
} }
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;
} }
} }
@@ -3398,8 +3398,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);
@@ -3674,7 +3670,7 @@ public class Loan extends AbstractPersistable<Long> {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
} }
public LoanCharge fetchLoanChargesById(Long id) { public LoanCharge fetchLoanChargesById(Long id) {
LoanCharge charge = null; LoanCharge charge = null;
for (LoanCharge loanCharge : this.charges) { for (LoanCharge loanCharge : this.charges) {
@@ -3685,7 +3681,7 @@ public class Loan extends AbstractPersistable<Long> {
} }
return charge; return charge;
} }
private List<Long> fetchAllLoanChargeIds() { private List<Long> fetchAllLoanChargeIds() {
List<Long> list = new ArrayList<Long>(); List<Long> list = new ArrayList<Long>();
for (LoanCharge loanCharge : this.charges) { for (LoanCharge loanCharge : this.charges) {
@@ -3693,46 +3689,47 @@ public class Loan extends AbstractPersistable<Long> {
} }
return list; return list;
} }
public Set<LoanDisbursementDetails> getDisbursementDetails() { public Set<LoanDisbursementDetails> getDisbursementDetails() {
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);
@@ -269,28 +270,46 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
if (!existingLoanApplication.isSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified( if (!existingLoanApplication.isSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(
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;
} }
} }
} }
final Set<LoanCollateral> possiblyModifedLoanCollateralItems = this.loanCollateralAssembler final Set<LoanCollateral> possiblyModifedLoanCollateralItems = this.loanCollateralAssembler
.fromParsedJson(command.parsedJson()); .fromParsedJson(command.parsedJson());
@@ -300,7 +319,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
if (changes.containsKey("expectedDisbursementDate")) { if (changes.containsKey("expectedDisbursementDate")) {
this.loanAssembler.validateExpectedDisbursementForHolidayAndNonWorkingDay(existingLoanApplication); this.loanAssembler.validateExpectedDisbursementForHolidayAndNonWorkingDay(existingLoanApplication);
} }
final String clientIdParamName = "clientId"; final String clientIdParamName = "clientId";
if (changes.containsKey(clientIdParamName)) { if (changes.containsKey(clientIdParamName)) {
final Long clientId = command.longValueOfParameterNamed(clientIdParamName); final Long clientId = command.longValueOfParameterNamed(clientIdParamName);
@@ -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);
@@ -727,17 +747,13 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
throw new LoanApplicationDateException("submitted.on.date.cannot.be.after.the.loan.product.close.date", defaultUserMessage, throw new LoanApplicationDateException("submitted.on.date.cannot.be.after.the.loan.product.close.date", defaultUserMessage,
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();