Remove disbursement details in undo loan disbursal action (#2609)

Co-authored-by: Jose Alberto Hernandez <alberto@MacBook-Pro.local>
This commit is contained in:
José Alberto Hernández
2022-09-27 13:15:59 -05:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent ee0184c0a5
commit d4517f4a4d
16 changed files with 307 additions and 69 deletions
@@ -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");
@@ -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";
@@ -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;
@@ -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) {
@@ -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;
}
}
@@ -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> {
}
@@ -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 <> ? )");
@@ -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());
}
@@ -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));
}
@@ -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")
@@ -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>
@@ -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>