FINERACT-2080: Extract DTO mappers from loan entity
This commit is contained in:
committed by
Adam Saghy
parent
b01dca3ecc
commit
2ba7e28108
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AccountingBridgeDataDTO {
|
||||
|
||||
private Long loanId;
|
||||
private Long loanProductId;
|
||||
private Long officeId;
|
||||
private String currencyCode;
|
||||
private BigDecimal calculatedInterest;
|
||||
private boolean cashBasedAccountingEnabled;
|
||||
private boolean upfrontAccrualBasedAccountingEnabled;
|
||||
private boolean periodicAccrualBasedAccountingEnabled;
|
||||
private boolean isAccountTransfer;
|
||||
private boolean isChargeOff;
|
||||
private boolean isFraud;
|
||||
private Long chargeOffReasonCodeValue;
|
||||
private List<AccountingBridgeLoanTransactionDTO> newLoanTransactions = new ArrayList<>();
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AccountingBridgeLoanTransactionDTO {
|
||||
|
||||
private Long id;
|
||||
private Long officeId;
|
||||
private LoanTransactionEnumData type;
|
||||
private boolean reversed;
|
||||
private LocalDate date;
|
||||
private String currencyCode;
|
||||
private BigDecimal amount;
|
||||
private BigDecimal netDisbursalAmount;
|
||||
private BigDecimal principalPortion;
|
||||
private BigDecimal interestPortion;
|
||||
private BigDecimal feeChargesPortion;
|
||||
private BigDecimal penaltyChargesPortion;
|
||||
private BigDecimal overPaymentPortion;
|
||||
private String chargeRefundChargeType;
|
||||
private Long paymentTypeId;
|
||||
private List<LoanChargePaidByDTO> loanChargesPaid = new ArrayList<>();
|
||||
private BigDecimal principalPaid;
|
||||
private BigDecimal feePaid;
|
||||
private BigDecimal penaltyPaid;
|
||||
private LoanChargeData loanChargeData;
|
||||
private boolean loanToLoanTransfer;
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class LoanChargePaidByDTO {
|
||||
|
||||
private Long chargeId;
|
||||
private Boolean isPenalty;
|
||||
private Long loanChargeId;
|
||||
private BigDecimal amount;
|
||||
private Integer installmentNumber;
|
||||
|
||||
}
|
||||
+30
-554
@@ -21,9 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.CUMULATIVE;
|
||||
import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Convert;
|
||||
@@ -43,20 +40,16 @@ import jakarta.persistence.UniqueConstraint;
|
||||
import jakarta.persistence.Version;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
@@ -67,61 +60,36 @@ import lombok.Setter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigurationServiceContainer;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.apache.fineract.organisation.staff.domain.Staff;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarWeekDaysType;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.collateral.domain.LoanCollateral;
|
||||
import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
|
||||
import org.apache.fineract.portfolio.common.domain.NthDayType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.apache.fineract.portfolio.fund.domain.Fund;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks;
|
||||
@@ -186,7 +154,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
@JoinColumn(name = "product_id", nullable = false)
|
||||
private LoanProduct loanProduct;
|
||||
|
||||
@ManyToOne(optional = true, fetch = FetchType.EAGER)
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "fund_id")
|
||||
private Fund fund;
|
||||
|
||||
@@ -246,7 +214,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
private LocalDate rejectedOnDate;
|
||||
|
||||
@Setter()
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY)
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rejectedon_userid")
|
||||
private AppUser rejectedBy;
|
||||
|
||||
@@ -255,7 +223,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
private LocalDate withdrawnOnDate;
|
||||
|
||||
@Setter()
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY)
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "withdrawnon_userid")
|
||||
private AppUser withdrawnBy;
|
||||
|
||||
@@ -264,7 +232,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
private LocalDate approvedOnDate;
|
||||
|
||||
@Setter()
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY)
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "approvedon_userid")
|
||||
private AppUser approvedBy;
|
||||
|
||||
@@ -277,7 +245,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
private LocalDate actualDisbursementDate;
|
||||
|
||||
@Setter()
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY)
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "disbursedon_userid")
|
||||
private AppUser disbursedBy;
|
||||
|
||||
@@ -286,7 +254,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
private LocalDate closedOnDate;
|
||||
|
||||
@Setter()
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY)
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "closedon_userid")
|
||||
private AppUser closedBy;
|
||||
|
||||
@@ -298,7 +266,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
@Column(name = "rescheduledon_date")
|
||||
private LocalDate rescheduledOnDate;
|
||||
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY)
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rescheduledon_userid")
|
||||
private AppUser rescheduledByUser;
|
||||
|
||||
@@ -396,7 +364,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
@Column(name = "total_recovered_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalRecovered;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
private LoanInterestRecalculationDetails loanInterestRecalculationDetails;
|
||||
|
||||
@Column(name = "is_npa", nullable = false)
|
||||
@@ -439,7 +407,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
@Column(name = "is_fraud", nullable = false)
|
||||
private boolean fraud = false;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
private LoanTopupDetails loanTopupDetails;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@@ -709,7 +677,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
* newly created transaction is also added to the Loan on which this method is called.
|
||||
*
|
||||
* If "suppliedTransactionDate" is not passed Id, the transaction date is set to the loans due date if the due date
|
||||
* is lesser than todays date. If not, the transaction date is set to todays date
|
||||
* is lesser than today's date. If not, the transaction date is set to today's date
|
||||
*/
|
||||
public LoanTransaction handleChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate) {
|
||||
if (isProgressiveSchedule()) {
|
||||
@@ -782,9 +750,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
if (transaction.isGreaterThan(chargeAmount)) {
|
||||
final Money principalPortion = Money.zero(currency);
|
||||
final Money interestPortion = Money.zero(currency);
|
||||
final Money penaltychargesPortion = Money.zero(currency);
|
||||
final Money penaltyChargesPortion = Money.zero(currency);
|
||||
|
||||
transaction.updateComponentsAndTotal(principalPortion, interestPortion, chargeAmount, penaltychargesPortion);
|
||||
transaction.updateComponentsAndTotal(principalPortion, interestPortion, chargeAmount, penaltyChargesPortion);
|
||||
|
||||
} else {
|
||||
transactionToRemove = transaction;
|
||||
@@ -874,12 +842,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return this.summary.getTotalWrittenOff();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param calculationType
|
||||
* @param percentage
|
||||
* @param installment
|
||||
* @return
|
||||
*/
|
||||
private Money calculateInstallmentChargeAmount(final ChargeCalculationType calculationType, final BigDecimal percentage,
|
||||
final LoanRepaymentScheduleInstallment installment) {
|
||||
Money percentOf = switch (calculationType) {
|
||||
@@ -1113,64 +1075,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
};
|
||||
}
|
||||
|
||||
// This method returns date format and locale if present in the JsonCommand
|
||||
public Map<String, String> getDateFormatAndLocale(final JsonCommand jsonCommand) {
|
||||
Map<String, String> returnObject = new HashMap<>();
|
||||
JsonElement jsonElement = jsonCommand.parsedJson();
|
||||
if (jsonElement.isJsonObject()) {
|
||||
JsonObject topLevel = jsonElement.getAsJsonObject();
|
||||
if (topLevel.has(LoanApiConstants.dateFormatParameterName)
|
||||
&& topLevel.get(LoanApiConstants.dateFormatParameterName).isJsonPrimitive()) {
|
||||
final JsonPrimitive primitive = topLevel.get(LoanApiConstants.dateFormatParameterName).getAsJsonPrimitive();
|
||||
returnObject.put(LoanApiConstants.dateFormatParameterName, primitive.getAsString());
|
||||
}
|
||||
if (topLevel.has(LoanApiConstants.localeParameterName)
|
||||
&& topLevel.get(LoanApiConstants.localeParameterName).isJsonPrimitive()) {
|
||||
final JsonPrimitive primitive = topLevel.get(LoanApiConstants.localeParameterName).getAsJsonPrimitive();
|
||||
String localeString = primitive.getAsString();
|
||||
returnObject.put(LoanApiConstants.localeParameterName, localeString);
|
||||
}
|
||||
}
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
public Map<String, Object> parseDisbursementDetails(final JsonObject jsonObject, String dateFormat, Locale locale) {
|
||||
Map<String, Object> returnObject = new HashMap<>();
|
||||
if (jsonObject.get(LoanApiConstants.expectedDisbursementDateParameterName) != null
|
||||
&& jsonObject.get(LoanApiConstants.expectedDisbursementDateParameterName).isJsonPrimitive()) {
|
||||
final JsonPrimitive primitive = jsonObject.get(LoanApiConstants.expectedDisbursementDateParameterName).getAsJsonPrimitive();
|
||||
final String valueAsString = primitive.getAsString();
|
||||
if (StringUtils.isNotBlank(valueAsString)) {
|
||||
LocalDate date = JsonParserHelper.convertFrom(valueAsString, LoanApiConstants.expectedDisbursementDateParameterName,
|
||||
dateFormat, locale);
|
||||
if (date != null) {
|
||||
returnObject.put(LoanApiConstants.expectedDisbursementDateParameterName, date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive()
|
||||
&& StringUtils.isNotBlank(jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString())) {
|
||||
BigDecimal principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal();
|
||||
returnObject.put(LoanApiConstants.disbursementPrincipalParameterName, principal);
|
||||
}
|
||||
|
||||
if (jsonObject.has(LoanApiConstants.disbursementIdParameterName)
|
||||
&& jsonObject.get(LoanApiConstants.disbursementIdParameterName).isJsonPrimitive()
|
||||
&& StringUtils.isNotBlank(jsonObject.get(LoanApiConstants.disbursementIdParameterName).getAsString())) {
|
||||
Long id = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementIdParameterName).getAsLong();
|
||||
returnObject.put(LoanApiConstants.disbursementIdParameterName, id);
|
||||
}
|
||||
|
||||
if (jsonObject.has(LoanApiConstants.loanChargeIdParameterName)
|
||||
&& jsonObject.get(LoanApiConstants.loanChargeIdParameterName).isJsonPrimitive()
|
||||
&& StringUtils.isNotBlank(jsonObject.get(LoanApiConstants.loanChargeIdParameterName).getAsString())) {
|
||||
returnObject.put(LoanApiConstants.loanChargeIdParameterName,
|
||||
jsonObject.getAsJsonPrimitive(LoanApiConstants.loanChargeIdParameterName).getAsString());
|
||||
}
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
public List<Long> fetchLoanTrancheChargeIds() {
|
||||
return getCharges().stream()//
|
||||
.filter(charge -> charge.isTrancheDisbursementCharge() && charge.isActive()) //
|
||||
@@ -1344,56 +1248,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
public LoanScheduleModel regenerateScheduleModel(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final MathContext mc = MoneyHelper.getMathContext();
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
LoanScheduleGenerator loanScheduleGenerator;
|
||||
if (loanApplicationTerms.isEqualAmortization()) {
|
||||
if (loanApplicationTerms.getInterestMethod().isDecliningBalance()) {
|
||||
final LoanScheduleGenerator decliningLoanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory()
|
||||
.create(loanApplicationTerms.getLoanScheduleType(), InterestMethod.DECLINING_BALANCE);
|
||||
Set<LoanCharge> loanCharges = getActiveCharges();
|
||||
LoanScheduleModel loanSchedule = decliningLoanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges,
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
|
||||
loanApplicationTerms
|
||||
.updateTotalInterestDue(Money.of(loanApplicationTerms.getCurrency(), loanSchedule.getTotalInterestCharged()));
|
||||
|
||||
}
|
||||
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(loanApplicationTerms.getLoanScheduleType(),
|
||||
InterestMethod.FLAT);
|
||||
} else {
|
||||
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(loanApplicationTerms.getLoanScheduleType(),
|
||||
loanApplicationTerms.getInterestMethod());
|
||||
}
|
||||
|
||||
return loanScheduleGenerator.generate(mc, loanApplicationTerms, getActiveCharges(), scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
}
|
||||
|
||||
private BigDecimal constructFloatingInterestRates(final BigDecimal annualNominalInterestRate, final FloatingRateDTO floatingRateDTO,
|
||||
final List<LoanTermVariationsData> loanTermVariations) {
|
||||
final LocalDate dateValue = null;
|
||||
final boolean isSpecificToInstallment = false;
|
||||
BigDecimal interestRate = annualNominalInterestRate;
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
floatingRateDTO.resetInterestRateDiff();
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getBusinessLocalDate();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanVariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate(), dateValue, isSpecificToInstallment);
|
||||
if (!DateUtils.isBefore(interestRateStartDate, periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
interestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
return interestRate;
|
||||
}
|
||||
|
||||
public boolean isAutoRepaymentForDownPaymentEnabled() {
|
||||
return this.loanRepaymentScheduleDetail.isEnableDownPayment()
|
||||
&& this.loanRepaymentScheduleDetail.isEnableAutoRepaymentForDownPayment();
|
||||
@@ -1664,7 +1518,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
// if total paid in transactions doesnt match repayment schedule then there's an overpayment.
|
||||
// if total paid in transactions doesn't match repayment schedule then there's an overpayment.
|
||||
return totalPaidInRepayments.minus(cumulativeTotalPaidOnInstallments);
|
||||
}
|
||||
|
||||
@@ -1945,7 +1799,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return this.client != null ? this.client.getOffice() : this.group.getOffice();
|
||||
}
|
||||
|
||||
private Boolean isCashBasedAccountingEnabledOnLoanProduct() {
|
||||
public Boolean isCashBasedAccountingEnabledOnLoanProduct() {
|
||||
return this.loanProduct.isCashBasedAccountingEnabled();
|
||||
}
|
||||
|
||||
@@ -1970,165 +1824,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return this.loanProduct.getId();
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> deriveAccountingBridgeDataForChargeOff(final String currencyCode,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, boolean isAccountTransfer) {
|
||||
|
||||
final List<Map<String, Object>> accountingBridgeData = new ArrayList<>();
|
||||
final List<Map<String, Object>> newLoanTransactionsBeforeChargeOff = new ArrayList<>();
|
||||
final List<Map<String, Object>> newLoanTransactionsAfterChargeOff = new ArrayList<>();
|
||||
// get map before charge-off
|
||||
final Map<String, Object> accountingBridgeDataBeforeChargeOff = buildAccountingMapForChargeOffDateCriteria(currencyCode,
|
||||
isAccountTransfer, true);
|
||||
// get map after charge-off
|
||||
final Map<String, Object> accountingBridgeDataAfterChargeOff = buildAccountingMapForChargeOffDateCriteria(currencyCode,
|
||||
isAccountTransfer, false);
|
||||
|
||||
// split the transactions according charge-off date
|
||||
classifyTransactionsBasedOnChargeOffDate(newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff,
|
||||
existingTransactionIds, existingReversedTransactionIds, currencyCode);
|
||||
|
||||
accountingBridgeDataBeforeChargeOff.put("newLoanTransactions", newLoanTransactionsBeforeChargeOff);
|
||||
accountingBridgeData.add(accountingBridgeDataBeforeChargeOff);
|
||||
|
||||
accountingBridgeDataAfterChargeOff.put("newLoanTransactions", newLoanTransactionsAfterChargeOff);
|
||||
accountingBridgeData.add(accountingBridgeDataAfterChargeOff);
|
||||
|
||||
return accountingBridgeData;
|
||||
}
|
||||
|
||||
private void classifyTransactionsBasedOnChargeOffDate(List<Map<String, Object>> newLoanTransactionsBeforeChargeOff,
|
||||
List<Map<String, Object>> newLoanTransactionsAfterChargeOff, List<Long> existingTransactionIds,
|
||||
List<Long> existingReversedTransactionIds, String currencyCode) {
|
||||
// Before
|
||||
filterTransactionsByChargeOffDate(newLoanTransactionsBeforeChargeOff, currencyCode, existingTransactionIds,
|
||||
existingReversedTransactionIds, transaction -> DateUtils.isBefore(transaction.getTransactionDate(), getChargedOffOnDate()));
|
||||
// On
|
||||
filterTransactionsByChargeOffDate(newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff, currencyCode,
|
||||
existingTransactionIds, existingReversedTransactionIds,
|
||||
transaction -> DateUtils.isEqual(transaction.getTransactionDate(), getChargedOffOnDate()));
|
||||
// After
|
||||
filterTransactionsByChargeOffDate(newLoanTransactionsAfterChargeOff, currencyCode, existingTransactionIds,
|
||||
existingReversedTransactionIds, transaction -> DateUtils.isAfter(transaction.getTransactionDate(), getChargedOffOnDate()));
|
||||
}
|
||||
|
||||
private Map<String, Object> getAccountingBridgeDataGenericAttributes(final String currencyCode, boolean isAccountTransfer) {
|
||||
final Map<String, Object> accountingBridgeDataGenericAttributes = new LinkedHashMap<>();
|
||||
accountingBridgeDataGenericAttributes.put("loanId", getId());
|
||||
accountingBridgeDataGenericAttributes.put("loanProductId", productId());
|
||||
accountingBridgeDataGenericAttributes.put("officeId", getOfficeId());
|
||||
accountingBridgeDataGenericAttributes.put("currencyCode", currencyCode);
|
||||
accountingBridgeDataGenericAttributes.put("calculatedInterest", this.summary.getTotalInterestCharged());
|
||||
accountingBridgeDataGenericAttributes.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeDataGenericAttributes.put("upfrontAccrualBasedAccountingEnabled", isUpfrontAccrualAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeDataGenericAttributes.put("periodicAccrualBasedAccountingEnabled",
|
||||
isPeriodicAccrualAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeDataGenericAttributes.put("isAccountTransfer", isAccountTransfer);
|
||||
return accountingBridgeDataGenericAttributes;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildAccountingMapForChargeOffDateCriteria(final String currencyCode, boolean isAccountTransfer,
|
||||
boolean isBeforeChargeOffDate) {
|
||||
final Map<String, Object> accountingBridgeDataChargeOff = new LinkedHashMap<>(
|
||||
getAccountingBridgeDataGenericAttributes(currencyCode, isAccountTransfer));
|
||||
accountingBridgeDataChargeOff.put("isChargeOff", !isBeforeChargeOffDate && isChargedOff());
|
||||
accountingBridgeDataChargeOff.put("isFraud", isFraud());
|
||||
accountingBridgeDataChargeOff.put("chargeOffReasonCodeValue", fetchChargeOffReasonId());
|
||||
return accountingBridgeDataChargeOff;
|
||||
}
|
||||
|
||||
private void filterTransactionsByChargeOffDate(List<Map<String, Object>> filteredTransactions, final String currencyCode,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
Predicate<LoanTransaction> chargeOffDateCriteria) {
|
||||
filteredTransactions.addAll(this.loanTransactions.stream() //
|
||||
.filter(chargeOffDateCriteria) //
|
||||
.filter(transaction -> {
|
||||
boolean isExistingTransaction = existingTransactionIds.contains(transaction.getId());
|
||||
boolean isExistingReversedTransaction = existingReversedTransactionIds.contains(transaction.getId());
|
||||
|
||||
if (transaction.isReversed() && isExistingTransaction && !isExistingReversedTransaction) {
|
||||
return true;
|
||||
} else {
|
||||
return !isExistingTransaction;
|
||||
}
|
||||
}) //
|
||||
.map(transaction -> transaction.toMapData(currencyCode)).toList());
|
||||
}
|
||||
|
||||
private void filterTransactionsByChargeOffDate(List<Map<String, Object>> newLoanTransactionsBeforeChargeOff,
|
||||
List<Map<String, Object>> newLoanTransactionsAfterChargeOff, String currencyCode, List<Long> existingTransactionIds,
|
||||
List<Long> existingReversedTransactionIds, Predicate<LoanTransaction> chargeOffDateCriteria) {
|
||||
|
||||
LoanTransaction chargeOffTransaction = this.loanTransactions.stream() //
|
||||
.filter(LoanTransaction::isChargeOff) //
|
||||
.filter(LoanTransaction::isNotReversed) //
|
||||
.findFirst().get();
|
||||
|
||||
LoanTransaction originalChargeOffTransaction = getOriginalTransactionIfReverseReplayed(chargeOffTransaction);
|
||||
|
||||
this.loanTransactions.stream().filter(chargeOffDateCriteria).forEach(transaction -> {
|
||||
boolean isExistingTransaction = existingTransactionIds.contains(transaction.getId());
|
||||
boolean isExistingReversedTransaction = existingReversedTransactionIds.contains(transaction.getId());
|
||||
List<Map<String, Object>> targetList = null;
|
||||
if ((transaction.isReversed() && isExistingTransaction && !isExistingReversedTransaction)) {
|
||||
// reversed transactions
|
||||
LoanTransaction originalTransaction = getOriginalTransactionIfReverseReplayed(transaction);
|
||||
targetList = originalTransaction.happenedBefore(originalChargeOffTransaction) ? newLoanTransactionsBeforeChargeOff
|
||||
: newLoanTransactionsAfterChargeOff;
|
||||
|
||||
} else if (!isExistingTransaction) {
|
||||
// new and replayed transactions
|
||||
targetList = transaction.happenedBefore(chargeOffTransaction) ? newLoanTransactionsBeforeChargeOff
|
||||
: newLoanTransactionsAfterChargeOff;
|
||||
}
|
||||
if (targetList != null) {
|
||||
targetList.add(transaction.toMapData(currencyCode));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private LoanTransaction getOriginalTransactionIfReverseReplayed(LoanTransaction loanTransaction) {
|
||||
if (!loanTransaction.getLoanTransactionRelations().isEmpty()) {
|
||||
return loanTransaction.getLoanTransactionRelations().stream()
|
||||
.filter(tr -> LoanTransactionRelationTypeEnum.REPLAYED.equals(tr.getRelationType())).map(tr -> tr.getToTransaction())
|
||||
.collect(Collectors.toList()).stream().sorted(Comparator.comparingLong(LoanTransaction::getId)).findFirst()
|
||||
.orElse(loanTransaction);
|
||||
}
|
||||
return loanTransaction;
|
||||
}
|
||||
|
||||
public Map<String, Object> deriveAccountingBridgeData(final String currencyCode, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, boolean isAccountTransfer) {
|
||||
|
||||
final Map<String, Object> accountingBridgeData = new LinkedHashMap<>();
|
||||
accountingBridgeData.put("loanId", getId());
|
||||
accountingBridgeData.put("loanProductId", productId());
|
||||
accountingBridgeData.put("officeId", getOfficeId());
|
||||
accountingBridgeData.put("currencyCode", currencyCode);
|
||||
accountingBridgeData.put("calculatedInterest", this.summary.getTotalInterestCharged());
|
||||
accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeData.put("upfrontAccrualBasedAccountingEnabled", isUpfrontAccrualAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeData.put("periodicAccrualBasedAccountingEnabled", isPeriodicAccrualAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeData.put("isAccountTransfer", isAccountTransfer);
|
||||
accountingBridgeData.put("isChargeOff", isChargedOff());
|
||||
accountingBridgeData.put("chargeOffReasonCodeValue", fetchChargeOffReasonId());
|
||||
|
||||
accountingBridgeData.put("isFraud", isFraud());
|
||||
|
||||
final List<Map<String, Object>> newLoanTransactions = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
if (transaction.isReversed() && existingTransactionIds.contains(transaction.getId())
|
||||
&& !existingReversedTransactionIds.contains(transaction.getId())) {
|
||||
newLoanTransactions.add(transaction.toMapData(currencyCode));
|
||||
} else if (!existingTransactionIds.contains(transaction.getId())) {
|
||||
newLoanTransactions.add(transaction.toMapData(currencyCode));
|
||||
}
|
||||
}
|
||||
|
||||
accountingBridgeData.put("newLoanTransactions", newLoanTransactions);
|
||||
return accountingBridgeData;
|
||||
}
|
||||
|
||||
private Long fetchChargeOffReasonId() {
|
||||
public Long fetchChargeOffReasonId() {
|
||||
return isChargedOff() && getChargeOffReason() != null ? getChargeOffReason().getId() : null;
|
||||
}
|
||||
|
||||
@@ -2223,25 +1919,23 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
final Integer loanRepaymentInterval = this.loanRepaymentScheduleDetail.getRepayEvery();
|
||||
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
|
||||
|
||||
LocalDate newRepaymentDate = null;
|
||||
LocalDate seedDate = meetingStartDate;
|
||||
LocalDate newRepaymentDate;
|
||||
LocalDate latestRepaymentDate = null;
|
||||
List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments();
|
||||
for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : installments) {
|
||||
LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate();
|
||||
|
||||
// FIXME: AA this won't update repayment dates before current date.
|
||||
|
||||
if (DateUtils.isAfter(oldDueDate, seedDate) && DateUtils.isDateInTheFuture(oldDueDate)) {
|
||||
newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recuringRule, seedDate, oldDueDate, loanRepaymentInterval,
|
||||
frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays);
|
||||
if (DateUtils.isAfter(oldDueDate, meetingStartDate) && DateUtils.isDateInTheFuture(oldDueDate)) {
|
||||
newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate,
|
||||
loanRepaymentInterval, frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays);
|
||||
|
||||
final LocalDate maxDateLimitForNewRepayment = getMaxDateLimitForNewRepayment(repaymentPeriodFrequencyType,
|
||||
loanRepaymentInterval, tmpFromDate);
|
||||
|
||||
if (DateUtils.isAfter(newRepaymentDate, maxDateLimitForNewRepayment)) {
|
||||
newRepaymentDate = CalendarUtils.getNextRepaymentMeetingDate(recuringRule, seedDate, tmpFromDate, loanRepaymentInterval,
|
||||
frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays);
|
||||
newRepaymentDate = CalendarUtils.getNextRepaymentMeetingDate(recuringRule, meetingStartDate, tmpFromDate,
|
||||
loanRepaymentInterval, frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays);
|
||||
}
|
||||
|
||||
if (isHolidayEnabled) {
|
||||
@@ -2292,10 +1986,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
}
|
||||
|
||||
public Integer getLoanProductLoanCounter() {
|
||||
if (this.loanProductCounter == null) {
|
||||
return 0;
|
||||
}
|
||||
return this.loanProductCounter;
|
||||
return Objects.requireNonNullElse(this.loanProductCounter, 0);
|
||||
}
|
||||
|
||||
public void updateClientLoanCounter(final Integer newLoanCounter) {
|
||||
@@ -2510,76 +2201,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return (this.actualMaturityDate != null) ? (referenceDate.compareTo(this.actualMaturityDate) >= 0) : false;
|
||||
}
|
||||
|
||||
public LoanApplicationTerms constructLoanApplicationTerms(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final Integer loanTermFrequency = this.termFrequency;
|
||||
NthDayType nthDayType = null;
|
||||
DayOfWeekType dayOfWeekType = null;
|
||||
final List<DisbursementData> disbursementData = new ArrayList<>();
|
||||
for (LoanDisbursementDetails disbursementDetails : getDisbursementDetails()) {
|
||||
disbursementData.add(disbursementDetails.toData());
|
||||
}
|
||||
|
||||
Calendar calendar = scheduleGeneratorDTO.getCalendar();
|
||||
if (calendar != null) {
|
||||
nthDayType = CalendarUtils.getRepeatsOnNthDayOfMonth(calendar.getRecurrence());
|
||||
dayOfWeekType = DayOfWeekType.fromInt(CalendarUtils.getRepeatsOnDay(calendar.getRecurrence()).getValue());
|
||||
}
|
||||
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
RecalculationFrequencyType recalculationFrequencyType = null;
|
||||
InterestRecalculationCompoundingMethod compoundingMethod = null;
|
||||
RecalculationFrequencyType compoundingFrequencyType = null;
|
||||
LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
CalendarHistoryDataWrapper calendarHistoryDataWrapper;
|
||||
RepaymentStartDateType repaymentStartDateType = this.getLoanProduct().getRepaymentStartDateType();
|
||||
boolean allowCompoundingOnEod = false;
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = scheduleGeneratorDTO.getCalendarInstanceForInterestRecalculation();
|
||||
compoundingCalendarInstance = scheduleGeneratorDTO.getCompoundingCalendarInstance();
|
||||
recalculationFrequencyType = this.loanInterestRecalculationDetails.getRestFrequencyType();
|
||||
compoundingMethod = this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod();
|
||||
compoundingFrequencyType = this.loanInterestRecalculationDetails.getCompoundingFrequencyType();
|
||||
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
|
||||
allowCompoundingOnEod = this.loanInterestRecalculationDetails.allowCompoundingOnEod();
|
||||
}
|
||||
calendar = scheduleGeneratorDTO.getCalendar();
|
||||
calendarHistoryDataWrapper = scheduleGeneratorDTO.getCalendarHistoryDataWrapper();
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
FloatingRateDTO floatingRateDTO = scheduleGeneratorDTO.getFloatingRateDTO();
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
annualNominalInterestRate = constructLoanTermVariations(floatingRateDTO, annualNominalInterestRate, loanTermVariations);
|
||||
LocalDate interestChargedFromDate = getInterestChargedFromDate();
|
||||
if (interestChargedFromDate == null && scheduleGeneratorDTO.isInterestChargedFromDateAsDisbursementDateEnabled()) {
|
||||
interestChargedFromDate = getDisbursementDate();
|
||||
}
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getCurrency(), loanTermFrequency, this.termPeriodFrequencyType,
|
||||
nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
||||
scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
||||
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
||||
interestChargedFromDate, this.loanProduct.getPrincipalThresholdForLastInstallment(),
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
|
||||
holidayDetailDTO, allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
|
||||
scheduleGeneratorDTO.isInterestToBeRecoveredFirstWhenGreaterThanEMI(), this.fixedPrincipalPercentagePerInstallment,
|
||||
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans(), repaymentStartDateType, getSubmittedOnDate());
|
||||
}
|
||||
|
||||
public BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
|
||||
List<LoanTermVariationsData> loanTermVariations) {
|
||||
for (LoanTermVariations variationTerms : this.loanTermVariations) {
|
||||
if (variationTerms.isActive()) {
|
||||
loanTermVariations.add(variationTerms.toData());
|
||||
}
|
||||
}
|
||||
annualNominalInterestRate = constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations);
|
||||
return annualNominalInterestRate;
|
||||
}
|
||||
|
||||
public LocalDate fetchInterestRecalculateFromDate() {
|
||||
LocalDate recalculatedOn;
|
||||
if (this.interestRecalculatedOn == null) {
|
||||
@@ -2602,7 +2223,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
Money transactionOutstanding = loanTransaction.getPrincipalPortion(getCurrency());
|
||||
if (loanTransaction.isOverPaid()) {
|
||||
// in case of advanced payment strategy and creditAllocations the full amount is recognized first
|
||||
if (this.getCreditAllocationRules() != null && this.getCreditAllocationRules().size() > 0) {
|
||||
if (this.getCreditAllocationRules() != null && !this.getCreditAllocationRules().isEmpty()) {
|
||||
Money payedPrincipal = loanTransaction.getLoanTransactionToRepaymentScheduleMappings().stream() //
|
||||
.map(mapping -> mapping.getPrincipalPortion(getCurrency())) //
|
||||
.reduce(Money.zero(getCurrency()), Money::plus);
|
||||
@@ -2690,137 +2311,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return getRepaymentScheduleInstallments().stream().filter(predicate).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return loan disbursement data
|
||||
**/
|
||||
public List<DisbursementData> getDisbursementData() {
|
||||
Iterator<LoanDisbursementDetails> iterator = this.getDisbursementDetails().iterator();
|
||||
List<DisbursementData> disbursementData = new ArrayList<>();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
LoanDisbursementDetails loanDisbursementDetails = iterator.next();
|
||||
|
||||
LocalDate expectedDisbursementDate = null;
|
||||
LocalDate actualDisbursementDate = null;
|
||||
|
||||
if (loanDisbursementDetails.expectedDisbursementDate() != null) {
|
||||
expectedDisbursementDate = loanDisbursementDetails.expectedDisbursementDate();
|
||||
}
|
||||
|
||||
if (loanDisbursementDetails.actualDisbursementDate() != null) {
|
||||
actualDisbursementDate = loanDisbursementDetails.actualDisbursementDate();
|
||||
}
|
||||
BigDecimal waivedChargeAmount = null;
|
||||
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate, actualDisbursementDate,
|
||||
loanDisbursementDetails.principal(), this.netDisbursalAmount, null, null, waivedChargeAmount));
|
||||
}
|
||||
|
||||
return disbursementData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return application terms of the Loan object
|
||||
**/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency,
|
||||
final CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar,
|
||||
final FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays,
|
||||
final HolidayDetailDTO holidayDetailDTO) {
|
||||
LoanProduct loanProduct = loanProduct();
|
||||
final MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getCurrency();
|
||||
|
||||
final Integer loanTermFrequency = getTermFrequency();
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
NthDayType nthDayType = null;
|
||||
DayOfWeekType dayOfWeekType = null;
|
||||
if (loanCalendar != null) {
|
||||
nthDayType = CalendarUtils.getRepeatsOnNthDayOfMonth(loanCalendar.getRecurrence());
|
||||
CalendarWeekDaysType getRepeatsOnDay = CalendarUtils.getRepeatsOnDay(loanCalendar.getRecurrence());
|
||||
Integer getRepeatsOnDayValue = null;
|
||||
if (getRepeatsOnDay != null) {
|
||||
getRepeatsOnDayValue = getRepeatsOnDay.getValue();
|
||||
}
|
||||
if (getRepeatsOnDayValue != null) {
|
||||
dayOfWeekType = DayOfWeekType.fromInt(getRepeatsOnDayValue);
|
||||
}
|
||||
}
|
||||
|
||||
final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
|
||||
|
||||
final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod();
|
||||
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = this.loanRepaymentScheduleDetail
|
||||
.getInterestCalculationPeriodMethod();
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
final Money principalMoney = this.loanRepaymentScheduleDetail.getPrincipal();
|
||||
|
||||
final LocalDate expectedDisbursementDate = getExpectedDisbursedOnLocalDate();
|
||||
final LocalDate repaymentsStartingFromDate = getExpectedFirstRepaymentOnDate();
|
||||
LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
|
||||
|
||||
// TODO get calender linked to loan if any exist. As of 17-07-2014,
|
||||
// could not find a link in the database.
|
||||
// skip for now and set the Calender object to null
|
||||
// Calendar loanCalendar = null;
|
||||
// The calendar instance might be null if the loan is not connected
|
||||
// To a calendar object
|
||||
// if (loanCalendarInstance != null) {
|
||||
// loanCalendar = loanCalendarInstance.getCalendar();
|
||||
// }
|
||||
|
||||
final Integer graceOnPrincipalPayment = this.loanRepaymentScheduleDetail.getGraceOnPrincipalPayment();
|
||||
final Integer graceOnInterestPayment = this.loanRepaymentScheduleDetail.getGraceOnInterestPayment();
|
||||
final Integer graceOnInterestCharged = this.loanRepaymentScheduleDetail.getGraceOnInterestCharged();
|
||||
final LocalDate interestChargedFromDate = getInterestChargedFromDate();
|
||||
final Integer graceOnArrearsAgeing = this.loanRepaymentScheduleDetail.getGraceOnArrearsAgeing();
|
||||
|
||||
final Money inArrearsToleranceMoney = this.loanRepaymentScheduleDetail.getInArrearsTolerance();
|
||||
final BigDecimal emiAmount = getFixedEmiAmount();
|
||||
final BigDecimal maxOutstandingBalance = getMaxOutstandingLoanBalance();
|
||||
|
||||
final List<DisbursementData> disbursementData = getDisbursementData();
|
||||
|
||||
CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
if (loanCalendar != null) {
|
||||
Set<CalendarHistory> calendarHistory = loanCalendar.getCalendarHistory();
|
||||
calendarHistoryDataWrapper = new CalendarHistoryDataWrapper(calendarHistory);
|
||||
}
|
||||
|
||||
RecalculationFrequencyType recalculationFrequencyType = null;
|
||||
InterestRecalculationCompoundingMethod compoundingMethod = null;
|
||||
RecalculationFrequencyType compoundingFrequencyType = null;
|
||||
LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
RepaymentStartDateType repaymentStartDateType = loanProduct.getRepaymentStartDateType();
|
||||
boolean allowCompoundingOnEod = false;
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
recalculationFrequencyType = this.loanInterestRecalculationDetails.getRestFrequencyType();
|
||||
compoundingMethod = this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod();
|
||||
compoundingFrequencyType = this.loanInterestRecalculationDetails.getCompoundingFrequencyType();
|
||||
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
|
||||
allowCompoundingOnEod = this.loanInterestRecalculationDetails.allowCompoundingOnEod();
|
||||
}
|
||||
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
annualNominalInterestRate = constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations);
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(applicationCurrency.toData(), loanTermFrequency, loanTermPeriodFrequencyType, nthDayType,
|
||||
dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||
inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData,
|
||||
maxOutstandingBalance, interestChargedFromDate, this.loanProduct.getPrincipalThresholdForLastInstallment(),
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false,
|
||||
false, this.fixedPrincipalPercentagePerInstallment, false, repaymentStartDateType, getSubmittedOnDate());
|
||||
}
|
||||
|
||||
public void updateRescheduledByUser(AppUser rescheduledByUser) {
|
||||
this.rescheduledByUser = rescheduledByUser;
|
||||
}
|
||||
@@ -2993,7 +2483,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment fetchLoanForeclosureDetail(final LocalDate closureDate) {
|
||||
Money[] receivables = retriveIncomeOutstandingTillDate(closureDate);
|
||||
Money[] receivables = retrieveIncomeOutstandingTillDate(closureDate);
|
||||
Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding());
|
||||
totalPrincipal = totalPrincipal.minus(receivables[3]);
|
||||
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
|
||||
@@ -3002,7 +2492,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
receivables[0].getAmount(), receivables[1].getAmount(), receivables[2].getAmount(), false, compoundingDetails);
|
||||
}
|
||||
|
||||
public Money[] retriveIncomeOutstandingTillDate(final LocalDate paymentDate) {
|
||||
private Money[] retrieveIncomeOutstandingTillDate(final LocalDate paymentDate) {
|
||||
Money[] balances = new Money[4];
|
||||
final MonetaryCurrency currency = getCurrency();
|
||||
Money interest = Money.zero(currency);
|
||||
@@ -3058,13 +2548,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
Money penaltyAccoutedForCurrentPeriod = Money.zero(getCurrency());
|
||||
Money feeForCurrentPeriod = Money.zero(getCurrency());
|
||||
Money feeAccountedForCurrentPeriod = Money.zero(getCurrency());
|
||||
Money interestForCurrentPeriod = Money.zero(getCurrency());
|
||||
Money interestAccountedForCurrentPeriod = Money.zero(getCurrency());
|
||||
int totalPeriodDays = DateUtils.getExactDifferenceInDays(installment.getFromDate(), installment.getDueDate());
|
||||
int tillDays = DateUtils.getExactDifferenceInDays(installment.getFromDate(), paymentDate);
|
||||
interestForCurrentPeriod = Money.of(getCurrency(), BigDecimal
|
||||
Money interestForCurrentPeriod = Money.of(getCurrency(), BigDecimal
|
||||
.valueOf(calculateInterestForDays(totalPeriodDays, installment.getInterestCharged(getCurrency()).getAmount(), tillDays)));
|
||||
interestAccountedForCurrentPeriod = installment.getInterestWaived(getCurrency()).plus(installment.getInterestPaid(getCurrency()));
|
||||
Money interestAccountedForCurrentPeriod = installment.getInterestWaived(getCurrency())
|
||||
.plus(installment.getInterestPaid(getCurrency()));
|
||||
for (LoanCharge loanCharge : this.charges) {
|
||||
if (loanCharge.isActive() && !loanCharge.isDueAtDisbursement()) {
|
||||
boolean isDue = loanCharge.isDueInPeriod(installment.getFromDate(), paymentDate, isFirstNormalInstallment);
|
||||
@@ -3101,7 +2590,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return balances;
|
||||
}
|
||||
|
||||
public Money[] retriveIncomeForOverlappingPeriod(final LocalDate paymentDate) {
|
||||
public Money[] retrieveIncomeForOverlappingPeriod(final LocalDate paymentDate) {
|
||||
Money[] balances = new Money[3];
|
||||
final MonetaryCurrency currency = getCurrency();
|
||||
balances[0] = balances[1] = balances[2] = Money.zero(currency);
|
||||
@@ -3165,19 +2654,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
this.fraud = value;
|
||||
}
|
||||
|
||||
public BigDecimal getFirstDisbursalAmount() {
|
||||
BigDecimal firstDisbursalAmount;
|
||||
|
||||
if (this.isMultiDisburmentLoan()) {
|
||||
List<DisbursementData> disbursementData = getDisbursementData();
|
||||
Collections.sort(disbursementData);
|
||||
firstDisbursalAmount = disbursementData.get(disbursementData.size() - 1).getPrincipal();
|
||||
} else {
|
||||
firstDisbursalAmount = this.getLoanRepaymentScheduleDetail().getPrincipal().getAmount();
|
||||
}
|
||||
return firstDisbursalAmount;
|
||||
}
|
||||
|
||||
public void setTopupLoanDetails(LoanTopupDetails topupLoanDetails) {
|
||||
this.loanTopupDetails = topupLoanDetails;
|
||||
}
|
||||
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* 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.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LoanAccountingBridgeMapper {
|
||||
|
||||
public List<AccountingBridgeDataDTO> deriveAccountingBridgeDataForChargeOff(final String currencyCode,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final boolean isAccountTransfer,
|
||||
final Loan loan) {
|
||||
final List<AccountingBridgeLoanTransactionDTO> newLoanTransactionsBeforeChargeOff = new ArrayList<>();
|
||||
final List<AccountingBridgeLoanTransactionDTO> newLoanTransactionsAfterChargeOff = new ArrayList<>();
|
||||
|
||||
// split the transactions according charge-off date
|
||||
classifyTransactionsBasedOnChargeOffDate(newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff,
|
||||
existingTransactionIds, existingReversedTransactionIds, currencyCode, loan);
|
||||
|
||||
AccountingBridgeDataDTO beforeChargeOff = new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(),
|
||||
currencyCode, loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(),
|
||||
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(),
|
||||
isAccountTransfer, false, loan.isFraud(), loan.fetchChargeOffReasonId(), newLoanTransactionsBeforeChargeOff);
|
||||
|
||||
AccountingBridgeDataDTO afterChargeOff = new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(),
|
||||
currencyCode, loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(),
|
||||
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(),
|
||||
isAccountTransfer, true, loan.isFraud(), loan.fetchChargeOffReasonId(), newLoanTransactionsAfterChargeOff);
|
||||
|
||||
List<AccountingBridgeDataDTO> result = new ArrayList<>();
|
||||
result.add(beforeChargeOff);
|
||||
result.add(afterChargeOff);
|
||||
return result;
|
||||
}
|
||||
|
||||
public AccountingBridgeDataDTO deriveAccountingBridgeData(final String currencyCode, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final boolean isAccountTransfer, final Loan loan) {
|
||||
final List<AccountingBridgeLoanTransactionDTO> newLoanTransactions = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : loan.getLoanTransactions()) {
|
||||
if (transaction.isReversed() && existingTransactionIds.contains(transaction.getId())
|
||||
&& !existingReversedTransactionIds.contains(transaction.getId())) {
|
||||
newLoanTransactions.add(mapToLoanTransactionData(transaction, currencyCode));
|
||||
} else if (!existingTransactionIds.contains(transaction.getId())) {
|
||||
newLoanTransactions.add(mapToLoanTransactionData(transaction, currencyCode));
|
||||
}
|
||||
}
|
||||
|
||||
return new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), currencyCode,
|
||||
loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(),
|
||||
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(),
|
||||
isAccountTransfer, loan.isChargedOff(), loan.isFraud(), loan.fetchChargeOffReasonId(), newLoanTransactions);
|
||||
}
|
||||
|
||||
public AccountingBridgeLoanTransactionDTO mapToLoanTransactionData(final LoanTransaction transaction, final String currencyCode) {
|
||||
final AccountingBridgeLoanTransactionDTO transactionDTO = new AccountingBridgeLoanTransactionDTO();
|
||||
|
||||
transactionDTO.setId(transaction.getId());
|
||||
transactionDTO.setOfficeId(transaction.getOffice().getId());
|
||||
transactionDTO.setType(LoanEnumerations.transactionType(transaction.getTypeOf()));
|
||||
transactionDTO.setReversed(transaction.isReversed());
|
||||
transactionDTO.setDate(transaction.getTransactionDate());
|
||||
transactionDTO.setCurrencyCode(currencyCode);
|
||||
transactionDTO.setAmount(transaction.getAmount());
|
||||
transactionDTO.setNetDisbursalAmount(transaction.getLoan().getNetDisbursalAmount());
|
||||
|
||||
if (transactionDTO.getType().isChargeback() && (transaction.getLoan().getCreditAllocationRules() == null
|
||||
|| transaction.getLoan().getCreditAllocationRules().isEmpty())) {
|
||||
transactionDTO.setPrincipalPortion(transaction.getAmount());
|
||||
} else {
|
||||
transactionDTO.setPrincipalPortion(transaction.getPrincipalPortion());
|
||||
}
|
||||
|
||||
transactionDTO.setInterestPortion(transaction.getInterestPortion());
|
||||
transactionDTO.setFeeChargesPortion(transaction.getFeeChargesPortion());
|
||||
transactionDTO.setPenaltyChargesPortion(transaction.getPenaltyChargesPortion());
|
||||
transactionDTO.setOverPaymentPortion(transaction.getOverPaymentPortion());
|
||||
|
||||
if (transactionDTO.getType().isChargeRefund()) {
|
||||
transactionDTO.setChargeRefundChargeType(transaction.getChargeRefundChargeType());
|
||||
}
|
||||
|
||||
if (transaction.getPaymentDetail() != null) {
|
||||
transactionDTO.setPaymentTypeId(transaction.getPaymentDetail().getPaymentType().getId());
|
||||
}
|
||||
|
||||
if (!transaction.getLoanChargesPaid().isEmpty()) {
|
||||
List<LoanChargePaidByDTO> loanChargesPaidData = new ArrayList<>();
|
||||
for (final LoanChargePaidBy chargePaidBy : transaction.getLoanChargesPaid()) {
|
||||
final LoanChargePaidByDTO loanChargePaidData = new LoanChargePaidByDTO();
|
||||
loanChargePaidData.setChargeId(chargePaidBy.getLoanCharge().getCharge().getId());
|
||||
loanChargePaidData.setIsPenalty(chargePaidBy.getLoanCharge().isPenaltyCharge());
|
||||
loanChargePaidData.setLoanChargeId(chargePaidBy.getLoanCharge().getId());
|
||||
loanChargePaidData.setAmount(chargePaidBy.getAmount());
|
||||
loanChargePaidData.setInstallmentNumber(chargePaidBy.getInstallmentNumber());
|
||||
|
||||
loanChargesPaidData.add(loanChargePaidData);
|
||||
}
|
||||
transactionDTO.setLoanChargesPaid(loanChargesPaidData);
|
||||
}
|
||||
|
||||
if (transactionDTO.getType().isChargeback() && transaction.getOverPaymentPortion() != null
|
||||
&& transaction.getOverPaymentPortion().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal principalPaid = transaction.getOverPaymentPortion();
|
||||
BigDecimal feePaid = BigDecimal.ZERO;
|
||||
BigDecimal penaltyPaid = BigDecimal.ZERO;
|
||||
if (!transaction.getLoanTransactionToRepaymentScheduleMappings().isEmpty()) {
|
||||
principalPaid = transaction.getLoanTransactionToRepaymentScheduleMappings().stream()
|
||||
.map(mapping -> Optional.ofNullable(mapping.getPrincipalPortion()).orElse(BigDecimal.ZERO))
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
feePaid = transaction.getLoanTransactionToRepaymentScheduleMappings().stream()
|
||||
.map(mapping -> Optional.ofNullable(mapping.getFeeChargesPortion()).orElse(BigDecimal.ZERO))
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
penaltyPaid = transaction.getLoanTransactionToRepaymentScheduleMappings().stream()
|
||||
.map(mapping -> Optional.ofNullable(mapping.getPenaltyChargesPortion()).orElse(BigDecimal.ZERO))
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
transactionDTO.setPrincipalPaid(principalPaid);
|
||||
transactionDTO.setFeePaid(feePaid);
|
||||
transactionDTO.setPenaltyPaid(penaltyPaid);
|
||||
}
|
||||
|
||||
LoanTransactionRelation loanTransactionRelation = transaction.getLoanTransactionRelations().stream()
|
||||
.filter(e -> LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT.equals(e.getRelationType())).findAny().orElse(null);
|
||||
if (loanTransactionRelation != null) {
|
||||
LoanCharge loanCharge = loanTransactionRelation.getToCharge();
|
||||
transactionDTO.setLoanChargeData(loanCharge.toData());
|
||||
}
|
||||
|
||||
transactionDTO.setLoanToLoanTransfer(false);
|
||||
|
||||
return transactionDTO;
|
||||
}
|
||||
|
||||
private void classifyTransactionsBasedOnChargeOffDate(final List<AccountingBridgeLoanTransactionDTO> newLoanTransactionsBeforeChargeOff,
|
||||
final List<AccountingBridgeLoanTransactionDTO> newLoanTransactionsAfterChargeOff, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final String currencyCode, final Loan loan) {
|
||||
// Before
|
||||
filterTransactionsByChargeOffDate(newLoanTransactionsBeforeChargeOff, currencyCode, existingTransactionIds,
|
||||
existingReversedTransactionIds,
|
||||
transaction -> DateUtils.isBefore(transaction.getTransactionDate(), loan.getChargedOffOnDate()), loan);
|
||||
// On
|
||||
filterTransactionsByChargeOffDate(newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff, currencyCode,
|
||||
existingTransactionIds, existingReversedTransactionIds,
|
||||
transaction -> DateUtils.isEqual(transaction.getTransactionDate(), loan.getChargedOffOnDate()), loan);
|
||||
// After
|
||||
filterTransactionsByChargeOffDate(newLoanTransactionsAfterChargeOff, currencyCode, existingTransactionIds,
|
||||
existingReversedTransactionIds,
|
||||
transaction -> DateUtils.isAfter(transaction.getTransactionDate(), loan.getChargedOffOnDate()), loan);
|
||||
}
|
||||
|
||||
private void filterTransactionsByChargeOffDate(final List<AccountingBridgeLoanTransactionDTO> filteredTransactions,
|
||||
final String currencyCode, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final Predicate<LoanTransaction> chargeOffDateCriteria, final Loan loan) {
|
||||
filteredTransactions.addAll(loan.getLoanTransactions().stream() //
|
||||
.filter(chargeOffDateCriteria) //
|
||||
.filter(transaction -> {
|
||||
boolean isExistingTransaction = existingTransactionIds.contains(transaction.getId());
|
||||
boolean isExistingReversedTransaction = existingReversedTransactionIds.contains(transaction.getId());
|
||||
|
||||
if (transaction.isReversed() && isExistingTransaction && !isExistingReversedTransaction) {
|
||||
return true;
|
||||
} else {
|
||||
return !isExistingTransaction;
|
||||
}
|
||||
}) //
|
||||
.map(transaction -> mapToLoanTransactionData(transaction, currencyCode)).toList());
|
||||
}
|
||||
|
||||
private void filterTransactionsByChargeOffDate(final List<AccountingBridgeLoanTransactionDTO> newLoanTransactionsBeforeChargeOff,
|
||||
final List<AccountingBridgeLoanTransactionDTO> newLoanTransactionsAfterChargeOff, final String currencyCode,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final Predicate<LoanTransaction> chargeOffDateCriteria, final Loan loan) {
|
||||
final Optional<LoanTransaction> chargeOffTransactionOptional = loan.getLoanTransactions().stream() //
|
||||
.filter(LoanTransaction::isChargeOff) //
|
||||
.filter(LoanTransaction::isNotReversed) //
|
||||
.findFirst();
|
||||
|
||||
if (chargeOffTransactionOptional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LoanTransaction chargeOffTransaction = chargeOffTransactionOptional.get();
|
||||
final LoanTransaction originalChargeOffTransaction = getOriginalTransactionIfReverseReplayed(chargeOffTransaction);
|
||||
|
||||
loan.getLoanTransactions().stream().filter(chargeOffDateCriteria).forEach(transaction -> {
|
||||
boolean isExistingTransaction = existingTransactionIds.contains(transaction.getId());
|
||||
boolean isExistingReversedTransaction = existingReversedTransactionIds.contains(transaction.getId());
|
||||
List<AccountingBridgeLoanTransactionDTO> targetList = null;
|
||||
if ((transaction.isReversed() && isExistingTransaction && !isExistingReversedTransaction)) {
|
||||
// reversed transactions
|
||||
LoanTransaction originalTransaction = getOriginalTransactionIfReverseReplayed(transaction);
|
||||
targetList = originalTransaction.happenedBefore(originalChargeOffTransaction) ? newLoanTransactionsBeforeChargeOff
|
||||
: newLoanTransactionsAfterChargeOff;
|
||||
|
||||
} else if (!isExistingTransaction) {
|
||||
// new and replayed transactions
|
||||
targetList = transaction.happenedBefore(chargeOffTransaction) ? newLoanTransactionsBeforeChargeOff
|
||||
: newLoanTransactionsAfterChargeOff;
|
||||
}
|
||||
if (targetList != null) {
|
||||
targetList.add(mapToLoanTransactionData(transaction, currencyCode));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private LoanTransaction getOriginalTransactionIfReverseReplayed(final LoanTransaction loanTransaction) {
|
||||
if (!loanTransaction.getLoanTransactionRelations().isEmpty()) {
|
||||
return loanTransaction.getLoanTransactionRelations().stream()
|
||||
.filter(tr -> LoanTransactionRelationTypeEnum.REPLAYED.equals(tr.getRelationType()))
|
||||
.map(LoanTransactionRelation::getToTransaction).toList().stream().min(Comparator.comparingLong(LoanTransaction::getId))
|
||||
.orElse(loanTransaction);
|
||||
}
|
||||
return loanTransaction;
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LoanMapper {
|
||||
|
||||
private final LoanTermVariationsMapper loanTermVariationsMapper;
|
||||
|
||||
public LoanScheduleModel regenerateScheduleModel(final ScheduleGeneratorDTO scheduleGeneratorDTO, final Loan loan) {
|
||||
final MathContext mc = MoneyHelper.getMathContext();
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO,
|
||||
loan);
|
||||
LoanScheduleGenerator loanScheduleGenerator;
|
||||
if (loanApplicationTerms.isEqualAmortization()) {
|
||||
if (loanApplicationTerms.getInterestMethod().isDecliningBalance()) {
|
||||
final LoanScheduleGenerator decliningLoanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory()
|
||||
.create(loanApplicationTerms.getLoanScheduleType(), InterestMethod.DECLINING_BALANCE);
|
||||
Set<LoanCharge> loanCharges = loan.getActiveCharges();
|
||||
LoanScheduleModel loanSchedule = decliningLoanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges,
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
|
||||
loanApplicationTerms
|
||||
.updateTotalInterestDue(Money.of(loanApplicationTerms.getCurrency(), loanSchedule.getTotalInterestCharged()));
|
||||
|
||||
}
|
||||
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(loanApplicationTerms.getLoanScheduleType(),
|
||||
InterestMethod.FLAT);
|
||||
} else {
|
||||
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(loanApplicationTerms.getLoanScheduleType(),
|
||||
loanApplicationTerms.getInterestMethod());
|
||||
}
|
||||
|
||||
return loanScheduleGenerator.generate(mc, loanApplicationTerms, loan.getActiveCharges(),
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
}
|
||||
|
||||
public List<DisbursementData> getDisbursementData(final Loan loan) {
|
||||
Iterator<LoanDisbursementDetails> iterator = loan.getDisbursementDetails().iterator();
|
||||
List<DisbursementData> disbursementData = new ArrayList<>();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
LoanDisbursementDetails loanDisbursementDetails = iterator.next();
|
||||
|
||||
LocalDate expectedDisbursementDate = null;
|
||||
LocalDate actualDisbursementDate = null;
|
||||
|
||||
if (loanDisbursementDetails.expectedDisbursementDate() != null) {
|
||||
expectedDisbursementDate = loanDisbursementDetails.expectedDisbursementDate();
|
||||
}
|
||||
|
||||
if (loanDisbursementDetails.actualDisbursementDate() != null) {
|
||||
actualDisbursementDate = loanDisbursementDetails.actualDisbursementDate();
|
||||
}
|
||||
BigDecimal waivedChargeAmount = null;
|
||||
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate, actualDisbursementDate,
|
||||
loanDisbursementDetails.principal(), loan.getNetDisbursalAmount(), null, null, waivedChargeAmount));
|
||||
}
|
||||
|
||||
return disbursementData;
|
||||
}
|
||||
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* 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.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
|
||||
import org.apache.fineract.portfolio.common.domain.NthDayType;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class LoanTermVariationsMapper {
|
||||
|
||||
public BigDecimal constructLoanTermVariations(final FloatingRateDTO floatingRateDTO, final BigDecimal annualNominalInterestRate,
|
||||
final List<LoanTermVariationsData> loanTermVariations, final Loan loan) {
|
||||
for (LoanTermVariations variationTerms : loan.getLoanTermVariations()) {
|
||||
if (variationTerms.isActive()) {
|
||||
loanTermVariations.add(variationTerms.toData());
|
||||
}
|
||||
}
|
||||
|
||||
return constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations, loan);
|
||||
}
|
||||
|
||||
public LoanApplicationTerms constructLoanApplicationTerms(final ScheduleGeneratorDTO scheduleGeneratorDTO, final Loan loan) {
|
||||
final Integer loanTermFrequency = loan.getTermFrequency();
|
||||
NthDayType nthDayType = null;
|
||||
DayOfWeekType dayOfWeekType = null;
|
||||
final List<DisbursementData> disbursementData = new ArrayList<>();
|
||||
for (LoanDisbursementDetails disbursementDetails : loan.getDisbursementDetails()) {
|
||||
disbursementData.add(disbursementDetails.toData());
|
||||
}
|
||||
|
||||
Calendar calendar = scheduleGeneratorDTO.getCalendar();
|
||||
if (calendar != null) {
|
||||
nthDayType = CalendarUtils.getRepeatsOnNthDayOfMonth(calendar.getRecurrence());
|
||||
dayOfWeekType = DayOfWeekType.fromInt(CalendarUtils.getRepeatsOnDay(calendar.getRecurrence()).getValue());
|
||||
}
|
||||
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
RecalculationFrequencyType recalculationFrequencyType = null;
|
||||
InterestRecalculationCompoundingMethod compoundingMethod = null;
|
||||
RecalculationFrequencyType compoundingFrequencyType = null;
|
||||
LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
CalendarHistoryDataWrapper calendarHistoryDataWrapper;
|
||||
RepaymentStartDateType repaymentStartDateType = loan.getLoanProduct().getRepaymentStartDateType();
|
||||
boolean allowCompoundingOnEod = false;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = scheduleGeneratorDTO.getCalendarInstanceForInterestRecalculation();
|
||||
compoundingCalendarInstance = scheduleGeneratorDTO.getCompoundingCalendarInstance();
|
||||
recalculationFrequencyType = loan.getLoanInterestRecalculationDetails().getRestFrequencyType();
|
||||
compoundingMethod = loan.getLoanInterestRecalculationDetails().getInterestRecalculationCompoundingMethod();
|
||||
compoundingFrequencyType = loan.getLoanInterestRecalculationDetails().getCompoundingFrequencyType();
|
||||
rescheduleStrategyMethod = loan.getLoanInterestRecalculationDetails().getRescheduleStrategyMethod();
|
||||
allowCompoundingOnEod = loan.getLoanInterestRecalculationDetails().allowCompoundingOnEod();
|
||||
}
|
||||
calendar = scheduleGeneratorDTO.getCalendar();
|
||||
calendarHistoryDataWrapper = scheduleGeneratorDTO.getCalendarHistoryDataWrapper();
|
||||
|
||||
BigDecimal annualNominalInterestRate = loan.getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate();
|
||||
FloatingRateDTO floatingRateDTO = scheduleGeneratorDTO.getFloatingRateDTO();
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
annualNominalInterestRate = constructLoanTermVariations(floatingRateDTO, annualNominalInterestRate, loanTermVariations, loan);
|
||||
LocalDate interestChargedFromDate = loan.getInterestChargedFromDate();
|
||||
if (interestChargedFromDate == null && scheduleGeneratorDTO.isInterestChargedFromDateAsDisbursementDateEnabled()) {
|
||||
interestChargedFromDate = loan.getDisbursementDate();
|
||||
}
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getCurrency(), loanTermFrequency, loan.getTermPeriodFrequencyType(),
|
||||
nthDayType, dayOfWeekType, loan.getDisbursementDate(), loan.getExpectedFirstRepaymentOnDate(),
|
||||
scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), loan.getInArrearsTolerance(),
|
||||
loan.getLoanRepaymentScheduleDetail(), loan.getLoanProduct().isMultiDisburseLoan(), loan.getFixedEmiAmount(),
|
||||
disbursementData, loan.getMaxOutstandingLoanBalance(), interestChargedFromDate,
|
||||
loan.getLoanProduct().getPrincipalThresholdForLastInstallment(), loan.getLoanProduct().getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
loan.getLoanProduct().preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, calendar,
|
||||
loan.getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations, calendarHistoryDataWrapper,
|
||||
scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), holidayDetailDTO,
|
||||
allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
|
||||
scheduleGeneratorDTO.isInterestToBeRecoveredFirstWhenGreaterThanEMI(), loan.getFixedPrincipalPercentagePerInstallment(),
|
||||
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans(), repaymentStartDateType, loan.getSubmittedOnDate());
|
||||
}
|
||||
|
||||
private BigDecimal constructFloatingInterestRates(final BigDecimal annualNominalInterestRate, final FloatingRateDTO floatingRateDTO,
|
||||
final List<LoanTermVariationsData> loanTermVariations, final Loan loan) {
|
||||
final LocalDate dateValue = null;
|
||||
final boolean isSpecificToInstallment = false;
|
||||
BigDecimal interestRate = annualNominalInterestRate;
|
||||
if (loan.getLoanProduct().isLinkedToFloatingInterestRate()) {
|
||||
floatingRateDTO.resetInterestRateDiff();
|
||||
Collection<FloatingRatePeriodData> applicableRates = loan.getLoanProduct().fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getBusinessLocalDate();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanVariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate(), dateValue, isSpecificToInstallment);
|
||||
if (!DateUtils.isBefore(interestRateStartDate, periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
interestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
return interestRate;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-1
@@ -29,6 +29,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@@ -36,6 +37,7 @@ public class LoanScheduleService {
|
||||
|
||||
private final LoanChargeService loanChargeService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanMapper loanMapper;
|
||||
private final LoanTransactionProcessingService loadTransactionProcessingService;
|
||||
private final LoanScheduleComponent loanSchedule;
|
||||
|
||||
@@ -43,7 +45,7 @@ public class LoanScheduleService {
|
||||
* Ability to regenerate the repayment schedule based on the loans current details/state.
|
||||
*/
|
||||
public void regenerateRepaymentSchedule(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(scheduleGeneratorDTO);
|
||||
final LoanScheduleModel loanScheduleModel = loanMapper.regenerateScheduleModel(scheduleGeneratorDTO, loan);
|
||||
if (loanScheduleModel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
+4
-2
@@ -41,6 +41,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Tra
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanTermVariationsMapper;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -49,6 +50,7 @@ import org.springframework.stereotype.Service;
|
||||
public class LoanTransactionProcessingService {
|
||||
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
|
||||
private final LoanTermVariationsMapper loanMapper;
|
||||
|
||||
public ChangedTransactionDetail processLatestTransaction(String transactionProcessingStrategyCode, LoanTransaction loanTransaction,
|
||||
TransactionCtx ctx) {
|
||||
@@ -103,7 +105,7 @@ public class LoanTransactionProcessingService {
|
||||
|
||||
final MathContext mc = MoneyHelper.getMathContext();
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(generatorDTO);
|
||||
final LoanApplicationTerms loanApplicationTerms = loanMapper.constructLoanApplicationTerms(generatorDTO, loan);
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(
|
||||
loan.getTransactionProcessingStrategyCode());
|
||||
@@ -119,7 +121,7 @@ public class LoanTransactionProcessingService {
|
||||
final MathContext mc = MoneyHelper.getMathContext();
|
||||
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
final LoanApplicationTerms loanApplicationTerms = loanMapper.constructLoanApplicationTerms(scheduleGeneratorDTO, loan);
|
||||
|
||||
final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory()
|
||||
.create(loanApplicationTerms.getLoanScheduleType(), interestMethod);
|
||||
|
||||
+56
-59
@@ -29,8 +29,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.accounting.closure.domain.GLClosure;
|
||||
import org.apache.fineract.accounting.closure.domain.GLClosureRepository;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForLoan;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForSavings;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForShares;
|
||||
@@ -68,12 +68,11 @@ import org.apache.fineract.portfolio.PortfolioProductType;
|
||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
|
||||
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionEnumData;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
@@ -92,55 +91,53 @@ public class AccountingProcessorHelper {
|
||||
private final GLClosureRepository closureRepository;
|
||||
private final GLAccountRepository glAccountRepository;
|
||||
private final OfficeRepository officeRepository;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final ClientTransactionRepository clientTransactionRepository;
|
||||
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
|
||||
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
||||
private final ChargeRepositoryWrapper chargeRepositoryWrapper;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
|
||||
public LoanDTO populateLoanDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
|
||||
final boolean upfrontAccrualBasedAccountingEnabled, final boolean periodicAccrualBasedAccountingEnabled) {
|
||||
final Long loanId = (Long) accountingBridgeData.get("loanId");
|
||||
final Long loanProductId = (Long) accountingBridgeData.get("loanProductId");
|
||||
final Long officeId = (Long) accountingBridgeData.get("officeId");
|
||||
final String currencyCode = (String) accountingBridgeData.get("currencyCode");
|
||||
public LoanDTO populateLoanDtoFromDTO(
|
||||
final org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO accountingBridgeData) {
|
||||
final Long loanId = accountingBridgeData.getLoanId();
|
||||
final Long loanProductId = accountingBridgeData.getLoanProductId();
|
||||
final Long officeId = accountingBridgeData.getOfficeId();
|
||||
final String currencyCode = accountingBridgeData.getCurrencyCode();
|
||||
final List<LoanTransactionDTO> newLoanTransactions = new ArrayList<>();
|
||||
boolean isAccountTransfer = (Boolean) accountingBridgeData.get("isAccountTransfer");
|
||||
boolean isLoanMarkedAsChargeOff = (Boolean) accountingBridgeData.get("isChargeOff");
|
||||
boolean isLoanMarkedAsFraud = (Boolean) accountingBridgeData.get("isFraud");
|
||||
final Long chargeOffReasonCodeValue = (Long) accountingBridgeData.get("chargeOffReasonCodeValue");
|
||||
boolean isAccountTransfer = accountingBridgeData.isAccountTransfer();
|
||||
boolean isLoanMarkedAsChargeOff = accountingBridgeData.isChargeOff();
|
||||
boolean isLoanMarkedAsFraud = accountingBridgeData.isFraud();
|
||||
final Long chargeOffReasonCodeValue = accountingBridgeData.getChargeOffReasonCodeValue();
|
||||
final boolean cashBasedAccountingEnabled = accountingBridgeData.isCashBasedAccountingEnabled();
|
||||
final boolean upfrontAccrualBasedAccountingEnabled = accountingBridgeData.isUpfrontAccrualBasedAccountingEnabled();
|
||||
final boolean periodicAccrualBasedAccountingEnabled = accountingBridgeData.isPeriodicAccrualBasedAccountingEnabled();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<Map<String, Object>> newTransactionsMap = (List<Map<String, Object>>) accountingBridgeData.get("newLoanTransactions");
|
||||
final List<AccountingBridgeLoanTransactionDTO> loanTransactionDTOs = accountingBridgeData.getNewLoanTransactions();
|
||||
|
||||
for (final Map<String, Object> map : newTransactionsMap) {
|
||||
final Long transactionOfficeId = (Long) map.get("officeId");
|
||||
final String transactionId = ((Long) map.get("id")).toString();
|
||||
final LocalDate transactionDate = ((LocalDate) map.get("date"));
|
||||
final LoanTransactionEnumData transactionType = (LoanTransactionEnumData) map.get("type");
|
||||
final BigDecimal amount = (BigDecimal) map.get("amount");
|
||||
final BigDecimal principal = (BigDecimal) map.get("principalPortion");
|
||||
final BigDecimal interest = (BigDecimal) map.get("interestPortion");
|
||||
final BigDecimal fees = (BigDecimal) map.get("feeChargesPortion");
|
||||
final BigDecimal penalties = (BigDecimal) map.get("penaltyChargesPortion");
|
||||
final BigDecimal overPayments = (BigDecimal) map.get("overPaymentPortion");
|
||||
final boolean reversed = (Boolean) map.get("reversed");
|
||||
final Long paymentTypeId = (Long) map.get("paymentTypeId");
|
||||
final String chargeRefundChargeType = (String) map.get("chargeRefundChargeType");
|
||||
final LoanChargeData loanChargeData = (LoanChargeData) map.get("loanChargeData");
|
||||
for (final AccountingBridgeLoanTransactionDTO loanTxnDto : loanTransactionDTOs) {
|
||||
final Long transactionOfficeId = loanTxnDto.getOfficeId();
|
||||
final String transactionId = loanTxnDto.getId().toString();
|
||||
final LocalDate transactionDate = loanTxnDto.getDate();
|
||||
final LoanTransactionEnumData transactionType = loanTxnDto.getType();
|
||||
final BigDecimal amount = loanTxnDto.getAmount();
|
||||
final BigDecimal principal = loanTxnDto.getPrincipalPortion();
|
||||
final BigDecimal interest = loanTxnDto.getInterestPortion();
|
||||
final BigDecimal fees = loanTxnDto.getFeeChargesPortion();
|
||||
final BigDecimal penalties = loanTxnDto.getPenaltyChargesPortion();
|
||||
final BigDecimal overPayments = loanTxnDto.getOverPaymentPortion();
|
||||
final boolean reversed = loanTxnDto.isReversed();
|
||||
final Long paymentTypeId = loanTxnDto.getPaymentTypeId();
|
||||
final String chargeRefundChargeType = loanTxnDto.getChargeRefundChargeType();
|
||||
final LoanChargeData loanChargeData = loanTxnDto.getLoanChargeData();
|
||||
|
||||
final List<ChargePaymentDTO> feePaymentDetails = new ArrayList<>();
|
||||
final List<ChargePaymentDTO> penaltyPaymentDetails = new ArrayList<>();
|
||||
// extract charge payment details (if exists)
|
||||
if (map.containsKey("loanChargesPaid")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<Map<String, Object>> loanChargesPaidData = (List<Map<String, Object>>) map.get("loanChargesPaid");
|
||||
for (final Map<String, Object> loanChargePaid : loanChargesPaidData) {
|
||||
final Long chargeId = (Long) loanChargePaid.get("chargeId");
|
||||
final Long loanChargeId = (Long) loanChargePaid.get("loanChargeId");
|
||||
final boolean isPenalty = (Boolean) loanChargePaid.get("isPenalty");
|
||||
final BigDecimal chargeAmountPaid = (BigDecimal) loanChargePaid.get("amount");
|
||||
if (loanTxnDto.getLoanChargesPaid() != null) {
|
||||
List<LoanChargePaidByDTO> loanChargesPaidData = loanTxnDto.getLoanChargesPaid();
|
||||
for (final LoanChargePaidByDTO loanChargePaid : loanChargesPaidData) {
|
||||
final Long chargeId = loanChargePaid.getChargeId();
|
||||
final Long loanChargeId = loanChargePaid.getLoanChargeId();
|
||||
final boolean isPenalty = loanChargePaid.getIsPenalty();
|
||||
final BigDecimal chargeAmountPaid = loanChargePaid.getAmount();
|
||||
final ChargePaymentDTO chargePaymentDTO = new ChargePaymentDTO(chargeId, chargeAmountPaid, loanChargeId);
|
||||
if (isPenalty) {
|
||||
penaltyPaymentDetails.add(chargePaymentDTO);
|
||||
@@ -150,23 +147,23 @@ public class AccountingProcessorHelper {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAccountTransfer) {
|
||||
isAccountTransfer = this.accountTransfersReadPlatformService.isAccountTransfer(Long.parseLong(transactionId),
|
||||
boolean localIsAccountTransfer = isAccountTransfer;
|
||||
if (!localIsAccountTransfer) {
|
||||
localIsAccountTransfer = this.accountTransfersReadPlatformService.isAccountTransfer(Long.parseLong(transactionId),
|
||||
PortfolioAccountType.LOAN);
|
||||
}
|
||||
|
||||
BigDecimal principalPaid = (BigDecimal) map.get("principalPaid");
|
||||
BigDecimal feePaid = (BigDecimal) map.get("feePaid");
|
||||
BigDecimal penaltyPaid = (BigDecimal) map.get("penaltyPaid");
|
||||
BigDecimal principalPaid = loanTxnDto.getPrincipalPaid();
|
||||
BigDecimal feePaid = loanTxnDto.getFeePaid();
|
||||
BigDecimal penaltyPaid = loanTxnDto.getPenaltyPaid();
|
||||
|
||||
final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
|
||||
transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, reversed,
|
||||
penaltyPaymentDetails, feePaymentDetails, isAccountTransfer, chargeRefundChargeType, loanChargeData, principalPaid,
|
||||
penaltyPaymentDetails, feePaymentDetails, localIsAccountTransfer, chargeRefundChargeType, loanChargeData, principalPaid,
|
||||
feePaid, penaltyPaid);
|
||||
Boolean isLoanToLoanTransfer = (Boolean) accountingBridgeData.get("isLoanToLoanTransfer");
|
||||
transaction.setLoanToLoanTransfer(isLoanToLoanTransfer != null && isLoanToLoanTransfer);
|
||||
newLoanTransactions.add(transaction);
|
||||
|
||||
transaction.setLoanToLoanTransfer(loanTxnDto.isLoanToLoanTransfer());
|
||||
newLoanTransactions.add(transaction);
|
||||
}
|
||||
|
||||
return new LoanDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, upfrontAccrualBasedAccountingEnabled,
|
||||
@@ -559,10 +556,10 @@ public class AccountingProcessorHelper {
|
||||
}
|
||||
|
||||
public void createAccrualBasedJournalEntriesAndReversalsForSavingsTax(final Office office, final String currencyCode,
|
||||
final AccrualAccountsForSavings accountTypeToBeDebited, final AccrualAccountsForSavings accountTypeToBeCredited,
|
||||
final Long savingsProductId, final Long paymentTypeId, final Long savingsId, final String transactionId,
|
||||
final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal, final List<TaxPaymentDTO> taxDetails) {
|
||||
|
||||
final AccountingConstants.AccrualAccountsForSavings accountTypeToBeDebited,
|
||||
final AccountingConstants.AccrualAccountsForSavings accountTypeToBeCredited, final Long savingsProductId,
|
||||
final Long paymentTypeId, final Long savingsId, final String transactionId, final LocalDate transactionDate,
|
||||
final BigDecimal amount, final Boolean isReversal, final List<TaxPaymentDTO> taxDetails) {
|
||||
for (TaxPaymentDTO taxPaymentDTO : taxDetails) {
|
||||
if (taxPaymentDTO.getAmount() != null) {
|
||||
if (taxPaymentDTO.getCreditAccountId() == null) {
|
||||
@@ -769,10 +766,10 @@ public class AccountingProcessorHelper {
|
||||
}
|
||||
|
||||
public void createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(final Office office, final String currencyCode,
|
||||
final AccrualAccountsForSavings accountTypeToBeDebited, AccrualAccountsForSavings accountTypeToBeCredited,
|
||||
final Long savingsProductId, final Long paymentTypeId, final Long loanId, final String transactionId,
|
||||
final LocalDate transactionDate, final BigDecimal totalAmount, final Boolean isReversal,
|
||||
final List<ChargePaymentDTO> chargePaymentDTOs) {
|
||||
final AccountingConstants.AccrualAccountsForSavings accountTypeToBeDebited,
|
||||
final AccountingConstants.AccrualAccountsForSavings accountTypeToBeCredited, final Long savingsProductId,
|
||||
final Long paymentTypeId, final Long loanId, final String transactionId, final LocalDate transactionDate,
|
||||
final BigDecimal totalAmount, final Boolean isReversal, final List<ChargePaymentDTO> chargePaymentDTOs) {
|
||||
// TODO Vishwas: Remove this validation, as and when appropriate Junit
|
||||
// tests are written for accounting
|
||||
/**
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@ import java.util.Map;
|
||||
import org.apache.fineract.accounting.provisioning.domain.ProvisioningEntry;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
|
||||
public interface JournalEntryWritePlatformService {
|
||||
|
||||
@@ -31,7 +32,7 @@ public interface JournalEntryWritePlatformService {
|
||||
|
||||
CommandProcessingResult revertJournalEntry(JsonCommand command);
|
||||
|
||||
void createJournalEntriesForLoan(Map<String, Object> accountingBridgeData);
|
||||
void createJournalEntriesForLoan(AccountingBridgeDataDTO accountingBridgeData);
|
||||
|
||||
void createJournalEntriesForSavings(Map<String, Object> accountingBridgeData);
|
||||
|
||||
|
||||
+6
-7
@@ -75,6 +75,7 @@ import org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepo
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.PortfolioProductType;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
@@ -480,15 +481,13 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void createJournalEntriesForLoan(final Map<String, Object> accountingBridgeData) {
|
||||
|
||||
final boolean cashBasedAccountingEnabled = (Boolean) accountingBridgeData.get("cashBasedAccountingEnabled");
|
||||
final boolean upfrontAccrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("upfrontAccrualBasedAccountingEnabled");
|
||||
final boolean periodicAccrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("periodicAccrualBasedAccountingEnabled");
|
||||
public void createJournalEntriesForLoan(final AccountingBridgeDataDTO accountingBridgeData) {
|
||||
final boolean cashBasedAccountingEnabled = accountingBridgeData.isCashBasedAccountingEnabled();
|
||||
final boolean upfrontAccrualBasedAccountingEnabled = accountingBridgeData.isUpfrontAccrualBasedAccountingEnabled();
|
||||
final boolean periodicAccrualBasedAccountingEnabled = accountingBridgeData.isPeriodicAccrualBasedAccountingEnabled();
|
||||
|
||||
if (cashBasedAccountingEnabled || upfrontAccrualBasedAccountingEnabled || periodicAccrualBasedAccountingEnabled) {
|
||||
final LoanDTO loanDTO = this.helper.populateLoanDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled,
|
||||
upfrontAccrualBasedAccountingEnabled, periodicAccrualBasedAccountingEnabled);
|
||||
final LoanDTO loanDTO = this.helper.populateLoanDtoFromDTO(accountingBridgeData);
|
||||
final AccountingProcessorForLoan accountingProcessorForLoan = this.accountingProcessorForLoanFactory
|
||||
.determineProcessor(loanDTO);
|
||||
accountingProcessorForLoan.createJournalEntriesForLoan(loanDTO);
|
||||
|
||||
+2
-8
@@ -46,10 +46,7 @@ import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -63,14 +60,11 @@ public class AccountingJournalEntryConfiguration {
|
||||
public AccountingProcessorHelper accountingProcessorHelper(JournalEntryRepository glJournalEntryRepository,
|
||||
ProductToGLAccountMappingRepository accountMappingRepository,
|
||||
FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository, GLClosureRepository closureRepository,
|
||||
GLAccountRepository glAccountRepository, OfficeRepository officeRepository, LoanTransactionRepository loanTransactionRepository,
|
||||
ClientTransactionRepository clientTransactionRepository,
|
||||
SavingsAccountTransactionRepository savingsAccountTransactionRepository,
|
||||
GLAccountRepository glAccountRepository, OfficeRepository officeRepository,
|
||||
AccountTransfersReadPlatformService accountTransfersReadPlatformService, ChargeRepositoryWrapper chargeRepositoryWrapper,
|
||||
BusinessEventNotifierService businessEventNotifierService) {
|
||||
return new AccountingProcessorHelper(glJournalEntryRepository, accountMappingRepository, financialActivityAccountRepository,
|
||||
closureRepository, glAccountRepository, officeRepository, loanTransactionRepository, clientTransactionRepository,
|
||||
savingsAccountTransactionRepository, accountTransfersReadPlatformService, chargeRepositoryWrapper,
|
||||
closureRepository, glAccountRepository, officeRepository, accountTransfersReadPlatformService, chargeRepositoryWrapper,
|
||||
businessEventNotifierService);
|
||||
}
|
||||
|
||||
|
||||
+22
-12
@@ -87,6 +87,7 @@ import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatfor
|
||||
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanRefundRequestData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
@@ -94,6 +95,7 @@ import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanForeclosureValidator;
|
||||
@@ -157,6 +159,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
private final LoanRefundService loanRefundService;
|
||||
private final LoanAccountService loanAccountService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -449,19 +452,26 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
final MonetaryCurrency currency = loanAccount.getCurrency();
|
||||
|
||||
List<Map<String, Object>> accountingBridgeData = new ArrayList<>();
|
||||
if (loanAccount.isChargedOff()) {
|
||||
accountingBridgeData = loanAccount.deriveAccountingBridgeDataForChargeOff(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer);
|
||||
} else {
|
||||
accountingBridgeData.add(loanAccount.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer));
|
||||
}
|
||||
for (Map<String, Object> accountingData : accountingBridgeData) {
|
||||
accountingData.put("isLoanToLoanTransfer", isLoanToLoanTransfer);
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingData);
|
||||
}
|
||||
final List<AccountingBridgeDataDTO> accountingBridgeDataList = loanAccountingBridgeMapper
|
||||
.deriveAccountingBridgeDataForChargeOff(currency.getCode(), existingTransactionIds, existingReversedTransactionIds,
|
||||
isAccountTransfer, loanAccount);
|
||||
if (isLoanToLoanTransfer) {
|
||||
accountingBridgeDataList.forEach(dto -> dto.getNewLoanTransactions().forEach(tx -> tx.setLoanToLoanTransfer(true)));
|
||||
}
|
||||
|
||||
for (AccountingBridgeDataDTO accountingBridgeData : accountingBridgeDataList) {
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
} else {
|
||||
final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loanAccount);
|
||||
if (isLoanToLoanTransfer) {
|
||||
accountingBridgeData.getNewLoanTransactions().forEach(tx -> tx.setLoanToLoanTransfer(true));
|
||||
}
|
||||
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkClientOrGroupActive(final Loan loan) {
|
||||
@@ -985,7 +995,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final List<LoanRepaymentScheduleInstallment> newInstallments = new ArrayList<>(loan.getRepaymentScheduleInstallments());
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
Money totalPrincipal = Money.zero(currency);
|
||||
final Money[] balances = loan.retriveIncomeForOverlappingPeriod(transactionDate);
|
||||
final Money[] balances = loan.retrieveIncomeForOverlappingPeriod(transactionDate);
|
||||
boolean isInterestComponent = true;
|
||||
for (final LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
|
||||
if (!DateUtils.isAfter(transactionDate, installment.getDueDate())) {
|
||||
|
||||
+6
-2
@@ -42,6 +42,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanTermVariationsMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
@@ -59,6 +60,7 @@ public class ApplyHolidaysToLoansTasklet implements Tasklet {
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanTermVariationsMapper loanTermVariationsMapper;
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
@@ -120,7 +122,8 @@ public class ApplyHolidaysToLoansTasklet implements Tasklet {
|
||||
private void adjustRepaymentSchedules(Loan loan, Holiday holiday, LocalDate adjustedRescheduleToDate) {
|
||||
final DefaultScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, holiday.getFromDate());
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
final LoanApplicationTerms loanApplicationTerms = loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO,
|
||||
loan);
|
||||
|
||||
// first repayment's from date is same as disbursement date.
|
||||
LocalDate tmpFromDate = loan.getDisbursementDate();
|
||||
@@ -152,7 +155,8 @@ public class ApplyHolidaysToLoansTasklet implements Tasklet {
|
||||
private void adjustAllRepaymentSchedules(Loan loan, Holiday holiday, LocalDate adjustedRescheduleToDate) {
|
||||
final DefaultScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, holiday.getFromDate());
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
final LoanApplicationTerms loanApplicationTerms = loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO,
|
||||
loan);
|
||||
|
||||
// first repayment's from date is same as disbursement date.
|
||||
LocalDate tmpFromDate = loan.getDisbursementDate();
|
||||
|
||||
+3
-1
@@ -44,6 +44,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleD
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanTermVariationsMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanScheduleValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
@@ -63,6 +64,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanTermVariationsMapper loanTermVariationsMapper;
|
||||
|
||||
@Override
|
||||
public LoanScheduleModel calculateLoanSchedule(final JsonQuery query, Boolean validateParams) {
|
||||
@@ -204,7 +206,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
private LoanApplicationTerms constructLoanApplicationTerms(final Loan loan) {
|
||||
final LocalDate recalculateFrom = null;
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
return loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
return loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO, loan);
|
||||
}
|
||||
|
||||
private Loan fetchLoan(final Long accountId) {
|
||||
|
||||
+7
-14
@@ -23,6 +23,7 @@ import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
@@ -38,32 +39,23 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplica
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanTermVariationsMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService {
|
||||
|
||||
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
|
||||
private final LoanRescheduleRequestRepositoryWrapper loanRescheduleRequestRepository;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
|
||||
|
||||
@Autowired
|
||||
public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepositoryWrapper loanRescheduleRequestRepository,
|
||||
final LoanUtilService loanUtilService,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory) {
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.loanUtilService = loanUtilService;
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
}
|
||||
private final LoanTermVariationsMapper loanTermVariationsMapper;
|
||||
|
||||
@Override
|
||||
public LoanScheduleModel previewLoanReschedule(Long requestId) {
|
||||
@@ -80,7 +72,8 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
loanRescheduleRequest.getRescheduleFromDate());
|
||||
LocalDate rescheduleFromDate = null;
|
||||
List<LoanTermVariationsData> removeLoanTermVariationsData = new ArrayList<>();
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
final LoanApplicationTerms loanApplicationTerms = loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO,
|
||||
loan);
|
||||
LoanTermVariations dueDateVariationInCurrentRequest = loanRescheduleRequest.getDueDateTermVariationIfExists();
|
||||
if (dueDateVariationInCurrentRequest != null) {
|
||||
for (LoanTermVariationsData loanTermVariation : loanApplicationTerms.getLoanTermVariations().getDueDateVariation()) {
|
||||
|
||||
+11
-4
@@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
@@ -68,6 +69,8 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepayme
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanTermVariationsMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
@@ -116,6 +119,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
private final LoanChargeService loanChargeService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanTermVariationsMapper loanTermVariationsMapper;
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
private final LoanScheduleComponent loanSchedule;
|
||||
|
||||
/**
|
||||
@@ -363,7 +368,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
|
||||
.createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
final LoanApplicationTerms loanApplicationTerms = loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO,
|
||||
loan);
|
||||
|
||||
LocalDate rescheduleFromDate = null;
|
||||
List<LoanTermVariations> activeLoanTermVariations = loan.getActiveLoanTermVariations();
|
||||
@@ -403,7 +409,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
}
|
||||
BigDecimal annualNominalInterestRate = null;
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
loan.constructLoanTermVariations(scheduleGeneratorDTO.getFloatingRateDTO(), annualNominalInterestRate, loanTermVariations);
|
||||
loanTermVariationsMapper.constructLoanTermVariations(scheduleGeneratorDTO.getFloatingRateDTO(), annualNominalInterestRate,
|
||||
loanTermVariations, loan);
|
||||
loanApplicationTerms.getLoanTermVariations().setExceptionData(loanTermVariations);
|
||||
|
||||
/*
|
||||
@@ -495,8 +502,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
private void postJournalEntries(Loan loan, List<Long> existingTransactionIds, List<Long> existingReversedTransactionIds) {
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
boolean isAccountTransfer = false;
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer);
|
||||
final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan);
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
|
||||
+27
-9
@@ -28,6 +28,7 @@ import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -90,6 +91,7 @@ import org.apache.fineract.portfolio.group.domain.GroupRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.group.exception.ClientNotInGroupException;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
||||
@@ -109,6 +111,7 @@ import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementData
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
@@ -200,6 +203,7 @@ public final class LoanApplicationValidator {
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
|
||||
private final LoanMapper loanMapper;
|
||||
|
||||
public void validateForCreate(final Loan loan) {
|
||||
final LocalDate expectedFirstRepaymentOnDate = loan.getExpectedFirstRepaymentOnDate();
|
||||
@@ -1404,12 +1408,11 @@ public final class LoanApplicationValidator {
|
||||
BigDecimal loanOutstanding = this.loanReadPlatformService
|
||||
.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, expectedDisbursementDate)
|
||||
.getAmount();
|
||||
final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount();
|
||||
final BigDecimal firstDisbursalAmount = getFirstDisbursalAmount(loan);
|
||||
if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
|
||||
"Topup loan amount should be greater than outstanding amount of loan to be closed.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1953,7 +1956,7 @@ public final class LoanApplicationValidator {
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_PARAMETERS);
|
||||
}
|
||||
|
||||
public void validateTopupLoan(Loan loan, LocalDate expectedDisbursementDate) {
|
||||
public BigDecimal validateTopupLoan(final Loan loan, final LocalDate disbursementDate) {
|
||||
final Long loanIdToClose = loan.getTopupLoanDetails().getLoanIdToClose();
|
||||
final Loan loanToClose = loanRepositoryWrapper.findNonClosedLoanThatBelongsToClient(loanIdToClose, loan.getClientId());
|
||||
if (loanToClose == null) {
|
||||
@@ -1969,16 +1972,15 @@ public final class LoanApplicationValidator {
|
||||
+ " should be after last transaction date of loan to be closed " + lastUserTransactionOnLoanToClose);
|
||||
}
|
||||
|
||||
BigDecimal loanOutstanding = loanReadPlatformService
|
||||
.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, expectedDisbursementDate).getAmount();
|
||||
final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount();
|
||||
final BigDecimal loanOutstanding = loanReadPlatformService
|
||||
.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, disbursementDate).getAmount();
|
||||
final BigDecimal firstDisbursalAmount = getFirstDisbursalAmount(loan);
|
||||
if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
|
||||
"Topup loan amount should be greater than outstanding amount of loan to be closed.");
|
||||
}
|
||||
|
||||
BigDecimal netDisbursalAmount = loan.getApprovedPrincipal().subtract(loanOutstanding);
|
||||
loan.adjustNetDisbursalAmount(netDisbursalAmount);
|
||||
return loanOutstanding;
|
||||
}
|
||||
|
||||
public void validateApproval(JsonCommand command, Long loanId) {
|
||||
@@ -2074,7 +2076,9 @@ public final class LoanApplicationValidator {
|
||||
StatusEnum.APPROVE.getValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
|
||||
|
||||
if (loan.isTopup() && loan.getClientId() != null) {
|
||||
validateTopupLoan(loan, expectedDisbursementDate);
|
||||
final BigDecimal loanOutstanding = validateTopupLoan(loan, expectedDisbursementDate);
|
||||
final BigDecimal netDisbursalAmountAdjusted = loan.getApprovedPrincipal().subtract(loanOutstanding);
|
||||
loan.adjustNetDisbursalAmount(netDisbursalAmountAdjusted);
|
||||
}
|
||||
|
||||
if (!loan.getStatus().isSubmittedAndPendingApproval()) {
|
||||
@@ -2202,4 +2206,18 @@ public final class LoanApplicationValidator {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors);
|
||||
}
|
||||
|
||||
private BigDecimal getFirstDisbursalAmount(final Loan loan) {
|
||||
BigDecimal firstDisbursalAmount;
|
||||
|
||||
if (loan.isMultiDisburmentLoan()) {
|
||||
List<DisbursementData> disbursementData = loanMapper.getDisbursementData(loan);
|
||||
Collections.sort(disbursementData);
|
||||
firstDisbursalAmount = disbursementData.get(disbursementData.size() - 1).getPrincipal();
|
||||
} else {
|
||||
firstDisbursalAmount = loan.getLoanRepaymentScheduleDetail().getPrincipal().getAmount();
|
||||
}
|
||||
return firstDisbursalAmount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-24
@@ -32,7 +32,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -60,6 +59,8 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccrualBalances;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccrualChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodData;
|
||||
@@ -79,6 +80,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepayme
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -109,6 +111,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
@Qualifier(TaskExecutorConstant.CONFIGURABLE_TASK_EXECUTOR_BEAN_NAME)
|
||||
private final ThreadPoolTaskExecutor taskExecutor;
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
|
||||
/**
|
||||
* method adds accrual for batch job "Add Periodic Accrual Transactions" and add accruals api for Loan
|
||||
@@ -413,7 +416,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
if (!isFinal || progressiveAccrual) {
|
||||
loan.setAccruedTill(isFinal ? accrualDate : tillDate);
|
||||
}
|
||||
ArrayList<Map<String, Object>> newTransactionMapping = new ArrayList<>();
|
||||
List<AccountingBridgeLoanTransactionDTO> newTransactionDTOs = new ArrayList<>();
|
||||
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
||||
accrualTransaction = loanTransactionRepository.saveAndFlush(accrualTransaction);
|
||||
if (addJournal) {
|
||||
@@ -421,11 +424,17 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction)
|
||||
: new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
|
||||
newTransactionMapping.add(accrualTransaction.toMapData(currency.getCode()));
|
||||
AccountingBridgeLoanTransactionDTO transactionDTO = loanAccountingBridgeMapper.mapToLoanTransactionData(accrualTransaction,
|
||||
currency.getCode());
|
||||
newTransactionDTOs.add(transactionDTO);
|
||||
}
|
||||
}
|
||||
if (addJournal) {
|
||||
Map<String, Object> accountingBridgeData = deriveAccountingBridgeData(loan, newTransactionMapping);
|
||||
AccountingBridgeDataDTO accountingBridgeData = new AccountingBridgeDataDTO(loan.getId(), loan.getLoanProduct().getId(),
|
||||
loan.getOfficeId(), loan.getCurrencyCode(), loan.getSummary().getTotalInterestCharged(),
|
||||
loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct(),
|
||||
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), false,
|
||||
false, false, null, newTransactionDTOs);
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
}
|
||||
@@ -1110,30 +1119,12 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
}
|
||||
|
||||
// Utility
|
||||
|
||||
private Map<String, Object> deriveAccountingBridgeData(@NotNull Loan loan, List<Map<String, Object>> newLoanTransactions) {
|
||||
final Map<String, Object> accountingBridgeData = new LinkedHashMap<>();
|
||||
accountingBridgeData.put("loanId", loan.getId());
|
||||
accountingBridgeData.put("loanProductId", loan.getLoanProduct().getId());
|
||||
accountingBridgeData.put("officeId", loan.getOfficeId());
|
||||
accountingBridgeData.put("currencyCode", loan.getCurrencyCode());
|
||||
accountingBridgeData.put("cashBasedAccountingEnabled", loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeData.put("upfrontAccrualBasedAccountingEnabled", loan.isUpfrontAccrualAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeData.put("periodicAccrualBasedAccountingEnabled", loan.isPeriodicAccrualAccountingEnabledOnLoanProduct());
|
||||
accountingBridgeData.put("isAccountTransfer", false);
|
||||
accountingBridgeData.put("isChargeOff", false);
|
||||
accountingBridgeData.put("isFraud", false);
|
||||
accountingBridgeData.put("newLoanTransactions", newLoanTransactions);
|
||||
return accountingBridgeData;
|
||||
}
|
||||
|
||||
private void postJournalEntries(final Loan loan, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
boolean isAccountTransfer = false;
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer);
|
||||
final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan);
|
||||
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
|
||||
+7
-4
@@ -90,6 +90,7 @@ import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
@@ -128,6 +129,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanSc
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator;
|
||||
@@ -178,6 +180,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanAccountService loanAccountService;
|
||||
private final LoanAdjustmentService loanAdjustmentService;
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
|
||||
private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) {
|
||||
return DateUtils.isAfter(loanCharge.getDueDate(), e.getFromDate()) && !DateUtils.isAfter(loanCharge.getDueDate(), e.getDueDate());
|
||||
@@ -929,8 +932,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(loan.getCurrency().getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, false);
|
||||
final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(
|
||||
loan.getCurrency().getCode(), existingTransactionIds, existingReversedTransactionIds, false, loan);
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
loanAccountDomainService.setLoanDelinquencyTag(loan, transactionDate);
|
||||
@@ -1302,8 +1305,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
boolean isAccountTransfer = false;
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer);
|
||||
final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan);
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
|
||||
+64
-2
@@ -24,15 +24,18 @@ import com.google.common.base.Splitter;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigurationServiceContainer;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
@@ -76,14 +79,14 @@ public class LoanDisbursementService {
|
||||
if (disbursementDataArray != null && !disbursementDataArray.isEmpty()) {
|
||||
String dateFormat;
|
||||
Locale locale = null;
|
||||
final Map<String, String> dateAndLocale = loan.getDateFormatAndLocale(jsonCommand);
|
||||
final Map<String, String> dateAndLocale = getDateFormatAndLocale(jsonCommand);
|
||||
dateFormat = dateAndLocale.get(LoanApiConstants.dateFormatParameterName);
|
||||
if (dateAndLocale.containsKey(LoanApiConstants.localeParameterName)) {
|
||||
locale = JsonParserHelper.localeFromString(dateAndLocale.get(LoanApiConstants.localeParameterName));
|
||||
}
|
||||
for (JsonElement jsonElement : disbursementDataArray) {
|
||||
final JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||
final Map<String, Object> parsedDisbursementData = loan.parseDisbursementDetails(jsonObject, dateFormat, locale);
|
||||
final Map<String, Object> parsedDisbursementData = parseDisbursementDetails(jsonObject, dateFormat, locale);
|
||||
final LocalDate expectedDisbursementDate = (LocalDate) parsedDisbursementData
|
||||
.get(LoanApiConstants.expectedDisbursementDateParameterName);
|
||||
final BigDecimal principal = (BigDecimal) parsedDisbursementData
|
||||
@@ -298,4 +301,63 @@ public class LoanDisbursementService {
|
||||
}) //
|
||||
.forEach(loanCharge -> reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge));
|
||||
}
|
||||
|
||||
// This method returns date format and locale if present in the JsonCommand
|
||||
private Map<String, String> getDateFormatAndLocale(final JsonCommand jsonCommand) {
|
||||
Map<String, String> returnObject = new HashMap<>();
|
||||
JsonElement jsonElement = jsonCommand.parsedJson();
|
||||
if (jsonElement.isJsonObject()) {
|
||||
JsonObject topLevel = jsonElement.getAsJsonObject();
|
||||
if (topLevel.has(LoanApiConstants.dateFormatParameterName)
|
||||
&& topLevel.get(LoanApiConstants.dateFormatParameterName).isJsonPrimitive()) {
|
||||
final JsonPrimitive primitive = topLevel.get(LoanApiConstants.dateFormatParameterName).getAsJsonPrimitive();
|
||||
returnObject.put(LoanApiConstants.dateFormatParameterName, primitive.getAsString());
|
||||
}
|
||||
if (topLevel.has(LoanApiConstants.localeParameterName)
|
||||
&& topLevel.get(LoanApiConstants.localeParameterName).isJsonPrimitive()) {
|
||||
final JsonPrimitive primitive = topLevel.get(LoanApiConstants.localeParameterName).getAsJsonPrimitive();
|
||||
String localeString = primitive.getAsString();
|
||||
returnObject.put(LoanApiConstants.localeParameterName, localeString);
|
||||
}
|
||||
}
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
private Map<String, Object> parseDisbursementDetails(final JsonObject jsonObject, String dateFormat, Locale locale) {
|
||||
Map<String, Object> returnObject = new HashMap<>();
|
||||
if (jsonObject.get(LoanApiConstants.expectedDisbursementDateParameterName) != null
|
||||
&& jsonObject.get(LoanApiConstants.expectedDisbursementDateParameterName).isJsonPrimitive()) {
|
||||
final JsonPrimitive primitive = jsonObject.get(LoanApiConstants.expectedDisbursementDateParameterName).getAsJsonPrimitive();
|
||||
final String valueAsString = primitive.getAsString();
|
||||
if (StringUtils.isNotBlank(valueAsString)) {
|
||||
LocalDate date = JsonParserHelper.convertFrom(valueAsString, LoanApiConstants.expectedDisbursementDateParameterName,
|
||||
dateFormat, locale);
|
||||
if (date != null) {
|
||||
returnObject.put(LoanApiConstants.expectedDisbursementDateParameterName, date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive()
|
||||
&& StringUtils.isNotBlank(jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString())) {
|
||||
BigDecimal principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal();
|
||||
returnObject.put(LoanApiConstants.disbursementPrincipalParameterName, principal);
|
||||
}
|
||||
|
||||
if (jsonObject.has(LoanApiConstants.disbursementIdParameterName)
|
||||
&& jsonObject.get(LoanApiConstants.disbursementIdParameterName).isJsonPrimitive()
|
||||
&& StringUtils.isNotBlank(jsonObject.get(LoanApiConstants.disbursementIdParameterName).getAsString())) {
|
||||
Long id = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementIdParameterName).getAsLong();
|
||||
returnObject.put(LoanApiConstants.disbursementIdParameterName, id);
|
||||
}
|
||||
|
||||
if (jsonObject.has(LoanApiConstants.loanChargeIdParameterName)
|
||||
&& jsonObject.get(LoanApiConstants.loanChargeIdParameterName).isJsonPrimitive()
|
||||
&& StringUtils.isNotBlank(jsonObject.get(LoanApiConstants.loanChargeIdParameterName).getAsString())) {
|
||||
returnObject.put(LoanApiConstants.loanChargeIdParameterName,
|
||||
jsonObject.getAsJsonPrimitive(LoanApiConstants.loanChargeIdParameterName).getAsString());
|
||||
}
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-14
@@ -18,13 +18,13 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@@ -32,23 +32,24 @@ import org.springframework.stereotype.Component;
|
||||
public class LoanJournalEntryPoster {
|
||||
|
||||
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
|
||||
public void postJournalEntries(final Loan loan, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
boolean isAccountTransfer = false;
|
||||
List<Map<String, Object>> accountingBridgeData = new ArrayList<>();
|
||||
if (loan.isChargedOff()) {
|
||||
accountingBridgeData = loan.deriveAccountingBridgeDataForChargeOff(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer);
|
||||
} else {
|
||||
accountingBridgeData.add(loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer));
|
||||
}
|
||||
for (Map<String, Object> accountingData : accountingBridgeData) {
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingData);
|
||||
}
|
||||
|
||||
if (loan.isChargedOff()) {
|
||||
List<AccountingBridgeDataDTO> accountingBridgeDataList = loanAccountingBridgeMapper.deriveAccountingBridgeDataForChargeOff(
|
||||
currency.getCode(), existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan);
|
||||
for (AccountingBridgeDataDTO accountingBridgeData : accountingBridgeDataList) {
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
} else {
|
||||
AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan);
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-5
@@ -38,7 +38,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
@@ -141,15 +141,13 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, LoanReadPlatformServiceCommon {
|
||||
|
||||
private static final String ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE = "submitted-date";
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
@@ -164,7 +162,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
private final CalendarReadPlatformService calendarReadPlatformService;
|
||||
private final StaffReadPlatformService staffReadPlatformService;
|
||||
private final PaginationHelper paginationHelper;
|
||||
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
@@ -179,6 +176,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
private final LoanTransactionRelationReadService loanTransactionRelationReadService;
|
||||
private final LoanForeclosureValidator loanForeclosureValidator;
|
||||
private final LoanTransactionMapper loanTransactionMapper;
|
||||
private final org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper loanMapper;
|
||||
private final LoanTransactionProcessingService loadTransactionProcessingService;
|
||||
|
||||
@Override
|
||||
|
||||
+13
-32
@@ -149,6 +149,7 @@ import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
@@ -163,7 +164,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachin
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
@@ -195,6 +195,8 @@ import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDoma
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
@@ -247,14 +249,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CalendarRepository calendarRepository;
|
||||
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
|
||||
private final LoanApplicationValidator loanApplicationCommandFromApiJsonHelper;
|
||||
private final LoanApplicationValidator loanApplicationValidator;
|
||||
private final AccountAssociationsRepository accountAssociationRepository;
|
||||
private final AccountTransferDetailRepository accountTransferDetailRepository;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final GuarantorDomainService guarantorDomainService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy;
|
||||
private final CodeValueRepositoryWrapper codeValueRepository;
|
||||
private final CashierTransactionDataValidator cashierTransactionDataValidator;
|
||||
private final GLIMAccountInfoRepository glimRepository;
|
||||
@@ -280,6 +281,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final LoanAccountService loanAccountService;
|
||||
private final LoanJournalEntryPoster journalEntryPoster;
|
||||
private final LoanAdjustmentService loanAdjustmentService;
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
private final LoanMapper loanMapper;
|
||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||
|
||||
@Transactional
|
||||
@@ -378,27 +381,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
|
||||
|
||||
if (loan.isTopup() && loan.getClientId() != null) {
|
||||
final Long loanIdToClose = loan.getTopupLoanDetails().getLoanIdToClose();
|
||||
final Loan loanToClose = this.loanRepositoryWrapper.findNonClosedLoanThatBelongsToClient(loanIdToClose, loan.getClientId());
|
||||
if (loanToClose == null) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.to.be.closed.with.topup.is.not.active",
|
||||
"Loan to be closed with this topup is not active.");
|
||||
}
|
||||
final LocalDate lastUserTransactionOnLoanToClose = loanToClose.getLastUserTransactionDate();
|
||||
if (DateUtils.isBefore(loan.getDisbursementDate(), lastUserTransactionOnLoanToClose)) {
|
||||
throw new GeneralPlatformDomainRuleException(
|
||||
"error.msg.loan.disbursal.date.should.be.after.last.transaction.date.of.loan.to.be.closed",
|
||||
"Disbursal date of this loan application " + loan.getDisbursementDate()
|
||||
+ " should be after last transaction date of loan to be closed " + lastUserTransactionOnLoanToClose);
|
||||
}
|
||||
|
||||
BigDecimal loanOutstanding = this.loanReadPlatformService
|
||||
.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, actualDisbursementDate).getAmount();
|
||||
final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount();
|
||||
if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
|
||||
"Topup loan amount should be greater than outstanding amount of loan to be closed.");
|
||||
}
|
||||
final BigDecimal loanOutstanding = loanApplicationValidator.validateTopupLoan(loan, actualDisbursementDate);
|
||||
|
||||
amountToDisburse = disburseAmount.minus(loanOutstanding);
|
||||
disburseLoanToLoan(loan, command, loanOutstanding);
|
||||
@@ -625,7 +608,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
private void createAndSaveLoanScheduleArchive(final Loan loan, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
LoanRescheduleRequest loanRescheduleRequest = null;
|
||||
LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(scheduleGeneratorDTO);
|
||||
LoanScheduleModel loanScheduleModel = loanMapper.regenerateScheduleModel(scheduleGeneratorDTO, loan);
|
||||
List<LoanRepaymentScheduleInstallment> installments = retrieveRepaymentScheduleFromModel(loanScheduleModel);
|
||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(installments, loan, loanRescheduleRequest);
|
||||
}
|
||||
@@ -922,11 +905,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
this.accountTransfersWritePlatformService.reverseAllTransactions(loanId, PortfolioAccountType.LOAN);
|
||||
String noteText;
|
||||
createNote(loan, command, changes);
|
||||
boolean isAccountTransfer = false;
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer);
|
||||
final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan);
|
||||
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoDisbursalBusinessEvent(loan));
|
||||
@@ -2353,8 +2335,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("approvedLoanAmount", element);
|
||||
|
||||
loanApplicationCommandFromApiJsonHelper.validateLoanMultiDisbursementDate(element, baseDataValidator, expectedDisbursementDate,
|
||||
principal);
|
||||
loanApplicationValidator.validateLoanMultiDisbursementDate(element, baseDataValidator, expectedDisbursementDate, principal);
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
@@ -2791,8 +2772,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
createNote(loan, command, changes);
|
||||
boolean isAccountTransfer = false;
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
|
||||
existingReversedTransactionIds, isAccountTransfer);
|
||||
final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan);
|
||||
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoLastDisbursalBusinessEvent(loan));
|
||||
|
||||
+23
-18
@@ -83,8 +83,10 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationTransitionValidator;
|
||||
@@ -162,7 +164,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
@Configuration
|
||||
public class LoanAccountConfiguration {
|
||||
@@ -302,7 +303,8 @@ public class LoanAccountConfiguration {
|
||||
LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanChargeValidator loanChargeValidator,
|
||||
LoanScheduleService loanScheduleService, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||
LoanAccountService loanAccountService, LoanAdjustmentService loanAdjustmentService) {
|
||||
LoanAccountService loanAccountService, LoanAdjustmentService loanAdjustmentService,
|
||||
LoanAccountingBridgeMapper loanAccountingBridgeMapper) {
|
||||
return new LoanChargeWritePlatformServiceImpl(loanChargeApiJsonValidator, loanAssembler, chargeRepository,
|
||||
businessEventNotifierService, loanTransactionRepository, accountTransfersWritePlatformService, loanRepositoryWrapper,
|
||||
journalEntryWritePlatformService, loanAccountDomainService, loanChargeRepository, loanWritePlatformService, loanUtilService,
|
||||
@@ -310,7 +312,8 @@ public class LoanAccountConfiguration {
|
||||
configurationDomainService, loanRepaymentScheduleTransactionProcessorFactory, externalIdFactory,
|
||||
accountTransferDetailRepository, loanChargeAssembler, paymentDetailWritePlatformService, noteRepository,
|
||||
loanAccrualTransactionBusinessEventService, loanAccrualsProcessingService, loanDownPaymentTransactionValidator,
|
||||
loanChargeValidator, loanScheduleService, reprocessLoanTransactionsService, loanAccountService, loanAdjustmentService);
|
||||
loanChargeValidator, loanScheduleService, reprocessLoanTransactionsService, loanAccountService, loanAdjustmentService,
|
||||
loanAccountingBridgeMapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -322,23 +325,23 @@ public class LoanAccountConfiguration {
|
||||
FundReadPlatformService fundReadPlatformService, ChargeReadPlatformService chargeReadPlatformService,
|
||||
CodeValueReadPlatformService codeValueReadPlatformService, CalendarReadPlatformService calendarReadPlatformService,
|
||||
StaffReadPlatformService staffReadPlatformService, PaginationHelper paginationHelper,
|
||||
NamedParameterJdbcTemplate namedParameterJdbcTemplate, PaymentTypeReadPlatformService paymentTypeReadPlatformService,
|
||||
PaymentTypeReadPlatformService paymentTypeReadPlatformService,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
FloatingRatesReadPlatformService floatingRatesReadPlatformService, LoanUtilService loanUtilService,
|
||||
ConfigurationDomainService configurationDomainService, AccountDetailsReadPlatformService accountDetailsReadPlatformService,
|
||||
ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator,
|
||||
DelinquencyReadPlatformService delinquencyReadPlatformService, LoanTransactionRepository loanTransactionRepository,
|
||||
LoanChargePaidByReadService loanChargePaidByReadService, LoanTransactionRelationReadService loanTransactionRelationReadService,
|
||||
LoanForeclosureValidator loanForeclosureValidator, LoanTransactionMapper loanTransactionMapper,
|
||||
LoanForeclosureValidator loanForeclosureValidator, LoanTransactionMapper loanTransactionMapper, LoanMapper loanMapper,
|
||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
||||
return new LoanReadPlatformServiceImpl(jdbcTemplate, context, loanRepositoryWrapper, applicationCurrencyRepository,
|
||||
loanProductReadPlatformService, clientReadPlatformService, groupReadPlatformService, loanDropdownReadPlatformService,
|
||||
fundReadPlatformService, chargeReadPlatformService, codeValueReadPlatformService, calendarReadPlatformService,
|
||||
staffReadPlatformService, paginationHelper, namedParameterJdbcTemplate, paymentTypeReadPlatformService,
|
||||
staffReadPlatformService, paginationHelper, paymentTypeReadPlatformService,
|
||||
loanRepaymentScheduleTransactionProcessorFactory, floatingRatesReadPlatformService, loanUtilService,
|
||||
configurationDomainService, accountDetailsReadPlatformService, columnValidator, sqlGenerator,
|
||||
delinquencyReadPlatformService, loanTransactionRepository, loanChargePaidByReadService, loanTransactionRelationReadService,
|
||||
loanForeclosureValidator, loanTransactionMapper, loanTransactionProcessingService);
|
||||
loanForeclosureValidator, loanTransactionMapper, loanMapper, loanTransactionProcessingService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -387,9 +390,9 @@ public class LoanAccountConfiguration {
|
||||
AccountTransferDetailRepository accountTransferDetailRepository, BusinessEventNotifierService businessEventNotifierService,
|
||||
GuarantorDomainService guarantorDomainService, LoanUtilService loanUtilService,
|
||||
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy, CodeValueRepositoryWrapper codeValueRepository,
|
||||
CashierTransactionDataValidator cashierTransactionDataValidator, GLIMAccountInfoRepository glimRepository,
|
||||
LoanRepository loanRepository, RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler,
|
||||
CodeValueRepositoryWrapper codeValueRepository, CashierTransactionDataValidator cashierTransactionDataValidator,
|
||||
GLIMAccountInfoRepository glimRepository, LoanRepository loanRepository,
|
||||
RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler,
|
||||
PostDatedChecksRepository postDatedChecksRepository,
|
||||
LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, LoanAccountLockService loanAccountLockService,
|
||||
@@ -401,6 +404,7 @@ public class LoanAccountConfiguration {
|
||||
LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanAccountService loanAccountService,
|
||||
LoanJournalEntryPoster journalEntryPoster, LoanAdjustmentService loanAdjustmentService,
|
||||
LoanAccountingBridgeMapper loanAccountingBridgeMapper, LoanMapper loanMapper,
|
||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
||||
return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer,
|
||||
loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository,
|
||||
@@ -409,14 +413,14 @@ public class LoanAccountConfiguration {
|
||||
accountTransfersWritePlatformService, accountTransfersReadPlatformService, accountAssociationsReadPlatformService,
|
||||
loanReadPlatformService, fromApiJsonHelper, calendarRepository, loanScheduleHistoryWritePlatformService,
|
||||
loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
|
||||
guarantorDomainService, loanUtilService, entityDatatableChecksWritePlatformService, transactionProcessingStrategy,
|
||||
codeValueRepository, cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
|
||||
guarantorDomainService, loanUtilService, entityDatatableChecksWritePlatformService, codeValueRepository,
|
||||
cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
|
||||
postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
|
||||
loanAccountLockService, externalIdFactory, loanAccrualTransactionBusinessEventService, errorHandler,
|
||||
loanDownPaymentHandlerService, loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator,
|
||||
loanDownPaymentTransactionValidator, loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService,
|
||||
reprocessLoanTransactionsService, loanAccountService, journalEntryPoster, loanAdjustmentService,
|
||||
loanTransactionProcessingService);
|
||||
reprocessLoanTransactionsService, loanAccountService, journalEntryPoster, loanAdjustmentService, loanAccountingBridgeMapper,
|
||||
loanMapper, loanTransactionProcessingService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -461,10 +465,11 @@ public class LoanAccountConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanScheduleService.class)
|
||||
public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||
LoanTransactionProcessingService loanTransactionProcessingService, LoanScheduleComponent loanSchedule) {
|
||||
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanTransactionProcessingService, loanSchedule);
|
||||
public LoanScheduleService loanScheduleService(final LoanChargeService loanChargeService,
|
||||
final ReprocessLoanTransactionsService reprocessLoanTransactionsService, final LoanMapper loanMapper,
|
||||
final LoanTransactionProcessingService loanTransactionProcessingService, LoanScheduleComponent loanSchedule) {
|
||||
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanMapper, loanTransactionProcessingService,
|
||||
loanSchedule);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+3
-2
@@ -18,10 +18,11 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.LocalDate;
|
||||
@@ -89,7 +90,7 @@ public class LoanAccrualsProcessingServiceImplTest {
|
||||
verify(loan, times(1)).isClosed();
|
||||
|
||||
verify(loanTransactionRepository, never()).saveAndFlush(any());
|
||||
verify(journalEntryWritePlatformService, never()).createJournalEntriesForLoan(any());
|
||||
verifyNoInteractions(journalEntryWritePlatformService);
|
||||
verify(businessEventNotifierService, never()).notifyPostBusinessEvent(any());
|
||||
verify(loan, never()).addLoanTransaction(any());
|
||||
}
|
||||
|
||||
+30
-1
@@ -19,7 +19,11 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -27,6 +31,8 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
@@ -38,6 +44,7 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
@@ -46,6 +53,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
@@ -71,6 +79,7 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
private static final LocalDate BUSINESS_DATE_AFTER = LocalDate.of(2024, 2, 26);
|
||||
private static final LocalDate BUSINESS_DATE_ON = MATURITY_DATE;
|
||||
private static final LocalDate BUSINESS_DATE_BEFORE = LocalDate.of(2024, 2, 14);
|
||||
private static final String CURRENCY_CODE = "USD";
|
||||
|
||||
@InjectMocks
|
||||
private LoanChargeWritePlatformServiceImpl loanChargeWritePlatformService;
|
||||
@@ -138,19 +147,35 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
@Mock
|
||||
private LoanAccountService loanAccountService;
|
||||
|
||||
@Mock
|
||||
private LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
||||
when(chargeRepository.findOneWithNotFoundDetection(anyLong())).thenReturn(chargeDefinition);
|
||||
when(chargeDefinition.getChargeTimeType()).thenReturn(SPECIFIED_DUE_DATE);
|
||||
when(chargeDefinition.getCurrencyCode()).thenReturn(CURRENCY_CODE);
|
||||
when(loanChargeAssembler.createNewFromJson(loan, chargeDefinition, jsonCommand)).thenReturn(loanCharge);
|
||||
when(loan.repaymentScheduleDetail()).thenReturn(loanRepaymentScheduleDetail);
|
||||
when(loan.hasCurrencyCodeOf(any())).thenReturn(true);
|
||||
when(loan.hasCurrencyCodeOf(CURRENCY_CODE)).thenReturn(true);
|
||||
when(loanCharge.getChargePaymentMode()).thenReturn(ChargePaymentMode.REGULAR);
|
||||
when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
||||
when(loanChargeRepository.saveAndFlush(any(LoanCharge.class))).thenReturn(loanCharge);
|
||||
when(loan.getCurrency()).thenReturn(monetaryCurrency);
|
||||
when(monetaryCurrency.getCode()).thenReturn(CURRENCY_CODE);
|
||||
when(loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(any())).thenReturn(loan);
|
||||
List<Long> existingTransactionIds = new ArrayList<>();
|
||||
List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
when(loan.findExistingTransactionIds()).thenReturn(existingTransactionIds);
|
||||
when(loan.findExistingReversedTransactionIds()).thenReturn(existingReversedTransactionIds);
|
||||
|
||||
when(loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()).thenReturn(false);
|
||||
when(loan.isCashBasedAccountingEnabledOnLoanProduct()).thenReturn(false);
|
||||
when(loan.isUpfrontAccrualAccountingEnabledOnLoanProduct()).thenReturn(false);
|
||||
|
||||
when(loanAccountingBridgeMapper.deriveAccountingBridgeData(anyString(), anyList(), anyList(), anyBoolean(), any(Loan.class))).thenReturn(new AccountingBridgeDataDTO());
|
||||
doNothing().when(journalEntryWritePlatformService).createJournalEntriesForLoan(any(AccountingBridgeDataDTO.class));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@@ -160,6 +185,10 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
when(loan.getMaturityDate()).thenReturn(maturityDate);
|
||||
when(loan.handleChargeAppliedTransaction(loanCharge, null)).thenReturn(loanTransaction);
|
||||
|
||||
if (isAccrualExpected) {
|
||||
when(loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()).thenReturn(true);
|
||||
}
|
||||
|
||||
try (MockedStatic<DateUtils> mockedDateUtils = mockStatic(DateUtils.class)) {
|
||||
mockedDateUtils.when(DateUtils::getBusinessLocalDate).thenReturn(businessDate);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user