You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by vi...@apache.org on 2022/09/27 18:16:05 UTC
[fineract] branch develop updated: Remove disbursement details in undo loan disbursal action (#2609)
This is an automated email from the ASF dual-hosted git repository.
victorromero pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new d4517f4a4 Remove disbursement details in undo loan disbursal action (#2609)
d4517f4a4 is described below
commit d4517f4a4d755872d6944709921d2b4d350d87d4
Author: José Alberto Hernández <44...@users.noreply.github.com>
AuthorDate: Tue Sep 27 13:15:59 2022 -0500
Remove disbursement details in undo loan disbursal action (#2609)
Co-authored-by: Jose Alberto Hernandez <al...@MacBook-Pro.local>
---
.../TellerManagementReadPlatformServiceImpl.java | 6 +-
.../loanaccount/api/LoanApiConstants.java | 1 +
.../loanaccount/api/LoansApiResourceSwagger.java | 2 +-
.../portfolio/loanaccount/domain/Loan.java | 83 +++++++------
.../domain/LoanDisbursementDetails.java | 14 ++-
.../domain/LoanDisbursementDetailsRepository.java | 27 +++++
.../service/LoanReadPlatformServiceImpl.java | 6 +-
.../loanaccount/service/LoanUtilService.java | 10 +-
.../LoanWritePlatformServiceJpaRepositoryImpl.java | 20 +++-
.../api/LoanProductsApiResourceSwagger.java | 2 +
.../db/changelog/tenant/changelog-tenant.xml | 1 +
.../0050_add_reverse_flag_disbursement_details.xml | 33 +++++
...rancheMultipleDisbursementsIntegrationTest.java | 11 +-
.../LoanDisbursementDetailsIntegrationTest.java | 133 ++++++++++++++++++---
.../common/loans/LoanProductTestBuilder.java | 2 +-
.../common/loans/LoanTransactionHelper.java | 25 ++++
16 files changed, 307 insertions(+), 69 deletions(-)
diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerManagementReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerManagementReadPlatformServiceImpl.java
index 120dbf132..da4cabcff 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerManagementReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerManagementReadPlatformServiceImpl.java
@@ -564,11 +564,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "start_date");
final LocalDate endDate = JdbcSupport.getLocalDate(rs, "end_date");
- final Integer fullDayFromDB = rs.getInt("full_day");
- Boolean fullDay = false;
- if (fullDayFromDB == 1) {
- fullDay = true;
- }
+ final Boolean fullDay = rs.getBoolean("full_day");
final String startTime = rs.getString("start_time");
final String endTime = rs.getString("end_time");
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
index 3b14b0660..47eb7d89e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
@@ -25,6 +25,7 @@ public interface LoanApiConstants {
String disbursementDataParameterName = "disbursementData";
String disbursementDateParameterName = "expectedDisbursementDate";
String disbursementPrincipalParameterName = "principal";
+ String disbursementReversedParameterName = "reversed";
String disbursementNetDisbursalAmountParameterName = "netDisbursalAmount";
String updatedDisbursementDateParameterName = "updatedExpectedDisbursementDate";
String updatedDisbursementPrincipalParameterName = "updatedPrincipal";
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index 8c082e213..1cffd9d25 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -959,7 +959,7 @@ final class LoansApiResourceSwagger {
public GetLoansLoanIdTimeline timeline;
public GetLoansLoanIdSummary summary;
public GetLoansLoanIdRepaymentSchedule repaymentSchedule;
- @Schema(description = "Set of GetLoansLoanIdDisbursementDetails")
+ @Schema(description = "Set of GetLoansLoanIdTransactions")
public Set<GetLoansLoanIdTransactions> transactions;
@Schema(description = "Set of GetLoansLoanIdDisbursementDetails")
public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
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 105fdbfe9..f3d72a97d 100644
--- 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
@@ -59,7 +59,6 @@ import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;
-import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
@@ -143,7 +142,6 @@ import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDat
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.stereotype.Component;
-@Slf4j
@Entity
@Component
@Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
@@ -916,7 +914,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
private Money getTotalAllTrancheDisbursementAmount() {
Money amount = Money.zero(getCurrency());
if (isMultiDisburmentLoan()) {
- for (final LoanDisbursementDetails loanDisbursementDetail : this.disbursementDetails) {
+ for (final LoanDisbursementDetails loanDisbursementDetail : getDisbursementDetails()) {
amount = amount.plus(loanDisbursementDetail.principal());
}
}
@@ -1133,7 +1131,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
loanCharge.update(this);
if (this.loanProduct.isMultiDisburseLoan() && loanCharge.isTrancheDisbursementCharge()) {
loanCharge.getTrancheDisbursementCharge().getloanDisbursementDetails().updateLoan(this);
- for (final LoanDisbursementDetails loanDisbursementDetails : this.disbursementDetails) {
+ for (final LoanDisbursementDetails loanDisbursementDetails : getDisbursementDetails()) {
if (loanCharge.getTrancheDisbursementCharge().getloanDisbursementDetails().getId() == null) {
if (loanCharge.getTrancheDisbursementCharge().getloanDisbursementDetails().equals(loanDisbursementDetails)) {
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, loanDisbursementDetails);
@@ -1600,7 +1598,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
if (loanProduct.isDisallowExpectedDisbursements()) {
- if (!disbursementDataArray.isEmpty()) {
+ if (disbursementDataArray != null && !disbursementDataArray.isEmpty()) {
final String errorMessage = "For this loan product, disbursement details are not allowed";
throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage);
}
@@ -1609,11 +1607,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final String errorMessage = "For this loan product, disbursement details must be provided";
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
}
- }
- if (disbursementDataArray.size() > loanProduct.maxTrancheCount()) {
- final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
- throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
- loanProduct.maxTrancheCount(), disbursementDetails.size());
+
+ if (disbursementDataArray.size() > loanProduct.maxTrancheCount()) {
+ final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
+ throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
+ loanProduct.maxTrancheCount(), disbursementDetails.size());
+ }
}
} else {
this.disbursementDetails.clear();
@@ -1905,7 +1904,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
if (loanDisbursementDetail.actualDisbursementDate() == null) {
LocalDate actualDisbursementDate = null;
LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate,
- principal, this.netDisbursalAmount);
+ principal, this.netDisbursalAmount, false);
disbursementDetails.updateLoan(this);
if (!loanDisbursementDetail.equals(disbursementDetails)) {
loanDisbursementDetail.copy(disbursementDetails);
@@ -1916,7 +1915,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} else {
LocalDate actualDisbursementDate = null;
LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate,
- principal, this.netDisbursalAmount);
+ principal, this.netDisbursalAmount, false);
disbursementDetails.updateLoan(this);
this.disbursementDetails.add(disbursementDetails);
for (LoanTrancheCharge trancheCharge : trancheCharges) {
@@ -1959,7 +1958,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public LoanDisbursementDetails fetchLoanDisbursementsById(Long id) {
LoanDisbursementDetails loanDisbursementDetail = null;
- for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetail : getDisbursementDetails()) {
if (id.equals(disbursementDetail.getId())) {
loanDisbursementDetail = disbursementDetail;
break;
@@ -1970,7 +1969,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
private List<Long> fetchDisbursementIds() {
List<Long> list = new ArrayList<>();
- for (LoanDisbursementDetails disbursementDetails : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetails : getDisbursementDetails()) {
list.add(disbursementDetails.getId());
}
return list;
@@ -2254,22 +2253,23 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
recalculateAllCharges();
if (loanProduct.isMultiDisburseLoan()) {
+ List<LoanDisbursementDetails> currentDisbursementDetails = getDisbursementDetails();
if (loanProduct.isDisallowExpectedDisbursements()) {
- if (!disbursementDetails.isEmpty()) {
+ if (!currentDisbursementDetails.isEmpty()) {
final String errorMessage = "For this loan product, disbursement details are not allowed";
throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage);
}
} else {
- if (disbursementDetails.isEmpty()) {
+ if (currentDisbursementDetails.isEmpty()) {
final String errorMessage = "For this loan product, disbursement details must be provided";
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
}
}
- if (this.disbursementDetails.size() > loanProduct.maxTrancheCount()) {
+ if (currentDisbursementDetails.size() > loanProduct.maxTrancheCount()) {
final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
- loanProduct.maxTrancheCount(), disbursementDetails.size());
+ loanProduct.maxTrancheCount(), currentDisbursementDetails.size());
}
}
this.approvedOnDate = approvedOn;
@@ -2463,7 +2463,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
private List<LoanDisbursementDetails> getDisbursedLoanDisbursementDetails() {
List<LoanDisbursementDetails> ret = new ArrayList<>();
if (this.disbursementDetails != null && this.disbursementDetails.size() > 0) {
- for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetail : getDisbursementDetails()) {
if (disbursementDetail.actualDisbursementDate() != null) {
ret.add(disbursementDetail);
}
@@ -2634,7 +2634,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
private Collection<LoanDisbursementDetails> fetchUndisbursedDetail() {
Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<>();
LocalDate date = null;
- for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetail : getDisbursementDetails()) {
if (disbursementDetail.actualDisbursementDate() == null) {
if (date == null || disbursementDetail.expectedDisbursementDate().compareTo(date) == 0 ? Boolean.TRUE : Boolean.FALSE) {
disbursementDetails.add(disbursementDetail);
@@ -2653,7 +2653,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
LoanDisbursementDetails details = null;
LocalDate date = this.actualDisbursementDate;
if (date != null) {
- for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetail : getDisbursementDetails()) {
if (disbursementDetail.actualDisbursementDate() != null) {
if (disbursementDetail.actualDisbursementDate().isAfter(date)
|| disbursementDetail.actualDisbursementDate().compareTo(date) == 0 ? Boolean.TRUE : Boolean.FALSE) {
@@ -2668,7 +2668,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
private boolean isDisbursementMissed() {
boolean isDisbursementMissed = false;
- for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetail : getDisbursementDetails()) {
if (disbursementDetail.actualDisbursementDate() == null
&& DateUtils.getBusinessLocalDate().isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate())) {
isDisbursementMissed = true;
@@ -2680,7 +2680,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public BigDecimal getDisbursedAmount() {
BigDecimal principal = BigDecimal.ZERO;
- for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetail : getDisbursementDetails()) {
if (disbursementDetail.actualDisbursementDate() != null) {
principal = principal.add(disbursementDetail.principal());
}
@@ -2689,7 +2689,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
}
private void removeDisbursementDetail() {
- Set<LoanDisbursementDetails> details = new HashSet<>(this.disbursementDetails);
+ Set<LoanDisbursementDetails> details = new HashSet<>(getDisbursementDetails());
for (LoanDisbursementDetails disbursementDetail : details) {
if (disbursementDetail.actualDisbursementDate() == null) {
this.disbursementDetails.remove(disbursementDetail);
@@ -2699,10 +2699,15 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
private boolean isDisbursementAllowed() {
boolean isAllowed = false;
- for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
- if (disbursementDetail.actualDisbursementDate() == null) {
- isAllowed = true;
- break;
+ List<LoanDisbursementDetails> disbursementDetails = getDisbursementDetails();
+ if (disbursementDetails == null || disbursementDetails.size() == 0) {
+ isAllowed = true;
+ } else {
+ for (LoanDisbursementDetails disbursementDetail : disbursementDetails) {
+ if (disbursementDetail.actualDisbursementDate() == null) {
+ isAllowed = true;
+ break;
+ }
}
}
return isAllowed;
@@ -2710,7 +2715,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
private boolean atleastOnceDisbursed() {
boolean isDisbursed = false;
- for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetail : getDisbursementDetails()) {
if (disbursementDetail.actualDisbursementDate() != null) {
isDisbursed = true;
break;
@@ -2926,7 +2931,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
: Boolean.TRUE;
this.loanRepaymentScheduleDetail.setPrincipal(this.approvedPrincipal);
if (this.loanProduct.isMultiDisburseLoan()) {
- for (final LoanDisbursementDetails details : this.disbursementDetails) {
+ for (final LoanDisbursementDetails details : getDisbursementDetails()) {
details.updateActualDisbursementDate(null);
}
}
@@ -5094,10 +5099,20 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return list;
}
- public List<LoanDisbursementDetails> getDisbursementDetails() {
+ public List<LoanDisbursementDetails> getAllDisbursementDetails() {
return this.disbursementDetails;
}
+ public List<LoanDisbursementDetails> getDisbursementDetails() {
+ List<LoanDisbursementDetails> currentDisbursementDetails = new ArrayList<LoanDisbursementDetails>();
+ for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
+ if (!disbursementDetail.isReversed()) {
+ currentDisbursementDetails.add(disbursementDetail);
+ }
+ }
+ return currentDisbursementDetails;
+ }
+
public ChangedTransactionDetail updateDisbursementDateAndAmountForTranche(final LoanDisbursementDetails disbursementDetails,
final JsonCommand command, final Map<String, Object> actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final Locale locale = command.extractLocale();
@@ -5552,7 +5567,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
NthDayType nthDayType = null;
DayOfWeekType dayOfWeekType = null;
final List<DisbursementData> disbursementData = new ArrayList<>();
- for (LoanDisbursementDetails disbursementDetails : this.disbursementDetails) {
+ for (LoanDisbursementDetails disbursementDetails : getDisbursementDetails()) {
disbursementData.add(disbursementDetails.toData());
}
@@ -6215,7 +6230,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
charge.resetToOriginal(loanCurrency());
}
}
- for (final LoanDisbursementDetails details : this.disbursementDetails) {
+ for (final LoanDisbursementDetails details : getDisbursementDetails()) {
if (actualDisbursementDate.equals(details.actualDisbursementDate())) {
this.loanRepaymentScheduleDetail.setPrincipal(getDisbursedAmount().subtract(details.principal()));
details.updateActualDisbursementDate(null);
@@ -6278,7 +6293,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
* get the next repayment LocalDate for rescheduling at the time of disbursement
*/
public LocalDate getNextPossibleRepaymentDateForRescheduling() {
- List<LoanDisbursementDetails> loanDisbursementDetails = this.disbursementDetails;
+ List<LoanDisbursementDetails> loanDisbursementDetails = getDisbursementDetails();
LocalDate nextRepaymentDate = DateUtils.getBusinessLocalDate();
for (LoanDisbursementDetails loanDisbursementDetail : loanDisbursementDetails) {
if (loanDisbursementDetail.actualDisbursementDate() == null) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanDisbursementDetails.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanDisbursementDetails.java
index 8dca91eed..125552d7f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanDisbursementDetails.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanDisbursementDetails.java
@@ -49,16 +49,20 @@ public class LoanDisbursementDetails extends AbstractPersistableCustom {
@Column(name = "net_disbursal_amount", scale = 6, precision = 19)
private BigDecimal netDisbursalAmount;
+ @Column(name = "is_reversed", nullable = false)
+ private boolean reversed;
+
protected LoanDisbursementDetails() {
}
public LoanDisbursementDetails(final LocalDate expectedDisbursementDate, final LocalDate actualDisbursementDate,
- final BigDecimal principal, final BigDecimal netDisbursalAmount) {
+ final BigDecimal principal, final BigDecimal netDisbursalAmount, final boolean reversed) {
this.expectedDisbursementDate = expectedDisbursementDate;
this.actualDisbursementDate = actualDisbursementDate;
this.principal = principal;
this.netDisbursalAmount = netDisbursalAmount;
+ this.reversed = reversed;
}
public void updateLoan(final Loan loan) {
@@ -88,6 +92,7 @@ public class LoanDisbursementDetails extends AbstractPersistableCustom {
this.principal = disbursementDetails.principal;
this.expectedDisbursementDate = disbursementDetails.expectedDisbursementDate;
this.actualDisbursementDate = disbursementDetails.actualDisbursementDate;
+ this.reversed = disbursementDetails.reversed;
}
public LocalDate expectedDisbursementDate() {
@@ -142,4 +147,11 @@ public class LoanDisbursementDetails extends AbstractPersistableCustom {
this.principal = principal;
}
+ public void reverse() {
+ this.reversed = true;
+ }
+
+ public boolean isReversed() {
+ return reversed;
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanDisbursementDetailsRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanDisbursementDetailsRepository.java
new file mode 100644
index 000000000..77da6021f
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanDisbursementDetailsRepository.java
@@ -0,0 +1,27 @@
+/**
+ * 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 org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface LoanDisbursementDetailsRepository
+ extends JpaRepository<LoanDisbursementDetails, Long>, JpaSpecificationExecutor<LoanDisbursementDetails> {
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index e7ba8855b..9c0d3266e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -1628,7 +1628,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
public Collection<DisbursementData> retrieveLoanDisbursementDetails(final Long loanId) {
final LoanDisbursementDetailMapper rm = new LoanDisbursementDetailMapper(sqlGenerator);
final String sql = "select " + rm.schema()
- + " where dd.loan_id=? group by dd.id, lc.amount_waived_derived order by dd.expected_disburse_date";
+ + " where dd.loan_id=? and dd.is_reversed=false group by dd.id, lc.amount_waived_derived order by dd.expected_disburse_date";
return this.jdbcTemplate.query(sql, rm, loanId); // NOSONAR
}
@@ -1960,7 +1960,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
sqlBuilder.append(" left join m_floating_rates bfr on bfr.is_base_lending_rate = true");
sqlBuilder.append(" left join m_floating_rates_periods bfrp on bfr.id = bfrp.floating_rates_id and bfrp.created_date >= ?");
sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
- sqlBuilder.append(" and ml.is_npa = false ");
+ sqlBuilder.append(" and ml.is_npa = false and dd.is_reversed = false ");
sqlBuilder.append(" and ((");
sqlBuilder.append("ml.interest_recalculation_enabled = true ");
sqlBuilder.append(" and (ml.interest_recalcualated_on is null or ml.interest_recalcualated_on <> ?)");
@@ -2008,7 +2008,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
sqlBuilder.append(" left join m_floating_rates bfr on bfr.is_base_lending_rate = true");
sqlBuilder.append(" left join m_floating_rates_periods bfrp on bfr.id = bfrp.floating_rates_id and bfrp.created_date >= ?");
sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
- sqlBuilder.append(" and ml.is_npa = false ");
+ sqlBuilder.append(" and ml.is_npa = false and dd.is_reversed = false ");
sqlBuilder.append(" and ((");
sqlBuilder.append("ml.interest_recalculation_enabled = true ");
sqlBuilder.append(" and (ml.interest_recalcualated_on is null or ml.interest_recalcualated_on <> ? )");
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
index 55a24f4b6..dbac6b5a5 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
@@ -337,9 +337,13 @@ public class LoanUtilService {
netDisbursalAmount = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementNetDisbursalAmountParameterName)
.getAsBigDecimal();
}
-
- disbursementDatas.add(
- new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate, principal, netDisbursalAmount));
+ boolean isReversed = false;
+ if (jsonObject.has(LoanApiConstants.disbursementReversedParameterName)) {
+ isReversed = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.disbursementReversedParameterName,
+ jsonObject);
+ }
+ disbursementDatas.add(new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate, principal,
+ netDisbursalAmount, isReversed));
i++;
} while (i < disbursementDataArray.size());
}
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 19c9462b5..3a59b7a19 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
@@ -164,6 +164,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
@@ -269,7 +270,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanRepository loanRepository;
private final RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler;
private final PostDatedChecksRepository postDatedChecksRepository;
-
+ private final LoanDisbursementDetailsRepository loanDisbursementDetailsRepository;
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -325,9 +326,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// products
final LocalDate artificialExpectedDate = loan.getExpectedDisbursedOnLocalDate();
LoanDisbursementDetails disbursementDetail = new LoanDisbursementDetails(artificialExpectedDate, null,
- loan.getDisbursedAmount(), null);
+ loan.getDisbursedAmount(), null, false);
disbursementDetail.updateLoan(loan);
- loan.getDisbursementDetails().add(disbursementDetail);
+ loan.getAllDisbursementDetails().add(disbursementDetail);
}
}
loan.validateAccountStatus(LoanEvent.LOAN_DISBURSED);
@@ -869,6 +870,19 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
existingReversedTransactionIds, isAccountTransfer);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
+
+ // Remove All the Disbursement Details If the Loan Product is disabled and exists one
+ if (loan.loanProduct().isDisallowExpectedDisbursements()) {
+ if (!loan.getDisbursementDetails().isEmpty()) {
+ List<LoanDisbursementDetails> reversedDisbursementDetails = new ArrayList<LoanDisbursementDetails>();
+ for (LoanDisbursementDetails disbursementDetail : loan.getAllDisbursementDetails()) {
+ disbursementDetail.reverse();
+ reversedDisbursementDetails.add(disbursementDetail);
+ }
+ this.loanDisbursementDetailsRepository.saveAllAndFlush(reversedDisbursementDetails);
+ }
+ }
+
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoDisbursalBusinessEvent(loan));
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index 6d70ee447..04d604094 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -1237,6 +1237,8 @@ final class LoanProductsApiResourceSwagger {
@Schema(example = "50")
public Integer principalThresholdForLastInstalment;
public GetDelinquencyBucketsResponse delinquencyBucket;
+ @Schema(example = "true")
+ public Boolean disallowExpectedDisbursements;
}
@Schema(description = "PutLoanProductsProductIdRequest")
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 179aaf743..6d8ff7c18 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
@@ -69,4 +69,5 @@
<include file="parts/0047_add_loan_delinquency_tags_business_step.xml" relativeToChangelogFile="true"/>
<include file="parts/0048_rework_loan_account_locks.xml" relativeToChangelogFile="true"/>
<include file="parts/0049_add_send_asynchronous_events_job.xml" relativeToChangelogFile="true"/>
+ <include file="parts/0050_add_reverse_flag_disbursement_details.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0050_add_reverse_flag_disbursement_details.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0050_add_reverse_flag_disbursement_details.xml
new file mode 100644
index 000000000..fe24671a9
--- /dev/null
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0050_add_reverse_flag_disbursement_details.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+
+ <changeSet id="1" author="fineract">
+ <addColumn tableName="m_loan_disbursement_detail">
+ <column name="is_reversed" type="boolean" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
+ </addColumn>
+ </changeSet>
+</databaseChangeLog>
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanNonTrancheMultipleDisbursementsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanNonTrancheMultipleDisbursementsIntegrationTest.java
index c4646906e..2cf2d83ab 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanNonTrancheMultipleDisbursementsIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanNonTrancheMultipleDisbursementsIntegrationTest.java
@@ -25,6 +25,7 @@ import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.util.ArrayList;
import java.util.HashMap;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
@@ -75,7 +76,7 @@ public class ClientLoanNonTrancheMultipleDisbursementsIntegrationTest {
.withMultiDisburse() //
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true) //
.withMaxTrancheCount("30") //
- .withDisallowExpectectedDisbursements(true);
+ .withDisallowExpectedDisbursements(true);
if (isInterestRecalculationEnabled) {
final String interestRecalculationCompoundingMethod = LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE;
final String rescheduleStrategyMethod = LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS;
@@ -160,8 +161,12 @@ public class ClientLoanNonTrancheMultipleDisbursementsIntegrationTest {
LOG.info("-------------------------------DISBURSE non-tranch multi-disbursal loan ----------");
final String netDisbursedAmt = null;
- loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(submitDate, loanID, approved.toString(),
- netDisbursedAmt);
+ loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithTransactionAmount(submitDate, loanID, approved.toString());
+
+ GetLoansLoanIdResponse getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanID);
+ Assertions.assertNotNull(getLoansLoanIdResponse);
+
+ this.loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDisbursementDetailsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDisbursementDetailsIntegrationTest.java
index 6b6df5410..8c1cc4e74 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDisbursementDetailsIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDisbursementDetailsIntegrationTest.java
@@ -35,9 +35,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdResponse;
+import org.apache.fineract.client.models.PutLoansLoanIdResponse;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
import org.apache.fineract.integrationtests.common.Utils;
@@ -271,8 +274,6 @@ public class LoanDisbursementDetailsIntegrationTest {
@Test
public void validateEqualInstallmentsForMultiTrancheLoan() {
- List<HashMap> emptyTranches = new ArrayList<>();
-
final String operationDate = "01 January 2014";
final String principal = "1000";
final String disbursedPrincipal = "900";
@@ -284,7 +285,7 @@ public class LoanDisbursementDetailsIntegrationTest {
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
.withInterestTypeAsDecliningBalance() //
.withMultiDisburse() //
- .withDisallowExpectectedDisbursements(true) //
+ .withDisallowExpectedDisbursements(true) //
.build(null);
log.info("Product {}", loanProductJSON);
final Integer loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
@@ -294,7 +295,7 @@ public class LoanDisbursementDetailsIntegrationTest {
log.info("-------------------LOAN CREATED WITH loanId----------------- {}", loanId);
- this.loanTransactionHelper.approveLoanWithApproveAmount(operationDate, expectedDisbursementDate, principal, loanId, emptyTranches);
+ this.loanTransactionHelper.approveLoanWithApproveAmount(operationDate, expectedDisbursementDate, principal, loanId, null);
log.info("-------------------MULTI DISBURSAL LOAN APPROVED SUCCESSFULLY-------");
GetLoansLoanIdResponse getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
@@ -347,6 +348,101 @@ public class LoanDisbursementDetailsIntegrationTest {
this.editLoanDisbursementDetails();
}
+ @Test
+ public void allowModifyLoanApplicationAfterUndoDisbursalWithTranches() throws ParseException {
+ final String operationDate = this.approveDate;
+ List<HashMap> tranches = new ArrayList<>();
+ String principal = "1000";
+ final List<HashMap> collaterals = new ArrayList<>();
+
+ final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, operationDate);
+ log.info("---------------------------------CLIENT CREATED WITH ID--------------------------------------------------- {}", clientId);
+
+ final Integer loanProductId = this.loanTransactionHelper
+ .getLoanProductId(new LoanProductTestBuilder().withInterestTypeAsDecliningBalance().withTranches(true)
+ .withDisallowExpectedDisbursements(true).withInterestCalculationPeriodTypeAsRepaymentPeriod(true).build(null));
+ log.info("----------------------------------LOAN PRODUCT CREATED WITH ID------------------------------------------- {}",
+ loanProductId);
+ GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = this.loanTransactionHelper.getLoanProduct(loanProductId);
+ assertNotNull(getLoanProductsProductIdResponse);
+ log.info("Loan Product Id {} with DisallowExpectectedDisbursements in {}", loanProductId,
+ getLoanProductsProductIdResponse.getDisallowExpectedDisbursements());
+ assertEquals(true, getLoanProductsProductIdResponse.getDisallowExpectedDisbursements());
+
+ final Integer loanId = applyForLoanApplicationWithTranches(clientId, loanProductId, proposedAmount, tranches);
+ log.info("-----------------------------------LOAN CREATED WITH LOANID------------------------------------------------- {}", loanId);
+
+ loanTransactionHelper.approveLoanWithApproveAmount(operationDate, operationDate, approvalAmount, loanId, tranches);
+ GetLoansLoanIdResponse getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ log.info("Loan Id {} with Status {} with Disbursement details {}", getLoansLoanIdResponse.getId(),
+ getLoansLoanIdResponse.getStatus().getCode(), getLoansLoanIdResponse.getDisbursementDetails().size());
+ log.info("-------------------MULTI DISBURSAL LOAN APPROVED SUCCESSFULLY-------");
+ assertEquals(0, getLoansLoanIdResponse.getDisbursementDetails().size(), "Disbursement details items");
+
+ loanTransactionHelper.disburseLoanWithTransactionAmount(operationDate, loanId, principal);
+
+ getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ log.info("Loan Id {} with Status {} with Disbursement details {}", getLoansLoanIdResponse.getId(),
+ getLoansLoanIdResponse.getStatus().getCode(), getLoansLoanIdResponse.getDisbursementDetails().size());
+ log.info("-------------------MULTI DISBURSAL LOAN DISBURSED SUCCESSFULLY-------");
+ assertEquals(1, getLoansLoanIdResponse.getDisbursementDetails().size(), "Disbursement details items");
+
+ PostLoansLoanIdResponse postLoansLoanIdResponse = this.loanTransactionHelper.applyLoanCommand(loanId, "undoDisbursal");
+ assertNotNull(postLoansLoanIdResponse);
+ log.info("-------------------UNDO DISBURSAL LOAN SUCCESSFULLY-------");
+ getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ log.info("Loan Id {} with Status {} with Disbursement details {}", getLoansLoanIdResponse.getId(),
+ getLoansLoanIdResponse.getStatus().getCode(), getLoansLoanIdResponse.getDisbursementDetails().size());
+ assertEquals(0, getLoansLoanIdResponse.getDisbursementDetails().size(), "Disbursement details items");
+
+ getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ log.info("Loan Id {} with Status {} with Disbursement details {}", getLoansLoanIdResponse.getId(),
+ getLoansLoanIdResponse.getStatus().getCode(), getLoansLoanIdResponse.getDisbursementDetails().size());
+ assertEquals(0, getLoansLoanIdResponse.getDisbursementDetails().size(), "Disbursement details items");
+
+ postLoansLoanIdResponse = this.loanTransactionHelper.applyLoanCommand(loanId, "undoApproval");
+ assertNotNull(postLoansLoanIdResponse);
+ log.info("-------------------UNDO APPROVAL LOAN SUCCESSFULLY-------");
+
+ getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ log.info("Loan Id {} with Status {} with Disbursement details {}", getLoansLoanIdResponse.getId(),
+ getLoansLoanIdResponse.getStatus().getCode(), getLoansLoanIdResponse.getDisbursementDetails().size());
+
+ principal = "10000";
+ final String loanApplicationJSON = buildLoanApplicationJSON(clientId, loanProductId, principal, tranches, operationDate,
+ collaterals);
+ log.info("Modify Loan Application: {}", loanApplicationJSON);
+ PutLoansLoanIdResponse putLoansLoanIdResponse = this.loanTransactionHelper.modifyLoanApplication(loanId, loanApplicationJSON);
+ assertNotNull(putLoansLoanIdResponse);
+
+ getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ log.info("Loan Id {} with Status {} with Disbursement details {} and Principal {}", getLoansLoanIdResponse.getId(),
+ getLoansLoanIdResponse.getStatus().getCode(), getLoansLoanIdResponse.getDisbursementDetails().size(),
+ getLoansLoanIdResponse.getPrincipal());
+
+ // ReDo the Approval and Disbursement
+ loanTransactionHelper.approveLoanWithApproveAmount(operationDate, operationDate, approvalAmount, loanId, null);
+ getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ log.info("Loan Id {} with Status {} with Disbursement details {}", getLoansLoanIdResponse.getId(),
+ getLoansLoanIdResponse.getStatus().getCode(), getLoansLoanIdResponse.getDisbursementDetails().size());
+
+ loanTransactionHelper.disburseLoanWithTransactionAmount(operationDate, loanId, principal);
+
+ getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ log.info("Loan Id {} with Status {} with Disbursement details {}", getLoansLoanIdResponse.getId(),
+ getLoansLoanIdResponse.getStatus().getCode(), getLoansLoanIdResponse.getDisbursementDetails().size());
+ log.info("-------------------MULTI DISBURSAL LOAN DISBURSED SUCCESSFULLY-------");
+ assertEquals(1, getLoansLoanIdResponse.getDisbursementDetails().size(), "Disbursement details items");
+ }
+
private void editLoanDisbursementDetails() throws ParseException {
this.editDateAndPrincipalOfExistingTranche();
this.addNewDisbursementDetails();
@@ -412,17 +508,10 @@ public class LoanDisbursementDetailsIntegrationTest {
return date;
}
- private Integer applyForLoanApplicationWithTranches(final Integer clientId, final Integer loanProductId, String principal,
- List<HashMap> tranches) {
- log.info("----------------APPLYING FOR LOAN APPLICATION");
- List<HashMap> collaterals = new ArrayList<>();
- final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
- Assertions.assertNotNull(collateralId);
- final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec,
- clientId.toString(), collateralId);
- Assertions.assertNotNull(clientCollateralId);
- addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
- final String loanApplicationJSON = new LoanApplicationTestBuilder() //
+ private String buildLoanApplicationJSON(final Integer clientId, final Integer loanProductId, String principal, List<HashMap> tranches,
+ final String operationDate, List<HashMap> collaterals) {
+
+ return new LoanApplicationTestBuilder() //
.withPrincipal(principal) //
.withLoanTermFrequency("5") //
.withLoanTermFrequencyAsMonths() //
@@ -435,6 +524,20 @@ public class LoanDisbursementDetailsIntegrationTest {
.withInterestTypeAsDecliningBalance() //
.withSubmittedOnDate("01 March 2014") //
.withCollaterals(collaterals).build(clientId.toString(), loanProductId.toString(), null);
+ }
+
+ private Integer applyForLoanApplicationWithTranches(final Integer clientId, final Integer loanProductId, String principal,
+ List<HashMap> tranches) {
+ log.info("----------------APPLYING FOR LOAN APPLICATION");
+ List<HashMap> collaterals = new ArrayList<>();
+ final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
+ Assertions.assertNotNull(collateralId);
+ final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec,
+ clientId.toString(), collateralId);
+ Assertions.assertNotNull(clientCollateralId);
+ addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
+ final String loanApplicationJSON = buildLoanApplicationJSON(clientId, loanProductId, principal, tranches, "01 March 2014",
+ collaterals);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
index 936dee5f4..b11fa5f0b 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
@@ -394,7 +394,7 @@ public class LoanProductTestBuilder {
return this;
}
- public LoanProductTestBuilder withDisallowExpectectedDisbursements(boolean disallowExpectectedDisbursements) {
+ public LoanProductTestBuilder withDisallowExpectedDisbursements(boolean disallowExpectectedDisbursements) {
this.disallowExpectedDisbursements = disallowExpectectedDisbursements;
if (this.disallowExpectedDisbursements) {
this.allowApprovedDisbursedAmountsOverApplied = true;
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 890d2d6f2..96c51a0f5 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -45,7 +45,9 @@ import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
+import org.apache.fineract.client.models.PutLoansLoanIdResponse;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.integrationtests.common.CommonConstants;
import org.apache.fineract.integrationtests.common.Utils;
@@ -136,6 +138,12 @@ public class LoanTransactionHelper {
"/fineract-provider/api/v1/loans/" + id + "?" + Utils.TENANT_IDENTIFIER, loanApplicationJSON, "loanId");
}
+ public PutLoansLoanIdResponse modifyLoanApplication(final Integer id, final String loanApplicationJSON) {
+ final String response = Utils.performServerPut(this.requestSpec, this.responseSpec,
+ "/fineract-provider/api/v1/loans/" + id + "?" + Utils.TENANT_IDENTIFIER, loanApplicationJSON, null);
+ return GSON.fromJson(response, PutLoansLoanIdResponse.class);
+ }
+
public ArrayList getLoanRepaymentSchedule(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final Integer loanID) {
final String URL = "/fineract-provider/api/v1/loans/" + loanID + "?associations=repaymentSchedule&" + Utils.TENANT_IDENTIFIER;
@@ -319,6 +327,21 @@ public class LoanTransactionHelper {
getDisburseLoanAsJSON(date, null, netDisbursalAmount));
}
+ public PostLoansLoanIdResponse applyLoanCommand(final Integer loanId, final String command) {
+ String undoBodyJson = "{}";
+ String url = "";
+ if (command.equals(UNDO_APPROVAL_LOAN_COMMAND)) {
+ undoBodyJson = "{'note':'UNDO APPROVAL'}";
+ url = createLoanOperationURL(UNDO_APPROVAL_LOAN_COMMAND, loanId);
+ } else if (command.equals(UNDO_DISBURSE_LOAN_COMMAND)) {
+ undoBodyJson = "{'note' : 'UNDO DISBURSAL'}";
+ url = createLoanOperationURL(UNDO_DISBURSE_LOAN_COMMAND, loanId);
+ }
+ final String response = Utils.performServerPost(this.requestSpec, this.responseSpec, url, undoBodyJson, null);
+ log.info("Response {}", response);
+ return GSON.fromJson(response, PostLoansLoanIdResponse.class);
+ }
+
public HashMap undoDisbursal(final Integer loanID) {
final String undoDisburseJson = "{'note' : 'UNDO DISBURSAL'}";
log.info("IN DISBURSE LOAN");
@@ -807,6 +830,8 @@ public class LoanTransactionHelper {
}
private HashMap performLoanTransaction(final String postURLForLoanTransaction, final String jsonToBeSent) {
+ log.info("URL: {}", postURLForLoanTransaction);
+ log.info("Body: {}", jsonToBeSent);
final HashMap response = Utils.performServerPost(this.requestSpec, this.responseSpec, postURLForLoanTransaction, jsonToBeSent,
"changes");
return (HashMap) response.get("status");