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,
@@ -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,8 +903,11 @@ 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;
// add new charges
if (loanCharge.getId() == null) { if (loanCharge.getId() == null) {
loanCharge.update(this); loanCharge.update(this);
this.charges.add(loanCharge); this.charges.add(loanCharge);
@@ -933,6 +932,8 @@ public class Loan extends AbstractPersistable<Long> {
charge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(), charge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
totalChargeAmt); totalChargeAmt);
} }
/** Updated deleted charges **/
for (Long id : existingCharges) { for (Long id : existingCharges) {
fetchLoanChargesById(id).setActive(false); fetchLoanChargesById(id).setActive(false);
} }
@@ -1245,8 +1246,10 @@ 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";
@@ -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) {
@@ -1706,7 +1707,8 @@ public class Loan extends AbstractPersistable<Long> {
} }
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,7 +1720,6 @@ 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);
@@ -1770,11 +1771,13 @@ public class Loan extends AbstractPersistable<Long> {
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,7 +1791,8 @@ public class Loan extends AbstractPersistable<Long> {
actualDisbursementDate); actualDisbursementDate);
this.loanTransactions.add(interestAppliedTransaction); this.loanTransactions.add(interestAppliedTransaction);
// changedTransactionDetail = reprocessTransactionForDisbursement(changedTransactionDetail); // changedTransactionDetail =
// reprocessTransactionForDisbursement(changedTransactionDetail);
} }
} }
@@ -1817,7 +1821,6 @@ public class Loan extends AbstractPersistable<Long> {
updateLoanSummaryDerivedFields(); updateLoanSummaryDerivedFields();
} }
return changedTransactionDetail; return changedTransactionDetail;
} }
@@ -1845,8 +1848,7 @@ public class Loan extends AbstractPersistable<Long> {
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;
} }
@@ -1935,15 +1937,16 @@ public class Loan extends AbstractPersistable<Long> {
} }
} }
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);
@@ -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);
@@ -1978,7 +1981,8 @@ public class Loan extends AbstractPersistable<Long> {
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())) {
@@ -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;
} }
@@ -2583,11 +2585,10 @@ 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) {
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);
@@ -2646,15 +2647,16 @@ public class Loan extends AbstractPersistable<Long> {
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(),
@@ -2673,8 +2675,8 @@ public class Loan extends AbstractPersistable<Long> {
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()) {
@@ -2868,8 +2869,7 @@ public class Loan extends AbstractPersistable<Long> {
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();
} }
@@ -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;
} }
@@ -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,26 +3694,29 @@ 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());
@@ -3726,13 +3725,11 @@ public class Loan extends AbstractPersistable<Long> {
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);
@@ -277,15 +278,33 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
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;
/**
* 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) { for (LoanCharge loanCharge : possiblyModifedLoanCharges) {
if (loanCharge.getId() == null) { 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) {