FINERACT-202 : enabling the option for multi reshedule
Project: http://git-wip-us.apache.org/repos/asf/incubator-fineract/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-fineract/commit/fe617123 Tree: http://git-wip-us.apache.org/repos/asf/incubator-fineract/tree/fe617123 Diff: http://git-wip-us.apache.org/repos/asf/incubator-fineract/diff/fe617123 Branch: refs/heads/develop Commit: fe6171237e09cb1f347dc6577800ca4c665e361e Parents: ada9ced Author: venkatconflux <[email protected]> Authored: Fri Jul 29 18:22:46 2016 +0530 Committer: venkatconflux <[email protected]> Committed: Mon Aug 1 17:07:34 2016 +0530 ---------------------------------------------------------------------- api-docs/apiLive.htm | 90 ++- .../LoanRescheduleRequestTest.java | 2 +- .../data/LoanTermVariationsData.java | 6 +- .../data/LoanTermVariationsDataWrapper.java | 78 +- .../portfolio/loanaccount/domain/Loan.java | 55 +- .../domain/LoanAccountDomainService.java | 2 + .../domain/LoanAccountDomainServiceJpa.java | 23 +- .../LoanRepaymentScheduleInstallment.java | 6 + ...RescheduleRequestToTermVariationMapping.java | 53 ++ .../domain/LoanTermVariationType.java | 34 +- .../loanaccount/domain/LoanTermVariations.java | 43 +- .../loanaccount/domain/LoanTransaction.java | 6 + .../domain/AbstractLoanScheduleGenerator.java | 750 ++++++++----------- ...ingBalanceInterestLoanScheduleGenerator.java | 6 +- .../FlatInterestLoanScheduleGenerator.java | 8 +- .../domain/LoanApplicationTerms.java | 217 +++++- .../domain/LoanScheduleGenerator.java | 25 +- .../service/LoanScheduleAssembler.java | 20 +- ...nScheduleCalculationPlatformServiceImpl.java | 8 +- .../api/RescheduleLoansApiResource.java | 22 +- .../data/LoanRescheduleRequestData.java | 71 +- .../LoanRescheduleRequestDataValidator.java | 31 +- .../domain/DefaultLoanReschedulerFactory.java | 62 -- .../domain/LoanRescheduleRequest.java | 244 +++--- .../domain/LoanReschedulerFactory.java | 37 - .../LoanReschedulePreviewPlatformService.java | 4 +- ...oanReschedulePreviewPlatformServiceImpl.java | 187 +++-- ...escheduleRequestReadPlatformServiceImpl.java | 71 +- ...scheduleRequestWritePlatformServiceImpl.java | 488 ++++++------ .../loanproduct/service/LoanEnumerations.java | 16 + .../core_db/V313__multi_rescheduling_script.sql | 141 ++++ 31 files changed, 1496 insertions(+), 1310 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/api-docs/apiLive.htm ---------------------------------------------------------------------- diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 696e066..7fe3fc9 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -11877,11 +11877,11 @@ Content-Type: application/json Request Body: { "loanId": 1, "graceOnPrincipal": 2, - "rescheduleFromDate": "04 December 2014", - "adjustedDueDate": "20 December 2014", + "rescheduleFromDate": "25 December 2013", + "adjustedDueDate": "31 December 2013", "dateFormat": "dd MMMM yyyy", "locale": "en", - "submittedOnDate": "04 September 2014", + "submittedOnDate": "04 September 2013", "rescheduleReasonComment" : "Client has gone AWOL", "rescheduleReasonId": 1 } @@ -11907,52 +11907,80 @@ GET https://DomainName/api/v1/rescheduleloans/{requestId} </code> <code class="method-response"> { - "id": 2, - "loanId": 17, - "clientId": 4, - "clientName": "Fernando Michael Torres", - "loanAccountNumber": "000000017", + "id": 1, + "loanId": 1, + "clientId": 1, + "clientName": "test test", + "loanAccountNumber": "000000001", "statusEnum": { - "id": 500, - "code": "loanStatusType.rejected", - "value": "Rejected", - "pendingApproval": false, + "id": 100, + "code": "loanStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "pendingApproval": true, "approved": false, - "rejected": true + "rejected": false }, "rescheduleFromInstallment": 5, - "graceOnPrincipal": 3, "rescheduleFromDate": [ 2013, 12, - 6 + 25 ], "recalculateInterest": false, "rescheduleReasonCodeValue": { - "id": 239, - "name": "client cannot pay on time" + "id": 1, + "name": "Passport", + "isActive": false }, "timeline": { "submittedOnDate": [ - 2014, - 8, + 2013, + 9, 4 ], "submittedByUsername": "mifos", "submittedByFirstname": "App", - "submittedByLastname": "Administrator", - "rejectedOnDate": [ - 2014, - 8, - 5 - ], - "rejectedByUsername": "mifos", - "rejectedByFirstname": "App", - "rejectedByLastname": "Administrator" + "submittedByLastname": "Administrator" }, - "rescheduleReasonComment": "" -} - </code> + "rescheduleReasonComment": "Client has gone AWOL", + "loanTermVariationsData": [ + { + "id": 13, + "termType": { + "id": 4, + "code": "loanTermType.dueDate", + "value": "dueDate" + }, + "termVariationApplicableFrom": [ + 2013, + 12, + 25 + ], + "dateValue": [ + 2013, + 12, + 31 + ], + "isSpecificToInstallment": false + }, + { + "id": 14, + "termType": { + "id": 8, + "code": "loanTermType.graceOnPrincipal", + "value": "graceOnPrincipal" + }, + "termVariationApplicableFrom": [ + 2013, + 12, + 25 + ], + "decimalValue": 2, + "isSpecificToInstallment": false + } + ] +} +</code> </div> </div> <a id="loan_reschedule_preview_retrieve" name="loan_reschedule_preview_retrieve" class="old-syle-anchor"> </a> http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java index 2b00a98..ae39433 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java @@ -211,7 +211,7 @@ public class LoanRescheduleRequestTest { final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId); final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment"); - assertEquals("NUMBER OF REPAYMENTS SHOULD BE 16, NOT 12", "16", numberOfRepayments.toString()); + assertEquals("NUMBER OF REPAYMENTS SHOULD BE 16, NOT 12", "12", numberOfRepayments.toString()); assertEquals("TOTAL EXPECTED REPAYMENT MUST BE EQUAL TO 118000.0", "118000.0", totalExpectedRepayment.toString()); System.out.println("Successfully approved loan reschedule request (ID: " + this.loanRescheduleRequestId + ")"); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java index f5d2cc0..a19d3b1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java @@ -29,7 +29,7 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData @SuppressWarnings("unused") private final Long id; private final EnumOptionData termType; - private final LocalDate termVariationApplicableFrom; + private LocalDate termVariationApplicableFrom; private final BigDecimal decimalValue; private final LocalDate dateValue; private final boolean isSpecificToInstallment; @@ -113,5 +113,9 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData } return comparsion; } + + public void setApplicableFromDate(final LocalDate applicableFromDate) { + this.termVariationApplicableFrom = applicableFromDate; + } } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java index 65730e7..ea784e3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java @@ -22,37 +22,27 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; - import org.joda.time.LocalDate; public class LoanTermVariationsDataWrapper { private final List<LoanTermVariationsData> exceptionData; - private final ListIterator<LoanTermVariationsData> iterator; + private ListIterator<LoanTermVariationsData> iterator; private final List<LoanTermVariationsData> interestRateChanges; + private final List<LoanTermVariationsData> interestRateFromInstallment; private final List<LoanTermVariationsData> dueDateVariation; - private final ListIterator<LoanTermVariationsData> dueDateIterator; + private ListIterator<LoanTermVariationsData> dueDateIterator; public LoanTermVariationsDataWrapper(final List<LoanTermVariationsData> exceptionData) { if (exceptionData == null) { this.exceptionData = new ArrayList<>(1); } else { this.exceptionData = exceptionData; - Collections.sort(this.exceptionData); } this.interestRateChanges = new ArrayList<>(); this.dueDateVariation = new ArrayList<>(); - for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) { - if (loanTermVariationsData.getTermVariationType().isInterestRateVariation()) { - this.interestRateChanges.add(loanTermVariationsData); - } else if (loanTermVariationsData.getTermVariationType().isDueDateVariation()) { - this.dueDateVariation.add(loanTermVariationsData); - } - } - this.exceptionData.removeAll(this.interestRateChanges); - this.exceptionData.removeAll(this.dueDateVariation); - iterator = this.exceptionData.listIterator(); - dueDateIterator = this.dueDateVariation.listIterator(); + this.interestRateFromInstallment = new ArrayList<>(); + deriveLoanTermVariations(); } public boolean hasVariation(final LocalDate date) { @@ -84,6 +74,10 @@ public class LoanTermVariationsDataWrapper { public LoanTermVariationsData nextDueDateVariation() { return this.dueDateIterator.next(); } + + public LoanTermVariationsData previousDueDateVariation() { + return this.dueDateIterator.previous(); + } public List<LoanTermVariationsData> getInterestRateChanges() { return this.interestRateChanges; @@ -96,6 +90,23 @@ public class LoanTermVariationsDataWrapper { public List<LoanTermVariationsData> getExceptionData() { return this.exceptionData; } + + public void setExceptionData(final List<LoanTermVariationsData> exceptionData) { + clearTerms(); + this.exceptionData.addAll(exceptionData); + deriveLoanTermVariations(); + } + + public void clearTerms() { + this.exceptionData.clear(); + this.interestRateChanges.clear(); + this.dueDateVariation.clear(); + this.interestRateFromInstallment.clear(); + } + + public List<LoanTermVariationsData> getInterestRateFromInstallment() { + return this.interestRateFromInstallment; + } public int adjustNumberOfRepayments() { int repaymetsForAdjust = 0; @@ -108,7 +119,7 @@ public class LoanTermVariationsDataWrapper { } return repaymetsForAdjust; } - + public LoanTermVariationsData fetchLoanTermDueDateVariationsData(final LocalDate onDate) { LoanTermVariationsData data = null; for (LoanTermVariationsData termVariationsData : this.dueDateVariation) { @@ -125,4 +136,39 @@ public class LoanTermVariationsDataWrapper { return hasNext(date, iterator); } + public void updateLoanTermVariationsData(final List<LoanTermVariationsData> exceptionData){ + if(this.exceptionData != null && exceptionData != null && exceptionData.size() > 0){ + this.exceptionData.addAll(exceptionData); + deriveLoanTermVariations(); + } + } + + private void deriveLoanTermVariations() { + Collections.sort(this.exceptionData); + for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) { + if (loanTermVariationsData.getTermVariationType().isInterestRateVariation()) { + this.interestRateChanges.add(loanTermVariationsData); + } else if (loanTermVariationsData.getTermVariationType().isDueDateVariation()) { + this.dueDateVariation.add(loanTermVariationsData); + } else if (loanTermVariationsData.getTermVariationType().isInterestRateFromInstallment()) { + this.interestRateFromInstallment.add(loanTermVariationsData); + } + } + Collections.sort(this.dueDateVariation); + this.exceptionData.removeAll(this.interestRateChanges); + this.exceptionData.removeAll(this.dueDateVariation); + this.exceptionData.removeAll(this.interestRateFromInstallment); + this.iterator = this.exceptionData.listIterator(); + this.dueDateIterator = this.dueDateVariation.listIterator(); + } + + public void resetVariations(){ + + for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) { + loanTermVariationsData.setProcessed(false); + } + this.iterator = this.exceptionData.listIterator(); + this.dueDateIterator = this.dueDateVariation.listIterator(); + } + } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 5bd7a0b..adb516e 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -2734,6 +2734,8 @@ public class Loan extends AbstractPersistable<Long> { final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus); final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSAL_UNDO, getDisbursementDate()); + existingTransactionIds.addAll(findExistingTransactionIds()); + existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); if (!statusEnum.hasStateOf(currentStatus)) { this.loanStatus = statusEnum.getValue(); actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); @@ -2750,6 +2752,7 @@ public class Loan extends AbstractPersistable<Long> { } } boolean isEmiAmountChanged = this.loanTermVariations.size() > 0; + updateLoanToPreDisbursalState(); if (isScheduleRegenerateRequired || isDisbursedAmountChanged || isEmiAmountChanged || this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { @@ -2760,14 +2763,14 @@ public class Loan extends AbstractPersistable<Long> { if (isDisbursedAmountChanged) { updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); } + }else if(isPeriodicAccrualAccountingEnabledOnLoanProduct()){ + for (final LoanRepaymentScheduleInstallment period : getRepaymentScheduleInstallments()) { + period.resetAccrualComponents(); + } } actualChanges.put("actualDisbursementDate", ""); - existingTransactionIds.addAll(findExistingTransactionIds()); - existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - this.accruedTill = null; - reverseExistingTransactions(); updateLoanSummaryDerivedFields(); } @@ -2776,14 +2779,22 @@ public class Loan extends AbstractPersistable<Long> { } private final void reverseExistingTransactions() { - + Collection<LoanTransaction> retainTransactions = new ArrayList<>(); for (final LoanTransaction transaction : this.loanTransactions) { transaction.reverse(); + if(transaction.getId() != null){ + retainTransactions.add(transaction); + } } + this.loanTransactions.retainAll(retainTransactions); + isTransactionsListDirty = true ; } private void updateLoanToPreDisbursalState() { this.actualDisbursementDate = null; + + this.accruedTill = null; + reverseExistingTransactions(); for (final LoanCharge charge : charges()) { if (charge.isOverdueInstallmentCharge()) { @@ -2796,13 +2807,11 @@ public class Loan extends AbstractPersistable<Long> { for (final LoanRepaymentScheduleInstallment currentInstallment : installments) { currentInstallment.resetDerivedComponents(); } - final List<LoanTermVariations> removeTerms = new ArrayList<>(this.loanTermVariations.size()); for (LoanTermVariations variations : this.loanTermVariations) { if (variations.getOnLoanStatus().equals(LoanStatus.ACTIVE.getValue())) { - removeTerms.add(variations); + variations.markAsInactive(); } } - this.loanTermVariations.removeAll(removeTerms); final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), charges()); @@ -5024,7 +5033,7 @@ public class Loan extends AbstractPersistable<Long> { } - private ChangedTransactionDetail processTransactions() { + public ChangedTransactionDetail processTransactions() { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); @@ -5292,9 +5301,8 @@ public class Loan extends AbstractPersistable<Long> { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); - return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, charges(), generatorDTO.getHolidayDetailDTO(), - retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor, - getRepaymentScheduleInstallments(), generatorDTO.getRecalculateFrom()); + return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, this, generatorDTO.getHolidayDetailDTO(), + loanRepaymentScheduleTransactionProcessor, generatorDTO.getRecalculateFrom()); } public LoanRepaymentScheduleInstallment fetchPrepaymentDetail(final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate onDate) { @@ -5310,9 +5318,8 @@ public class Loan extends AbstractPersistable<Long> { final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); - installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, charges(), - scheduleGeneratorDTO.getHolidayDetailDTO(), retreiveListOfTransactionsPostDisbursementExcludeAccruals(), - loanRepaymentScheduleTransactionProcessor, getRepaymentScheduleInstallments()); + installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, this, + scheduleGeneratorDTO.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor); } else { installment = this.getTotalOutstandingOnLoan(); } @@ -5377,10 +5384,12 @@ public class Loan extends AbstractPersistable<Long> { return loanApplicationTerms; } - private BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate, + public BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate, List<LoanTermVariationsData> loanTermVariations) { for (LoanTermVariations variationTerms : this.loanTermVariations) { - loanTermVariations.add(variationTerms.toData()); + if(variationTerms.isActive()) { + loanTermVariations.add(variationTerms.toData()); + } } annualNominalInterestRate = constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations); return annualNominalInterestRate; @@ -6332,4 +6341,16 @@ public class Loan extends AbstractPersistable<Long> { return isForeClosure; } + public Set<LoanTermVariations> getActiveLoanTermVariations() { + Set<LoanTermVariations> retData = new HashSet<>(); + if(this.loanTermVariations != null && this.loanTermVariations.size() > 0){ + for (LoanTermVariations loanTermVariations : this.loanTermVariations) { + if(loanTermVariations.isActive()){ + retData.add(loanTermVariations); + } + } + } + return retData.size()>0?retData:null; + } + } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java index b1052c3..407a706 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java @@ -65,4 +65,6 @@ public interface LoanAccountDomainService { * @param loan {@link Loan} object */ void disableStandingInstructionsLinkedToClosedLoan(Loan loan); + + void recalculateAccruals(Loan loan, boolean isInterestCalcualtionHappened); } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index a46b43f..f7b1a6b 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -447,15 +447,21 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { */ @Override public void recalculateAccruals(Loan loan) { + boolean isInterestCalcualtionHappened = loan.repaymentScheduleDetail().isInterestRecalculationEnabled(); + recalculateAccruals(loan, isInterestCalcualtionHappened); + } + + @Override + public void recalculateAccruals(Loan loan, boolean isInterestCalcualtionHappened) { LocalDate accruedTill = loan.getAccruedTill(); + if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !isInterestCalcualtionHappened + || accruedTill == null || loan.isNpa() || !loan.status().isActive()) { return; } + boolean isOrganisationDateEnabled = this.configurationDomainService.isOrganisationstartDateEnabled(); Date organisationStartDate = new Date(); if(isOrganisationDateEnabled){ organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate(); } - - if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !loan.repaymentScheduleDetail().isInterestRecalculationEnabled() - || accruedTill == null || loan.isNpa() || !loan.status().isActive()) { return; } Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>(); List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments(); Long loanId = loan.getId(); @@ -697,17 +703,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } - private LoanRepaymentScheduleInstallment fetchLoanRepaymentScheduleInstallment(LocalDate fromDate, final Loan loan) { - LoanRepaymentScheduleInstallment installment = null; - for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : loan.getRepaymentScheduleInstallments()) { - if (fromDate.equals(loanRepaymentScheduleInstallment.getFromDate())) { - installment = loanRepaymentScheduleInstallment; - break; - } - } - return installment; - } - private Map<BUSINESS_ENTITY, Object> constructEntityMap(final BUSINESS_ENTITY entityEvent, Object entity) { Map<BUSINESS_ENTITY, Object> map = new HashMap<>(1); map.put(entityEvent, entity); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java index d95d741..c31d1ec 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java @@ -344,6 +344,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus this.obligationsMet = false; this.obligationsMetOnDate = null; } + + public void resetAccrualComponents() { + this.interestAccrued = null; + this.feeAccrued = null; + this.penaltyAccrued = null; + } public Money payPenaltyChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) { http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRescheduleRequestToTermVariationMapping.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRescheduleRequestToTermVariationMapping.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRescheduleRequestToTermVariationMapping.java new file mode 100644 index 0000000..af9a8e2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRescheduleRequestToTermVariationMapping.java @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanaccount.domain; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name="m_loan_reschedule_request_term_variations_mapping") +public class LoanRescheduleRequestToTermVariationMapping extends AbstractPersistable<Long> { + + + @ManyToOne(optional = false, cascade = CascadeType.PERSIST) + @JoinColumn(name = "loan_term_variations_id", nullable = false) + private LoanTermVariations loanTermVariations; + + protected LoanRescheduleRequestToTermVariationMapping(){ + + } + + private LoanRescheduleRequestToTermVariationMapping(final LoanTermVariations loanTermVariations) { + this.loanTermVariations = loanTermVariations; + } + + public static LoanRescheduleRequestToTermVariationMapping createNew(final LoanTermVariations loanTermVariation) { + return new LoanRescheduleRequestToTermVariationMapping(loanTermVariation); + } + + public LoanTermVariations getLoanTermVariations() { + return this.loanTermVariations; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java index c0df3bb..0b12182 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java @@ -26,7 +26,11 @@ public enum LoanTermVariationType { PRINCIPAL_AMOUNT(3, "loanTermType.principalAmount"), // DUE_DATE(4, "loanTermType.dueDate"), // INSERT_INSTALLMENT(5, "loanTermType.insertInstallment"), // - DELETE_INSTALLMENT(6, "loanTermType.deleteInstallment"); + DELETE_INSTALLMENT(6, "loanTermType.deleteInstallment"), + GRACE_ON_INTEREST(7, "loanTermType.graceOnInterest"), + GRACE_ON_PRINCIPAL(8, "loanTermType.graceOnPrincipal"), + EXTEND_REPAYMENT_PERIOD(9, "loanTermType.extendRepaymentPeriod"), + INTEREST_RATE_FROM_INSTALLMENT(10, "loanTermType.interestRateFromInstallment"); private final Integer value; private final String code; @@ -66,6 +70,18 @@ public enum LoanTermVariationType { case 6: enumeration = LoanTermVariationType.DELETE_INSTALLMENT; break; + case 7: + enumeration = LoanTermVariationType.GRACE_ON_INTEREST; + break; + case 8: + enumeration = LoanTermVariationType.GRACE_ON_PRINCIPAL; + break; + case 9: + enumeration = LoanTermVariationType.EXTEND_REPAYMENT_PERIOD; + break; + case 10: + enumeration = LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT; + break; } return enumeration; } @@ -93,4 +109,20 @@ public enum LoanTermVariationType { public boolean isDeleteInstallment() { return this.value.equals(LoanTermVariationType.DELETE_INSTALLMENT.getValue()); } + + public boolean isGraceOnInterest() { + return this.value.equals(LoanTermVariationType.GRACE_ON_INTEREST.getValue()); + } + + public boolean isGraceOnPrincipal() { + return this.value.equals(LoanTermVariationType.GRACE_ON_PRINCIPAL.getValue()); + } + + public boolean isExtendRepaymentPeriod() { + return this.value.equals(LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue()); + } + + public boolean isInterestRateFromInstallment() { + return this.value.equals(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getValue()); + } } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java index 01245b9..6dbadad 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java @@ -23,12 +23,13 @@ import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; - import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations; @@ -62,6 +63,13 @@ public class LoanTermVariations extends AbstractPersistable<Long> { @Column(name = "applied_on_loan_status", nullable = false) private Integer onLoanStatus; + + @Column(name = "is_active", nullable = false) + private Boolean isActive; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parent_id") + private LoanTermVariations parent; public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue, final boolean isSpecificToInstallment, final Loan loan) { @@ -72,6 +80,8 @@ public class LoanTermVariations extends AbstractPersistable<Long> { this.dateValue = dateValue; this.isSpecificToInstallment = isSpecificToInstallment; this.onLoanStatus = loan.status().getValue(); + this.isActive = true; + this.parent = null; } public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue, @@ -83,6 +93,21 @@ public class LoanTermVariations extends AbstractPersistable<Long> { this.dateValue = dateValue; this.isSpecificToInstallment = isSpecificToInstallment; this.onLoanStatus = loanStatus; + this.isActive = true; + this.parent = null; + } + + public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue, + final boolean isSpecificToInstallment, final Loan loan, final Integer loanStatus, final Boolean isActive, final LoanTermVariations parent) { + this.loan = loan; + this.termApplicableFrom = termApplicableFrom; + this.termType = termType; + this.decimalValue = decimalValue; + this.dateValue = dateValue; + this.isSpecificToInstallment = isSpecificToInstallment; + this.onLoanStatus = loanStatus; + this.isActive = isActive; + this.parent = parent; } protected LoanTermVariations() { @@ -135,5 +160,21 @@ public class LoanTermVariations extends AbstractPersistable<Long> { public Integer getOnLoanStatus() { return this.onLoanStatus; } + + public Boolean isActive() { + return this.isActive; + } + + public LoanTermVariations parent() { + return this.parent; + } + + public void updateIsActive(final Boolean isActive){ + this.isActive = isActive; + } + + public void markAsInactive() { + this.isActive = false; + } } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java index 414ca14..7b16a5c 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java @@ -777,4 +777,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> { public boolean isAccrualTransaction() { return isAccrual(); } + + public boolean isPaymentTransaction() { + return this.isNotReversed() + && !(this.isDisbursement() || this.isAccrual() || this.isRepaymentAtDisbursement() || this.isNonMonetaryTransaction() || this + .isIncomePosting()); + } } \ No newline at end of file
