From db35ed06adb2057925d1a52f7b2c9b8c509138a7 Mon Sep 17 00:00:00 2001 From: Farooq Date: Thu, 19 Dec 2024 19:12:53 +0100 Subject: [PATCH 1/6] [SU-457] Anulacion de Credito --- .../insurance/domain/InsuranceIncident.java | 3 ++ .../domain/InsuranceIncidentType.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 23 +++++++--- .../db/changelog/tenant/changelog-tenant.xml | 1 + ...000_SU_457_insurance_incident_addition.xml | 43 +++++++++++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/20240828220000_SU_457_insurance_incident_addition.xml diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncident.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncident.java index 9a0c95b7cfb..12201292caf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncident.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncident.java @@ -60,4 +60,7 @@ public InsuranceIncidentData toData() { return InsuranceIncidentData.instance(this.getId(), this.name, this.isMandatory, this.isVoluntary, code, value, null); } + public boolean isValid() { + return this.isMandatory || this.isVoluntary; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncidentType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncidentType.java index d4eeb498e73..36284209a98 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncidentType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncidentType.java @@ -24,7 +24,9 @@ public enum InsuranceIncidentType { PERMANENT_CANCELLATION_DUE_TO_MAX_AGE(9, "labels.inputs.insurance.incident.permanent.cancellation.max.age", "Cancelación definitiva por edad máxima de permanencia"), // DEATH_CANCELLATION(10, "labels.inputs.insurance.incident.death.cancellation", "Cancelación definitiva por fallecimiento"), // - SUSPENSION_REMOVED(11, "labels.inputs.insurance.incident.removed.suspension", "Salida de suspensión"); // + SUSPENSION_REMOVED(11, "labels.inputs.insurance.incident.removed.suspension", "Salida de suspensión"), // + DEFINITIVE_FINAL_INVALIDATION(12, "labels.inputs.insurance.incident.final.annulment", + "Anulación definitiva por invalidez del contrato");// private final Integer value; private final String code; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 5f0ebc85073..5944ecccddd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -1352,12 +1352,22 @@ private static void handleOverPaidException(String totalOverpaid) { } private void createCancellationNoveltyNews(Loan loan, LocalDate writeOffDate) { - InsuranceIncident incident = this.insuranceIncidentRepository - .findByIncidentType(InsuranceIncidentType.DEFINITIVE_FINAL_CANCELLATION); - if (incident == null || (!incident.isMandatory() && !incident.isVoluntary())) { - throw new InsuranceIncidentNotFoundException(InsuranceIncidentType.DEFINITIVE_FINAL_CANCELLATION.name()); + createNoveltyNews(loan, writeOffDate, InsuranceIncidentType.DEFINITIVE_FINAL_CANCELLATION); + } + + private void createAnulacionNoveltyNews(Loan loan, LocalDate writeOffDate) { + // Implementation for creating the novelty "Anulación" + createNoveltyNews(loan, writeOffDate, InsuranceIncidentType.DEFINITIVE_FINAL_INVALIDATION); + } + + private void createNoveltyNews(Loan loan, LocalDate writeOffDate, InsuranceIncidentType incidentType) { + // Fetch the Insurance Incident based on the provided type + InsuranceIncident incident = this.insuranceIncidentRepository.findByIncidentType(incidentType); + if (incident == null || (!incident.isValid())) { + throw new InsuranceIncidentNotFoundException(incidentType.name()); } + // Iterate through loan charges and create novelty news as needed for (LoanCharge loanCharge : loan.getCharges()) { if (loanCharge.getAmountOutstanding(loan.getCurrency()).isGreaterThanZero()) { if ((incident.isMandatory() && loanCharge.isMandatoryInsurance()) @@ -3680,7 +3690,8 @@ public CommandProcessingResult cancelLoan(final Long loanId, final JsonCommand c * incident, transactionDate, cumulative); * this.insuranceIncidentNoveltyNewsRepository.saveAndFlush(insuranceIncidentNoveltyNews); } */ - createCancellationNoveltyNews(loan, transactionDate); + // Generate novelty "Anulación" + createAnulacionNoveltyNews(loan, transactionDate); if (transactionDate.equals(loan.getDisbursementDate())) { loan.setAnulado(true); loan.setAnuladoOnDisbursementDate(true); @@ -3689,6 +3700,8 @@ public CommandProcessingResult cancelLoan(final Long loanId, final JsonCommand c externalId, changes, false); final BlockingReasonSetting blockingReasonSetting = loanBlockingReasonRepository.getSingleBlockingReasonSettingByReason( BlockingReasonSettingEnum.CREDIT_ANULADO.getDatabaseString(), BlockLevel.CREDIT.toString()); + // not to mess with the record , we will just ensure it does not affect client level + blockingReasonSetting.setAffectsClientLevel(0); loanBlockWritePlatformService.blockLoan(loan.getId(), blockingReasonSetting, "Anulado", DateUtils.getLocalDateOfTenant()); final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder(); return commandProcessingResultBuilder.withLoanId(loanId).withEntityId(foreclosureTransaction.getId()) diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index d2eba3255d9..08aabf00b3e 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -335,4 +335,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/20240828220000_SU_457_insurance_incident_addition.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/20240828220000_SU_457_insurance_incident_addition.xml new file mode 100644 index 00000000000..b5e7dadf678 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/20240828220000_SU_457_insurance_incident_addition.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + From 0c8957fd7941f399f078c05058541870f8a16e3e Mon Sep 17 00:00:00 2001 From: Faheem Ahmad Date: Fri, 20 Dec 2024 01:20:16 +0500 Subject: [PATCH 2/6] SU-516 (#1414) * SU-516 --- .../serialization/LoanEventApiJsonValidator.java | 5 ++++- .../LoanWritePlatformServiceJpaRepositoryImpl.java | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index 0710f03a93e..24a8c93aa5f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -290,7 +290,7 @@ public void validateNewRepaymentTransaction(final String json) { final Set transactionParameters = new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note", "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber", "loanId", "channelHash", "channelName", "pointOfSalesCode", "isImportedTransaction", "repaymentChannelId", - "repaymentBankId", "transactionProcessingStrategy", "clientIdNumber", "reduceInstallmentAmount")); + "repaymentBankId", "transactionProcessingStrategy", "clientIdNumber", "reduceInstallmentAmount", "honorariosAmount")); final Type typeOfMap = new TypeToken>() {}.getType(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters); @@ -305,6 +305,9 @@ public void validateNewRepaymentTransaction(final String json) { final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element); baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).notNull().positiveAmount(); + final BigDecimal honorariosAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("honorariosAmount", element); + baseDataValidator.reset().parameter("honorariosAmount").value(honorariosAmount).ignoreIfNull().zeroOrPositiveAmount(); + final String note = this.fromApiJsonHelper.extractStringNamed("note", element); baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 5f0ebc85073..f9b8f0bb507 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -1143,7 +1143,16 @@ public CommandProcessingResult makeLoanRepayment(final LoanTransactionType repay .findFirst(); final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); + + BigDecimal honoAmount = command.bigDecimalValueOfParameterNamed("honorariosAmount"); + if (honoAmount == null) { + honoAmount = BigDecimal.ZERO; + } + Money remainingAmount = Money.of(loan.getCurrency(), transactionAmount); + // SU-516 Transaction amount may contain hono amount as well. ReCalculate hono charge amount based on + // the actual transaction amount + remainingAmount = remainingAmount.minus(honoAmount); Integer installmentNumber = 0; // increment the batch id which will be used to delete the rows from db table when a transaction is // rollbacked. The rows with highest version will be roll backed From e2466c85369695cf8115cb24dd3973006e6c6e4e Mon Sep 17 00:00:00 2001 From: Farooq Date: Fri, 20 Dec 2024 00:58:51 +0100 Subject: [PATCH 3/6] =?UTF-8?q?[SU-535]=20Generate=20"CANCELACI=C3=93N=20D?= =?UTF-8?q?EFINITIVA=20POR=20REESTRUCTURACI=C3=93N"=20Event=20on=20Top-Up?= =?UTF-8?q?=20Loan=20Closure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/InsuranceIncidentType.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 9 +++- .../db/changelog/tenant/changelog-tenant.xml | 1 + ...000_SU_535_insurance_incident_addition.xml | 43 +++++++++++++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/20240828220000_SU_535_insurance_incident_addition.xml diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncidentType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncidentType.java index 36284209a98..09973c3075f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncidentType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/insurance/domain/InsuranceIncidentType.java @@ -26,7 +26,9 @@ public enum InsuranceIncidentType { DEATH_CANCELLATION(10, "labels.inputs.insurance.incident.death.cancellation", "Cancelación definitiva por fallecimiento"), // SUSPENSION_REMOVED(11, "labels.inputs.insurance.incident.removed.suspension", "Salida de suspensión"), // DEFINITIVE_FINAL_INVALIDATION(12, "labels.inputs.insurance.incident.final.annulment", - "Anulación definitiva por invalidez del contrato");// + "Anulación definitiva por invalidez del contrato"), // + DEFINITIVE_RESTRUCTURING_CANCELLATION(13, "labels.inputs.insurance.incident.definitive.restructuring.cancellation", + "Cancelación definitiva por reestructuración"); // private final Integer value; private final String code; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 4fb84ab4470..3b2407afc71 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -576,6 +576,7 @@ public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand // the existing loan amountToDisburse = disburseAmount.minus(loanOutstanding); } + createRestructuringCancellationEvent(loanToClose); // Generate the event disburseLoanToLoan(loan, command, loanOutstanding, loanToClose); } @@ -1369,7 +1370,7 @@ private void createAnulacionNoveltyNews(Loan loan, LocalDate writeOffDate) { createNoveltyNews(loan, writeOffDate, InsuranceIncidentType.DEFINITIVE_FINAL_INVALIDATION); } - private void createNoveltyNews(Loan loan, LocalDate writeOffDate, InsuranceIncidentType incidentType) { + private void createNoveltyNews(Loan loan, LocalDate transactionDate, InsuranceIncidentType incidentType) { // Fetch the Insurance Incident based on the provided type InsuranceIncident incident = this.insuranceIncidentRepository.findByIncidentType(incidentType); if (incident == null || (!incident.isValid())) { @@ -1383,7 +1384,7 @@ private void createNoveltyNews(Loan loan, LocalDate writeOffDate, InsuranceIncid || (incident.isVoluntary() && loanCharge.isVoluntaryInsurance())) { BigDecimal cumulative = BigDecimal.ZERO; InsuranceIncidentNoveltyNews insuranceIncidentNoveltyNews = InsuranceIncidentNoveltyNews.instance(loan, loanCharge, - null, incident, writeOffDate, cumulative); + null, incident, transactionDate, cumulative); this.insuranceIncidentNoveltyNewsRepository.saveAndFlush(insuranceIncidentNoveltyNews); } @@ -2528,6 +2529,10 @@ public CommandProcessingResult closeAsRescheduled(final Long loanId, final JsonC .build(); } + private void createRestructuringCancellationEvent(Loan loan) { + createNoveltyNews(loan, DateUtils.getBusinessLocalDate(), InsuranceIncidentType.DEFINITIVE_RESTRUCTURING_CANCELLATION); + } + private void disburseLoanToLoan(final Loan loan, final JsonCommand command, final BigDecimal amount, final Loan loanToClose) { final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 08aabf00b3e..421535bd078 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -336,4 +336,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/20240828220000_SU_535_insurance_incident_addition.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/20240828220000_SU_535_insurance_incident_addition.xml new file mode 100644 index 00000000000..54ad317db6b --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/20240828220000_SU_535_insurance_incident_addition.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + From af17b5624733b56e732a7b386d29126a87bc5e1f Mon Sep 17 00:00:00 2001 From: tabrez-fiter Date: Fri, 20 Dec 2024 18:13:29 +0800 Subject: [PATCH 4/6] SU-537 : Archivo de cartera --- .../ArchiveLoansHistoryTasklet.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/archiveloanhistory/ArchiveLoansHistoryTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/archiveloanhistory/ArchiveLoansHistoryTasklet.java index 4e07452dced..3fe37579f20 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/archiveloanhistory/ArchiveLoansHistoryTasklet.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/archiveloanhistory/ArchiveLoansHistoryTasklet.java @@ -250,6 +250,11 @@ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkCon estadoCliente = ClientStatus.fromInt(loan.getClient().getStatus()).name(); } + BigDecimal waivedFees = currentInstallment.getFeeChargesWaived(loan.getCurrency()).getAmount(); + BigDecimal waivedInterest = currentInstallment.getInterestWaived(loan.getCurrency()).getAmount(); + BigDecimal waivedPenalties = currentInstallment.getPenaltyChargesWaived(loan.getCurrency()).getAmount(); + BigDecimal totalWaived = waivedFees.add(waivedInterest).add(waivedPenalties); + if (existingLoanArchive.isPresent()) { LoanArchiveHistory existingEntry = existingLoanArchive.get(); @@ -292,7 +297,8 @@ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkCon existingEntry.setSegurosVoluntarios(voluntaryInsuranceAmount); existingEntry.setPeriodicidad(PeriodFrequencyType.fromInt(loan.getTermPeriodFrequencyType()).name()); existingEntry.setEmpresaReporta("INTERCREDITO"); - existingEntry.setAbono(BigDecimal.ZERO); + existingEntry.setAbono(currentInstallment.getTotalPaid(loan.getCurrency()).getAmount()); + existingEntry.setCondonaciones(totalWaived); existingEntry.setActividadLaboral(actividadLaboral); existingEntry.setNumeroDeReprogramaciones(numberReschedule); existingEntry.setCreSaldo(creSaldo); @@ -358,7 +364,8 @@ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkCon loanArchiveHistory.setSegurosVoluntarios(voluntaryInsuranceAmount); loanArchiveHistory.setPeriodicidad(PeriodFrequencyType.fromInt(loan.getTermPeriodFrequencyType()).name()); loanArchiveHistory.setEmpresaReporta("INTERCREDITO"); - loanArchiveHistory.setAbono(BigDecimal.ZERO); + loanArchiveHistory.setAbono(currentInstallment.getTotalPaid(loan.getCurrency()).getAmount()); + loanArchiveHistory.setCondonaciones(totalWaived); loanArchiveHistory.setActividadLaboral(actividadLaboral); loanArchiveHistory.setNumeroDeReprogramaciones(numberReschedule); loanArchiveHistory.setCreSaldo(creSaldo); From 3fe828b4dbd2729a7eeed6cf72f0f9b73d5f221f Mon Sep 17 00:00:00 2001 From: Farooq Date: Fri, 20 Dec 2024 20:08:21 +0100 Subject: [PATCH 5/6] =?UTF-8?q?[SU-535]=20Generate=20"CANCELACI=C3=93N=20D?= =?UTF-8?q?EFINITIVA=20POR=20REESTRUCTURACI=C3=93N"=20Event=20on=20Top-Up?= =?UTF-8?q?=20Loan=20Closure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/LoanWritePlatformServiceJpaRepositoryImpl.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 3b2407afc71..a72b5aa8d7d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -576,7 +576,10 @@ public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand // the existing loan amountToDisburse = disburseAmount.minus(loanOutstanding); } - createRestructuringCancellationEvent(loanToClose); // Generate the event + if (!"castigado".equalsIgnoreCase(loanToClose.claimType())) { // Ensure the loan is not in castigado + // state + createRestructuringCancellationEvent(loanToClose); // Generate the event + } disburseLoanToLoan(loan, command, loanOutstanding, loanToClose); } From f8a6faef67da83325741b2befe90fddd33487852 Mon Sep 17 00:00:00 2001 From: Faheem Ahmad Date: Sat, 21 Dec 2024 19:15:48 +0500 Subject: [PATCH 6/6] SU-527 SU-530 (#1419) * SU-527 SU-530 --- .../portfolio/loanaccount/domain/Loan.java | 43 ++++++++++++++++++- .../LoanRepaymentScheduleInstallment.java | 15 +++++++ ...oanRepaymentScheduleProcessingWrapper.java | 2 +- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 4e2a077038a..7e132aa002b 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -1596,6 +1596,45 @@ public void updateLoanSchedule(final Collection scheduleInstallments = loanSchedule.getInstallments(); + List removeInstallments = new ArrayList<>(); + for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { + // Do not touch graced or fully paid installments + if (installment.getInstallmentNumber() == 0 || installment.isObligationsMet()) { + continue; + } + LoanRepaymentScheduleInstallment scheduledInstallment = findByInstallmentNumber(scheduleInstallments, + installment.getInstallmentNumber()); + if (scheduledInstallment != null) { + installment.updateComponents(scheduledInstallment, this.getCurrency()); + } else { + // This can only be possible in case of big advance payment which reduces the installment count + removeInstallments.add(installment); + } + } + // Check if there are extra installments created because of loan rescheduling then add those newely created + // installments + if (this.repaymentScheduleInstallments.size() < scheduleInstallments.size()) { + int newInstallmentsCount = scheduleInstallments.size() - this.repaymentScheduleInstallments.size(); + for (int i = newInstallmentsCount; i > 0; i--) { + int index = scheduleInstallments.size() - i; + addLoanRepaymentScheduleInstallment(scheduleInstallments.get(index)); + } + } + if (!removeInstallments.isEmpty()) { + this.repaymentScheduleInstallments.removeAll(removeInstallments); + } + } + private LoanRepaymentScheduleInstallment findByInstallmentNumber(Collection installments, Integer installmentNumber) { for (LoanRepaymentScheduleInstallment installment : installments) { @@ -6216,7 +6255,9 @@ public void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleG } // Either the installments got recalculated or the model if (loanSchedule.getInstallments() != null) { - updateLoanSchedule(loanSchedule.getInstallments()); + // SU-527 SU-530 using newely created method to update the loan schedule + // updateLoanSchedule(loanSchedule.getInstallments()); + updateLoanSchedule(loanSchedule); } else { updateLoanSchedule(loanSchedule.getLoanScheduleModel()); } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java index 5cfbadc1d30..9a32ca12694 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java @@ -1741,4 +1741,19 @@ public BigDecimal originalInterestChargedAmount() { public boolean isMigratedInstallment() { return isMigratedInstallment; } + + public void updateComponents(LoanRepaymentScheduleInstallment copy, MonetaryCurrency currency) { + this.setPrincipal(copy.getPrincipal(currency).getAmount()); + this.setRecalculateEMI(copy.recalculateEMI); + this.setRecalculatedInterestComponent(copy.recalculatedInterestComponent); + this.setAdvancePrincipalAmount(copy.getAdvancePrincipalAmount()); + this.setPenaltyCharges(copy.getPenaltyChargesCharged(currency).getAmount()); + this.setFeeChargesCharged(copy.getFeeChargesCharged(currency).getAmount()); + this.setInterestCharged(copy.getInterestCharged(currency).getAmount()); + if (copy.recalculatedInterestComponent) { + // This will only be true if loan is overpaid in advance. A new installment is created with only principal + // amount and all other null components + this.getInstallmentCharges().clear(); + } + } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java index 42b9a1421af..686f791889d 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java @@ -100,7 +100,7 @@ private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final L totalPrincipal, totalInstallments, outstandingBalance)); } else { if (loanCharge.getChargeCalculation().isFlatHono()) { - cumulative = Money.zero(monetaryCurrency); + cumulative = cumulative.plus(Money.zero(monetaryCurrency)); } else { cumulative = cumulative.plus(getInstallmentFee(monetaryCurrency, period, loanCharge)); }