FINERACT-2090: Restructure Loan creation and modification
This commit is contained in:
+20
-16
@@ -143,47 +143,51 @@ public enum ChargeTimeType {
|
||||
}
|
||||
|
||||
public boolean isTimeOfDisbursement() {
|
||||
return ChargeTimeType.DISBURSEMENT.getValue().equals(this.value);
|
||||
return ChargeTimeType.DISBURSEMENT.equals(this);
|
||||
}
|
||||
|
||||
public boolean isOnSpecifiedDueDate() {
|
||||
return this.value.equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
return this.equals(ChargeTimeType.SPECIFIED_DUE_DATE);
|
||||
}
|
||||
|
||||
public boolean isSavingsActivation() {
|
||||
return this.value.equals(ChargeTimeType.SAVINGS_ACTIVATION.getValue());
|
||||
return this.equals(ChargeTimeType.SAVINGS_ACTIVATION);
|
||||
}
|
||||
|
||||
public boolean isSavingsClosure() {
|
||||
return this.value.equals(ChargeTimeType.SAVINGS_CLOSURE.getValue());
|
||||
return this.equals(ChargeTimeType.SAVINGS_CLOSURE);
|
||||
}
|
||||
|
||||
public boolean isWithdrawalFee() {
|
||||
return this.value.equals(ChargeTimeType.WITHDRAWAL_FEE.getValue());
|
||||
return this.equals(ChargeTimeType.WITHDRAWAL_FEE);
|
||||
}
|
||||
|
||||
public boolean isSavingsNoActivityFee() {
|
||||
return this.value.equals(ChargeTimeType.SAVINGS_NOACTIVITY_FEE.getValue());
|
||||
return this.equals(ChargeTimeType.SAVINGS_NOACTIVITY_FEE);
|
||||
}
|
||||
|
||||
public boolean isAnnualFee() {
|
||||
return this.value.equals(ChargeTimeType.ANNUAL_FEE.getValue());
|
||||
return this.equals(ChargeTimeType.ANNUAL_FEE);
|
||||
}
|
||||
|
||||
public boolean isMonthlyFee() {
|
||||
return this.value.equals(ChargeTimeType.MONTHLY_FEE.getValue());
|
||||
return this.equals(ChargeTimeType.MONTHLY_FEE);
|
||||
}
|
||||
|
||||
public boolean isWeeklyFee() {
|
||||
return this.value.equals(ChargeTimeType.WEEKLY_FEE.getValue());
|
||||
return this.equals(ChargeTimeType.WEEKLY_FEE);
|
||||
}
|
||||
|
||||
public boolean isInstalmentFee() {
|
||||
return this.value.equals(ChargeTimeType.INSTALMENT_FEE.getValue());
|
||||
return this.equals(ChargeTimeType.INSTALMENT_FEE);
|
||||
}
|
||||
|
||||
public boolean isSpecifiedDueDate() {
|
||||
return this.equals(ChargeTimeType.SPECIFIED_DUE_DATE);
|
||||
}
|
||||
|
||||
public boolean isOverdueInstallment() {
|
||||
return this.value.equals(ChargeTimeType.OVERDUE_INSTALLMENT.getValue());
|
||||
return this.equals(ChargeTimeType.OVERDUE_INSTALLMENT);
|
||||
}
|
||||
|
||||
public boolean isAllowedLoanChargeTime() {
|
||||
@@ -200,22 +204,22 @@ public enum ChargeTimeType {
|
||||
}
|
||||
|
||||
public boolean isOverdraftFee() {
|
||||
return this.value.equals(ChargeTimeType.OVERDRAFT_FEE.getValue());
|
||||
return this.equals(ChargeTimeType.OVERDRAFT_FEE);
|
||||
}
|
||||
|
||||
public boolean isTrancheDisbursement() {
|
||||
return this.value.equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue());
|
||||
return this.equals(ChargeTimeType.TRANCHE_DISBURSEMENT);
|
||||
}
|
||||
|
||||
public boolean isShareAccountActivation() {
|
||||
return this.value.equals(ChargeTimeType.SHAREACCOUNT_ACTIVATION.getValue());
|
||||
return this.equals(ChargeTimeType.SHAREACCOUNT_ACTIVATION);
|
||||
}
|
||||
|
||||
public boolean isSharesPurchase() {
|
||||
return this.value.equals(ChargeTimeType.SHARE_PURCHASE.getValue());
|
||||
return this.equals(ChargeTimeType.SHARE_PURCHASE);
|
||||
}
|
||||
|
||||
public boolean isSharesRedeem() {
|
||||
return this.value.equals(ChargeTimeType.SHARE_REDEEM.getValue());
|
||||
return this.equals(ChargeTimeType.SHARE_REDEEM);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -20,7 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.api;
|
||||
|
||||
public interface LoanApiConstants {
|
||||
|
||||
String emiAmountParameterName = "fixedEmiAmount";
|
||||
String fixedEmiAmountParameterName = "fixedEmiAmount";
|
||||
String maxOutstandingBalanceParameterName = "maxOutstandingLoanBalance";
|
||||
String disbursementDataParameterName = "disbursementData";
|
||||
String expectedDisbursementDateParameterName = "expectedDisbursementDate";
|
||||
@@ -36,6 +36,7 @@ public interface LoanApiConstants {
|
||||
String amountParameterName = "amount";
|
||||
String chargeTimeTypeParameterName = "chargeTimeType";
|
||||
String chargeCalculationTypeParameterName = "chargeCalculationType";
|
||||
String chargePaymentModeParameterName = "chargePaymentMode";
|
||||
String principalDisbursedParameterName = "transactionAmount";
|
||||
String chargesParameterName = "charges";
|
||||
String loanIdTobeApproved = "loanId";
|
||||
|
||||
+14
-28
@@ -18,44 +18,30 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.command;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Data;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
|
||||
/**
|
||||
* Java object representation of {@link LoanCharge} API JSON.
|
||||
*/
|
||||
public class LoanChargeCommand implements Comparable<LoanChargeCommand> {
|
||||
@Data
|
||||
public class LoanChargeCommand implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
private final Long chargeId;
|
||||
private final BigDecimal amount;
|
||||
private Long id;
|
||||
private Long chargeId;
|
||||
private BigDecimal amount;
|
||||
@SuppressWarnings("unused")
|
||||
private final Integer chargeTimeType;
|
||||
private Integer chargeTimeType;
|
||||
@SuppressWarnings("unused")
|
||||
private final Integer chargeCalculationType;
|
||||
private Integer chargeCalculationType;
|
||||
@SuppressWarnings("unused")
|
||||
private final LocalDate dueDate;
|
||||
|
||||
public LoanChargeCommand(final Long id, final Long chargeId, final BigDecimal amount, final Integer chargeTimeType,
|
||||
final Integer chargeCalculationType, final LocalDate dueDate) {
|
||||
this.id = id;
|
||||
this.chargeId = chargeId;
|
||||
this.amount = amount;
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.dueDate = dueDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressFBWarnings(value = "EQ_COMPARETO_USE_OBJECT_EQUALS", justification = "TODO: fix this! See: https://stackoverflow.com/questions/2609037/findbugs-how-to-solve-eq-compareto-use-object-equals")
|
||||
public int compareTo(final LoanChargeCommand o) {
|
||||
int comparison = this.chargeId.compareTo(o.chargeId);
|
||||
if (comparison == 0) {
|
||||
comparison = this.amount.compareTo(o.amount);
|
||||
}
|
||||
return comparison;
|
||||
}
|
||||
private LocalDate dueDate;
|
||||
}
|
||||
|
||||
+9
-9
@@ -19,20 +19,20 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class LoanCollateralManagementData {
|
||||
|
||||
private final Long clientCollateralId;
|
||||
private Long clientCollateralId;
|
||||
|
||||
private final BigDecimal quantity;
|
||||
private BigDecimal quantity;
|
||||
|
||||
private final BigDecimal total;
|
||||
private BigDecimal total;
|
||||
|
||||
private final BigDecimal totalCollateral;
|
||||
private BigDecimal totalCollateral;
|
||||
|
||||
private final Long id;
|
||||
private Long id;
|
||||
}
|
||||
|
||||
+96
-445
@@ -60,6 +60,8 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import lombok.Getter;
|
||||
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;
|
||||
@@ -104,10 +106,8 @@ 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.command.LoanChargeCommand;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanCollateralManagementData;
|
||||
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;
|
||||
@@ -128,7 +128,6 @@ import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementData
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.UndoLastTrancheDisbursementException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
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;
|
||||
@@ -153,6 +152,8 @@ import org.apache.fineract.useradministration.domain.AppUser;
|
||||
@Entity
|
||||
@Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
|
||||
@UniqueConstraint(columnNames = { "external_id" }, name = "loan_externalid_UNIQUE") })
|
||||
@Setter
|
||||
@Getter
|
||||
public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
public static final String RECALCULATE_LOAN_SCHEDULE = "recalculateLoanSchedule";
|
||||
@@ -475,62 +476,73 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
@Column(name = "enable_installment_level_delinquency", nullable = false)
|
||||
private boolean enableInstallmentLevelDelinquency = false;
|
||||
|
||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final AccountType loanType,
|
||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||
final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
||||
final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final BigDecimal fixedEmiAmount,
|
||||
final LoanRepaymentScheduleTransactionProcessor transactionProcessingStrategy,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
|
||||
final Set<LoanCollateralManagement> collateral, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
return new Loan(accountNo, client, null, loanType, fund, officer, loanPurpose, transactionProcessingStrategyCode, loanProduct,
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
||||
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
|
||||
final Boolean enableInstallmentLevelDelinquency, final LocalDate submittedOnDate) {
|
||||
return new Loan(accountNo, client, null, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, null, loanCharges, collateral, null, fixedEmiAmount, disbursementDetails,
|
||||
maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel,
|
||||
enableInstallmentLevelDelinquency, submittedOnDate);
|
||||
}
|
||||
|
||||
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
|
||||
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final AccountType loanType,
|
||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||
final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
||||
final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting,
|
||||
final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates,
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
return new Loan(accountNo, null, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategyCode, loanProduct,
|
||||
loanRepaymentScheduleDetail, null, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
|
||||
final LoanRepaymentScheduleTransactionProcessor transactionProcessingStrategy,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
|
||||
final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
||||
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
|
||||
final Boolean enableInstallmentLevelDelinquency, final LocalDate submittedOnDate) {
|
||||
return new Loan(accountNo, null, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, null, loanCharges, null, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails,
|
||||
maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
|
||||
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel,
|
||||
enableInstallmentLevelDelinquency, submittedOnDate);
|
||||
}
|
||||
|
||||
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
|
||||
final Integer loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||
final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
||||
final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting,
|
||||
final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates,
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategyCode, loanProduct,
|
||||
loanRepaymentScheduleDetail, null, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
|
||||
final AccountType loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||
final LoanRepaymentScheduleTransactionProcessor transactionProcessingStrategy,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
|
||||
final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
||||
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
|
||||
final Boolean enableInstallmentLevelDelinquency, final LocalDate submittedOnDate) {
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, null, loanCharges, null, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails,
|
||||
maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
|
||||
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel,
|
||||
enableInstallmentLevelDelinquency, submittedOnDate);
|
||||
}
|
||||
|
||||
protected Loan() {
|
||||
this.client = null;
|
||||
}
|
||||
|
||||
private Loan(final String accountNo, final Client client, final Group group, final Integer loanType, final Fund fund,
|
||||
final Staff loanOfficer, final CodeValue loanPurpose, final String transactionProcessingStrategyCode,
|
||||
final LoanProduct loanProduct, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus,
|
||||
final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting,
|
||||
final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates,
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
private Loan(final String accountNo, final Client client, final Group group, final AccountType loanType, final Fund fund,
|
||||
final Staff loanOfficer, final CodeValue loanPurpose,
|
||||
final LoanRepaymentScheduleTransactionProcessor transactionProcessingStrategy, final LoanProduct loanProduct,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus, final Set<LoanCharge> loanCharges,
|
||||
final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
||||
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
|
||||
final Boolean enableInstallmentLevelDelinquency, final LocalDate submittedOnDate) {
|
||||
|
||||
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
|
||||
this.loanRepaymentScheduleDetail.validateRepaymentPeriodWithGraceSettings();
|
||||
|
||||
this.isFloatingInterestRate = isFloatingInterestRate;
|
||||
this.interestRateDifferential = interestRateDifferential;
|
||||
@@ -543,12 +555,14 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
this.client = client;
|
||||
this.group = group;
|
||||
this.loanType = loanType;
|
||||
this.loanType = loanType.getValue();
|
||||
this.fund = fund;
|
||||
this.loanOfficer = loanOfficer;
|
||||
this.loanPurpose = loanPurpose;
|
||||
|
||||
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
|
||||
this.transactionProcessingStrategyCode = transactionProcessingStrategy.getCode();
|
||||
this.transactionProcessingStrategyName = transactionProcessingStrategy.getName();
|
||||
|
||||
this.loanProduct = loanProduct;
|
||||
if (loanStatus != null) {
|
||||
this.loanStatus = loanStatus.getValue();
|
||||
@@ -563,7 +577,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
this.summary = new LoanSummary();
|
||||
}
|
||||
|
||||
if (loanType.equals(1) && collateral != null && !collateral.isEmpty()) {
|
||||
if (loanType.isIndividualAccount() && collateral != null && !collateral.isEmpty()) {
|
||||
this.loanCollateralManagements = associateWithThisLoan(collateral);
|
||||
} else {
|
||||
this.loanCollateralManagements = null;
|
||||
@@ -573,7 +587,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
this.syncDisbursementWithMeeting = syncDisbursementWithMeeting;
|
||||
this.fixedEmiAmount = fixedEmiAmount;
|
||||
this.maxOutstandingLoanBalance = maxOutstandingLoanBalance;
|
||||
this.disbursementDetails = disbursementDetails;
|
||||
if (disbursementDetails != null) {
|
||||
this.disbursementDetails = disbursementDetails;
|
||||
for (final LoanDisbursementDetails loanDisbursementDetails : disbursementDetails) {
|
||||
loanDisbursementDetails.updateLoan(this);
|
||||
}
|
||||
}
|
||||
this.approvedPrincipal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount();
|
||||
this.createStandingInstructionAtDisbursement = createStandingInstructionAtDisbursement;
|
||||
|
||||
@@ -590,7 +609,26 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
// Add net get net disbursal amount from charges and principal
|
||||
this.netDisbursalAmount = this.approvedPrincipal.subtract(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
this.loanStatus = LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue();
|
||||
this.externalId = externalId;
|
||||
this.termFrequency = loanApplicationTerms.getLoanTermFrequency();
|
||||
this.termPeriodFrequencyType = loanApplicationTerms.getLoanTermPeriodFrequencyType().getValue();
|
||||
this.expectedDisbursementDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
this.expectedFirstRepaymentOnDate = loanApplicationTerms.getRepaymentStartFromDate();
|
||||
this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate();
|
||||
this.submittedOnDate = submittedOnDate != null ? submittedOnDate : DateUtils.getBusinessLocalDate();
|
||||
|
||||
updateLoanSchedule(loanScheduleModel);
|
||||
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
|
||||
// Copy interest recalculation settings if interest recalculation is enabled
|
||||
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) {
|
||||
this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails
|
||||
.createFrom(this.loanProduct.getProductInterestRecalculationDetails());
|
||||
this.loanInterestRecalculationDetails.updateLoan(this);
|
||||
}
|
||||
this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency;
|
||||
}
|
||||
|
||||
public Integer getNumberOfRepayments() {
|
||||
@@ -652,10 +690,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return this.accountNumberRequiresAutoGeneration;
|
||||
}
|
||||
|
||||
public void setAccountNumberRequiresAutoGeneration(final boolean accountNumberRequiresAutoGeneration) {
|
||||
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
|
||||
}
|
||||
|
||||
public void addLoanCharge(final LoanCharge loanCharge) {
|
||||
|
||||
if (isChargesAdditionAllowed() && loanCharge.isDueAtDisbursement()) {
|
||||
@@ -1460,274 +1494,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
doPostLoanTransactionChecks(getLastUserTransactionDate(), loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
public Map<String, Object> loanApplicationModification(final JsonCommand command, final Set<LoanCharge> possiblyModifedLoanCharges,
|
||||
final Set<LoanCollateralManagement> possiblyModifiedLoanCollateralItems, final AprCalculator aprCalculator,
|
||||
boolean isChargesModified, final LoanProduct loanProduct) {
|
||||
|
||||
final Map<String, Object> actualChanges = this.loanRepaymentScheduleDetail.updateLoanApplicationAttributes(command, aprCalculator);
|
||||
final MonetaryCurrency currency = new MonetaryCurrency(loanProduct.getCurrency().getCode(),
|
||||
loanProduct.getCurrency().getDigitsAfterDecimal(), loanProduct.getCurrency().getCurrencyInMultiplesOf());
|
||||
this.loanRepaymentScheduleDetail.updateCurrency(currency);
|
||||
|
||||
if (!actualChanges.isEmpty()) {
|
||||
final boolean recalculateLoanSchedule = !(actualChanges.size() == 1 && actualChanges.containsKey(IN_ARREARS_TOLERANCE));
|
||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, recalculateLoanSchedule);
|
||||
isChargesModified = true;
|
||||
}
|
||||
|
||||
final String dateFormatAsInput = command.dateFormat();
|
||||
final String localeAsInput = command.locale();
|
||||
|
||||
final String accountNoParamName = ACCOUNT_NO;
|
||||
if (command.isChangeInStringParameterNamed(accountNoParamName, this.accountNumber)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(accountNoParamName);
|
||||
actualChanges.put(accountNoParamName, newValue);
|
||||
this.accountNumber = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(CREATE_STANDING_INSTRUCTION_AT_DISBURSEMENT,
|
||||
shouldCreateStandingInstructionAtDisbursement())) {
|
||||
final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(CREATE_STANDING_INSTRUCTION_AT_DISBURSEMENT);
|
||||
actualChanges.put(CREATE_STANDING_INSTRUCTION_AT_DISBURSEMENT, valueAsInput);
|
||||
this.createStandingInstructionAtDisbursement = valueAsInput;
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(EXTERNAL_ID, this.externalId.getValue())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(EXTERNAL_ID);
|
||||
ExternalId externalId = ExternalIdFactory.produce(newValue);
|
||||
if (externalId.isEmpty() && TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
actualChanges.put(EXTERNAL_ID, externalId);
|
||||
this.externalId = externalId;
|
||||
}
|
||||
|
||||
// add clientId, groupId and loanType changes to actual changes
|
||||
|
||||
final Long clientId = this.client == null ? null : this.client.getId();
|
||||
if (command.isChangeInLongParameterNamed(CLIENT_ID, clientId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(CLIENT_ID);
|
||||
actualChanges.put(CLIENT_ID, newValue);
|
||||
}
|
||||
|
||||
// FIXME: AA - We may require separate api command to move loan from one
|
||||
// group to another
|
||||
final Long groupId = this.group == null ? null : this.group.getId();
|
||||
if (command.isChangeInLongParameterNamed(GROUP_ID, groupId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(GROUP_ID);
|
||||
actualChanges.put(GROUP_ID, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(PRODUCT_ID, this.loanProduct.getId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(PRODUCT_ID);
|
||||
actualChanges.put(PRODUCT_ID, newValue);
|
||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(IS_FLOATING_INTEREST_RATE, this.isFloatingInterestRate)) {
|
||||
final Boolean newValue = command.booleanObjectValueOfParameterNamed(IS_FLOATING_INTEREST_RATE);
|
||||
actualChanges.put(IS_FLOATING_INTEREST_RATE, newValue);
|
||||
this.isFloatingInterestRate = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(INTEREST_RATE_DIFFERENTIAL, this.interestRateDifferential)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(INTEREST_RATE_DIFFERENTIAL);
|
||||
actualChanges.put(INTEREST_RATE_DIFFERENTIAL, newValue);
|
||||
this.interestRateDifferential = newValue;
|
||||
}
|
||||
|
||||
Long existingFundId = null;
|
||||
if (this.fund != null) {
|
||||
existingFundId = this.fund.getId();
|
||||
}
|
||||
if (command.isChangeInLongParameterNamed(FUND_ID, existingFundId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(FUND_ID);
|
||||
actualChanges.put(FUND_ID, newValue);
|
||||
}
|
||||
|
||||
Long existingLoanOfficerId = null;
|
||||
if (this.loanOfficer != null) {
|
||||
existingLoanOfficerId = this.loanOfficer.getId();
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(LOAN_OFFICER_ID, existingLoanOfficerId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(LOAN_OFFICER_ID);
|
||||
actualChanges.put(LOAN_OFFICER_ID, newValue);
|
||||
}
|
||||
|
||||
Long existingLoanPurposeId = null;
|
||||
if (this.loanPurpose != null) {
|
||||
existingLoanPurposeId = this.loanPurpose.getId();
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(LOAN_PURPOSE_ID, existingLoanPurposeId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(LOAN_PURPOSE_ID);
|
||||
actualChanges.put(LOAN_PURPOSE_ID, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(TRANSACTION_PROCESSING_STRATEGY_CODE, transactionProcessingStrategyCode)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(TRANSACTION_PROCESSING_STRATEGY_CODE);
|
||||
actualChanges.put(TRANSACTION_PROCESSING_STRATEGY_CODE, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(SUBMITTED_ON_DATE, getSubmittedOnDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(SUBMITTED_ON_DATE);
|
||||
actualChanges.put(SUBMITTED_ON_DATE, valueAsInput);
|
||||
actualChanges.put(DATE_FORMAT, dateFormatAsInput);
|
||||
actualChanges.put(LOCALE, localeAsInput);
|
||||
|
||||
this.submittedOnDate = command.localDateValueOfParameterNamed(SUBMITTED_ON_DATE);
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(EXPECTED_DISBURSEMENT_DATE, getExpectedDisbursedOnLocalDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(EXPECTED_DISBURSEMENT_DATE);
|
||||
actualChanges.put(EXPECTED_DISBURSEMENT_DATE, valueAsInput);
|
||||
actualChanges.put(DATE_FORMAT, dateFormatAsInput);
|
||||
actualChanges.put(LOCALE, localeAsInput);
|
||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
||||
|
||||
this.expectedDisbursementDate = command.localDateValueOfParameterNamed(EXPECTED_DISBURSEMENT_DATE);
|
||||
removeFirstDisbursementTransaction();
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(REPAYMENTS_STARTING_FROM_DATE, getExpectedFirstRepaymentOnDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(REPAYMENTS_STARTING_FROM_DATE);
|
||||
actualChanges.put(REPAYMENTS_STARTING_FROM_DATE, valueAsInput);
|
||||
actualChanges.put(DATE_FORMAT, dateFormatAsInput);
|
||||
actualChanges.put(LOCALE, localeAsInput);
|
||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
||||
|
||||
this.expectedFirstRepaymentOnDate = command.localDateValueOfParameterNamed(REPAYMENTS_STARTING_FROM_DATE);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(SYNC_DISBURSEMENT_WITH_MEETING, isSyncDisbursementWithMeeting())) {
|
||||
final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(SYNC_DISBURSEMENT_WITH_MEETING);
|
||||
actualChanges.put(SYNC_DISBURSEMENT_WITH_MEETING, valueAsInput);
|
||||
this.syncDisbursementWithMeeting = valueAsInput;
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(INTEREST_CHARGED_FROM_DATE, getInterestChargedFromDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(INTEREST_CHARGED_FROM_DATE);
|
||||
actualChanges.put(INTEREST_CHARGED_FROM_DATE, valueAsInput);
|
||||
actualChanges.put(DATE_FORMAT, dateFormatAsInput);
|
||||
actualChanges.put(LOCALE, localeAsInput);
|
||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
||||
|
||||
this.interestChargedFromDate = command.localDateValueOfParameterNamed(INTEREST_CHARGED_FROM_DATE);
|
||||
}
|
||||
|
||||
// the comparison should be done with the tenant date
|
||||
// (DateUtils.getBusinessDate()) and not the server date (new
|
||||
// LocalDate())
|
||||
if (DateUtils.isDateInTheFuture(getSubmittedOnDate())) {
|
||||
final String errorMessage = "The date on which a loan is submitted cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, getSubmittedOnDate());
|
||||
}
|
||||
|
||||
if (this.client != null && DateUtils.isBefore(getSubmittedOnDate(), this.client.getActivationDate())) {
|
||||
final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date.";
|
||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage,
|
||||
getSubmittedOnDate());
|
||||
} else if (this.group != null && DateUtils.isBefore(getSubmittedOnDate(), this.group.getActivationDate())) {
|
||||
final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date.";
|
||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage,
|
||||
getSubmittedOnDate());
|
||||
}
|
||||
|
||||
if (DateUtils.isAfter(getSubmittedOnDate(), getExpectedDisbursedOnLocalDate())) {
|
||||
final String errorMessage = "The date on which a loan is submitted cannot be after its expected disbursement date: "
|
||||
+ getExpectedDisbursedOnLocalDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.after.expected.disbursement.date", errorMessage,
|
||||
getSubmittedOnDate(), getExpectedDisbursedOnLocalDate());
|
||||
}
|
||||
|
||||
if (isChargesModified) {
|
||||
actualChanges.put(PARAM_CHARGES, getLoanCharges(possiblyModifedLoanCharges));
|
||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
||||
}
|
||||
|
||||
if (command.parameterExists(PARAM_COLLATERAL) && possiblyModifiedLoanCollateralItems != null
|
||||
&& !possiblyModifiedLoanCollateralItems.isEmpty()) {
|
||||
Set<LoanCollateralManagement> loanCollateralManagements = this.loanCollateralManagements;
|
||||
boolean isTrue = possiblyModifiedLoanCollateralItems.equals(loanCollateralManagements);
|
||||
|
||||
if (!isTrue) {
|
||||
actualChanges.put(PARAM_COLLATERAL, getLoanCollateralDataFormCommand(possiblyModifiedLoanCollateralItems));
|
||||
}
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LOAN_TERM_FREQUENCY, this.termFrequency)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LOAN_TERM_FREQUENCY);
|
||||
actualChanges.put(LOAN_TERM_FREQUENCY, newValue);
|
||||
this.termFrequency = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LOAN_TERM_FREQUENCY_TYPE, this.termPeriodFrequencyType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LOAN_TERM_FREQUENCY_TYPE);
|
||||
final PeriodFrequencyType newTermPeriodFrequencyType = PeriodFrequencyType.fromInt(newValue);
|
||||
actualChanges.put(LOAN_TERM_FREQUENCY_TYPE, newTermPeriodFrequencyType.getValue());
|
||||
this.termPeriodFrequencyType = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(PRINCIPAL, this.approvedPrincipal)) {
|
||||
this.approvedPrincipal = command.bigDecimalValueOfParameterNamed(PRINCIPAL);
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(PRINCIPAL, this.proposedPrincipal)) {
|
||||
this.proposedPrincipal = command.bigDecimalValueOfParameterNamed(PRINCIPAL);
|
||||
}
|
||||
|
||||
if (loanProduct.isMultiDisburseLoan()) {
|
||||
updateDisbursementDetails(command, actualChanges);
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
this.maxOutstandingLoanBalance)) {
|
||||
this.maxOutstandingLoanBalance = command
|
||||
.bigDecimalValueOfParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName);
|
||||
}
|
||||
final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
|
||||
|
||||
if (loanProduct.isDisallowExpectedDisbursements()) {
|
||||
if (disbursementDataArray != null && !disbursementDataArray.isEmpty()) {
|
||||
final String errorMessage = "For this loan product, disbursement details are not allowed";
|
||||
throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage);
|
||||
}
|
||||
} else {
|
||||
if (disbursementDataArray == null || disbursementDataArray.size() == 0) {
|
||||
final String errorMessage = "For this loan product, disbursement details must be provided";
|
||||
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
|
||||
}
|
||||
|
||||
if (disbursementDataArray.size() > loanProduct.maxTrancheCount()) {
|
||||
final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
|
||||
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
|
||||
loanProduct.maxTrancheCount(), disbursementDetails.size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.disbursementDetails.clear();
|
||||
}
|
||||
|
||||
if (loanProduct.isMultiDisburseLoan() || loanProduct.isCanDefineInstallmentAmount()) {
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount)) {
|
||||
this.fixedEmiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
||||
actualChanges.put(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount);
|
||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
||||
}
|
||||
} else {
|
||||
this.fixedEmiAmount = null;
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
|
||||
this.fixedPrincipalPercentagePerInstallment)) {
|
||||
this.fixedPrincipalPercentagePerInstallment = command
|
||||
.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName);
|
||||
actualChanges.put(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
|
||||
this.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public void recalculateAllCharges() {
|
||||
Set<LoanCharge> charges = this.getActiveCharges();
|
||||
int penaltyWaitPeriod = 0;
|
||||
@@ -2068,112 +1834,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return list;
|
||||
}
|
||||
|
||||
private LoanCollateralManagementData[] getLoanCollateralDataFormCommand(final Set<LoanCollateralManagement> setOfLoanCollateral) {
|
||||
|
||||
LoanCollateralManagementData[] existingLoanCollateral = null;
|
||||
|
||||
final List<LoanCollateralManagementData> loanCollateralList = new ArrayList<>();
|
||||
for (final LoanCollateralManagement loanCollateral : setOfLoanCollateral) {
|
||||
|
||||
loanCollateralList.add(loanCollateral.toCommand());
|
||||
|
||||
}
|
||||
|
||||
existingLoanCollateral = loanCollateralList.toArray(new LoanCollateralManagementData[0]);
|
||||
|
||||
return existingLoanCollateral;
|
||||
}
|
||||
|
||||
private LoanChargeCommand[] getLoanCharges(final Set<LoanCharge> setOfLoanCharges) {
|
||||
|
||||
LoanChargeCommand[] existingLoanCharges = null;
|
||||
|
||||
final List<LoanChargeCommand> loanChargesList = new ArrayList<>();
|
||||
for (final LoanCharge loanCharge : setOfLoanCharges) {
|
||||
loanChargesList.add(loanCharge.toCommand());
|
||||
}
|
||||
|
||||
existingLoanCharges = loanChargesList.toArray(new LoanChargeCommand[0]);
|
||||
|
||||
return existingLoanCharges;
|
||||
}
|
||||
|
||||
private void removeFirstDisbursementTransaction() {
|
||||
List<LoanTransaction> transactions = getLoanTransactions();
|
||||
for (final LoanTransaction loanTransaction : transactions) {
|
||||
if (loanTransaction.isDisbursement()) {
|
||||
removeLoanTransaction(loanTransaction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loanApplicationSubmittal(final LoanScheduleModel loanSchedule, final LoanApplicationTerms loanApplicationTerms,
|
||||
final LoanLifecycleStateMachine lifecycleStateMachine, final LocalDate submittedOn, final ExternalId externalId,
|
||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final boolean allowTransactionsOnNonWorkingDay) {
|
||||
updateLoanSchedule(loanSchedule);
|
||||
|
||||
lifecycleStateMachine.transition(LoanEvent.LOAN_CREATED, this);
|
||||
|
||||
this.externalId = externalId;
|
||||
this.termFrequency = loanApplicationTerms.getLoanTermFrequency();
|
||||
this.termPeriodFrequencyType = loanApplicationTerms.getLoanTermPeriodFrequencyType().getValue();
|
||||
this.submittedOnDate = submittedOn;
|
||||
this.expectedDisbursementDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
this.expectedFirstRepaymentOnDate = loanApplicationTerms.getRepaymentStartFromDate();
|
||||
this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate();
|
||||
|
||||
updateLoanScheduleDependentDerivedFields();
|
||||
|
||||
if (DateUtils.isDateInTheFuture(submittedOn)) {
|
||||
final String errorMessage = "The date on which a loan is submitted cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, submittedOn,
|
||||
DateUtils.getBusinessLocalDate());
|
||||
}
|
||||
|
||||
if (this.client != null && this.client.isActivatedAfter(submittedOn)) {
|
||||
final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date.";
|
||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, submittedOn,
|
||||
client.getActivationDate());
|
||||
}
|
||||
|
||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CREATED, submittedOn);
|
||||
|
||||
if (this.group != null && this.group.isActivatedAfter(submittedOn)) {
|
||||
final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date.";
|
||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, submittedOn,
|
||||
group.getActivationDate());
|
||||
}
|
||||
|
||||
if (DateUtils.isAfter(submittedOn, getExpectedDisbursedOnLocalDate())) {
|
||||
final String errorMessage = "The date on which a loan is submitted cannot be after its expected disbursement date: "
|
||||
+ getExpectedDisbursedOnLocalDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.after.expected.disbursement.date", errorMessage,
|
||||
submittedOn, getExpectedDisbursedOnLocalDate());
|
||||
}
|
||||
|
||||
// charges are optional
|
||||
int penaltyWaitPeriod = 0;
|
||||
for (final LoanCharge loanCharge : getActiveCharges()) {
|
||||
recalculateLoanCharge(loanCharge, penaltyWaitPeriod);
|
||||
}
|
||||
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
|
||||
// validate if disbursement date is a holiday or a non-working day
|
||||
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
|
||||
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
|
||||
|
||||
// Copy interest recalculation settings if interest recalculation is enabled
|
||||
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) {
|
||||
this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails
|
||||
.createFrom(this.loanProduct.getProductInterestRecalculationDetails());
|
||||
this.loanInterestRecalculationDetails.updateLoan(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private LocalDate determineExpectedMaturityDate() {
|
||||
List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments().stream()
|
||||
.filter(i -> !i.isDownPayment() && !i.isAdditional()).toList();
|
||||
@@ -4876,7 +4536,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
break;
|
||||
}
|
||||
return dueRepaymentPeriodDate.minusDays(1);// get 2n-1 range date from
|
||||
// startDate
|
||||
// startDate
|
||||
}
|
||||
|
||||
private void validateDisbursementDateIsOnNonWorkingDay(final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) {
|
||||
@@ -4945,7 +4605,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
public void updateInterestRateFrequencyType() {
|
||||
this.loanRepaymentScheduleDetail.updateInterestPeriodFrequencyType(this.loanProduct.getInterestPeriodFrequencyType());
|
||||
this.loanRepaymentScheduleDetail.setInterestPeriodFrequencyType(this.loanProduct.getInterestPeriodFrequencyType());
|
||||
}
|
||||
|
||||
public Integer getTermFrequency() {
|
||||
@@ -4975,14 +4635,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
this.loanStatus = loanStatus;
|
||||
}
|
||||
|
||||
public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final WorkingDays workingDays,
|
||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final boolean allowTransactionsOnNonWorkingDay) {
|
||||
// validate if disbursement date is a holiday or a non-working day
|
||||
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
|
||||
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
|
||||
|
||||
}
|
||||
|
||||
private void validateActivityNotBeforeClientOrGroupTransferDate(final LoanEvent event, final LocalDate activityDate) {
|
||||
if (this.client != null && this.client.getOfficeJoiningDate() != null) {
|
||||
final LocalDate clientOfficeJoiningDate = this.client.getOfficeJoiningDate();
|
||||
@@ -4991,11 +4643,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
String action = null;
|
||||
String postfix = null;
|
||||
switch (event) {
|
||||
case LOAN_CREATED:
|
||||
errorMessage = "The date on which a loan is submitted cannot be earlier than client's transfer date to this office";
|
||||
action = "submittal";
|
||||
postfix = "cannot.be.before.client.transfer.date";
|
||||
break;
|
||||
case LOAN_APPROVED:
|
||||
errorMessage = "The date on which a loan is approved cannot be earlier than client's transfer date to this office";
|
||||
action = "approval";
|
||||
@@ -5400,6 +5047,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return currentDisbursementDetails;
|
||||
}
|
||||
|
||||
public void clearDisbursementDetails() {
|
||||
this.disbursementDetails.clear();
|
||||
}
|
||||
|
||||
public LoanDisbursementDetails getDisbursementDetails(final LocalDate transactionDate, final BigDecimal transactionAmount) {
|
||||
for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
|
||||
if (!disbursementDetail.isReversed() && disbursementDetail.getDisbursementDate().equals(transactionDate)
|
||||
@@ -5525,7 +5176,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
public void updateIsInterestRecalculationEnabled() {
|
||||
this.loanRepaymentScheduleDetail.updateIsInterestRecalculationEnabled(isInterestRecalculationEnabledForProduct());
|
||||
this.loanRepaymentScheduleDetail.setInterestRecalculationEnabled(isInterestRecalculationEnabledForProduct());
|
||||
}
|
||||
|
||||
public LoanInterestRecalculationDetails loanInterestRecalculationDetails() {
|
||||
@@ -6196,11 +5847,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
// loanCalendar = loanCalendarInstance.getCalendar();
|
||||
// }
|
||||
|
||||
final Integer graceOnPrincipalPayment = this.loanRepaymentScheduleDetail.graceOnPrincipalPayment();
|
||||
final Integer graceOnInterestPayment = this.loanRepaymentScheduleDetail.graceOnInterestPayment();
|
||||
final Integer graceOnInterestCharged = this.loanRepaymentScheduleDetail.graceOnInterestCharged();
|
||||
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.getGraceOnDueDate();
|
||||
final Integer graceOnArrearsAgeing = this.loanRepaymentScheduleDetail.getGraceOnArrearsAgeing();
|
||||
|
||||
final Money inArrearsToleranceMoney = this.loanRepaymentScheduleDetail.getInArrearsTolerance();
|
||||
final BigDecimal emiAmount = getFixedEmiAmount();
|
||||
@@ -6258,7 +5909,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
public void updateNumberOfRepayments(Integer numberOfRepayments) {
|
||||
this.loanRepaymentScheduleDetail.updateNumberOfRepayments(numberOfRepayments);
|
||||
this.loanRepaymentScheduleDetail.setNumberOfRepayments(numberOfRepayments);
|
||||
}
|
||||
|
||||
public void updateRescheduledOnDate(LocalDate rescheduledOnDate) {
|
||||
@@ -7134,8 +6785,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return rates;
|
||||
}
|
||||
|
||||
public Integer getLoanType() {
|
||||
return loanType;
|
||||
public AccountType getLoanType() {
|
||||
return AccountType.fromInt(loanType);
|
||||
}
|
||||
|
||||
public void setLoanType(Integer loanType) {
|
||||
|
||||
-5
@@ -54,7 +54,6 @@ import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeWithoutMandatoryFieldException;
|
||||
import org.apache.fineract.portfolio.loanaccount.command.LoanChargeCommand;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||
@@ -509,10 +508,6 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return value.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
|
||||
public LoanChargeCommand toCommand() {
|
||||
return new LoanChargeCommand(getId(), this.charge.getId(), this.amount, this.chargeTime, this.chargeCalculation, getDueLocalDate());
|
||||
}
|
||||
|
||||
public LocalDate getDueLocalDate() {
|
||||
return this.dueDate;
|
||||
}
|
||||
|
||||
+5
-5
@@ -306,10 +306,10 @@ public final class LoanApplicationTerms {
|
||||
final Money principalMoney = loanProductRelatedDetail.getPrincipal();
|
||||
|
||||
//
|
||||
final Integer graceOnPrincipalPayment = loanProductRelatedDetail.graceOnPrincipalPayment();
|
||||
final Integer recurringMoratoriumOnPrincipalPeriods = loanProductRelatedDetail.recurringMoratoriumOnPrincipalPeriods();
|
||||
final Integer graceOnInterestPayment = loanProductRelatedDetail.graceOnInterestPayment();
|
||||
final Integer graceOnInterestCharged = loanProductRelatedDetail.graceOnInterestCharged();
|
||||
final Integer graceOnPrincipalPayment = loanProductRelatedDetail.getGraceOnPrincipalPayment();
|
||||
final Integer recurringMoratoriumOnPrincipalPeriods = loanProductRelatedDetail.getRecurringMoratoriumOnPrincipalPeriods();
|
||||
final Integer graceOnInterestPayment = loanProductRelatedDetail.getGraceOnInterestPayment();
|
||||
final Integer graceOnInterestCharged = loanProductRelatedDetail.getGraceOnInterestCharged();
|
||||
|
||||
// Interest recalculation settings
|
||||
final DaysInMonthType daysInMonthType = loanProductRelatedDetail.fetchDaysInMonthType();
|
||||
@@ -334,7 +334,7 @@ public final class LoanApplicationTerms {
|
||||
repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment,
|
||||
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate,
|
||||
inArrearsTolerance, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance,
|
||||
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
loanProductRelatedDetail.getGraceOnArrearsAgeing(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.loanaccount.command.LoanChargeCommand;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(config = MapstructMapperConfig.class)
|
||||
public interface LoanChargeMapper {
|
||||
|
||||
@Mapping(target = "id", source = "loanCharge.id")
|
||||
@Mapping(target = "chargeId", source = "loanCharge.charge.id")
|
||||
@Mapping(target = "amount", expression = "java(loanCharge.getAmount(currency).getAmount())")
|
||||
@Mapping(target = "chargeTimeType", source = "loanCharge.chargeTimeType.value")
|
||||
@Mapping(target = "chargeCalculationType", source = "loanCharge.chargeCalculation.value")
|
||||
@Mapping(target = "dueDate", source = "loanCharge.dueDate")
|
||||
LoanChargeCommand map(LoanCharge loanCharge, MonetaryCurrency currency);
|
||||
|
||||
default Set<LoanChargeCommand> map(Set<LoanCharge> loanCharge, MonetaryCurrency currency) {
|
||||
return loanCharge.stream().map(lc -> map(lc, currency)).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.util.Set;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanCollateralManagementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(config = MapstructMapperConfig.class)
|
||||
public interface LoanCollateralManagementMapper {
|
||||
|
||||
@Mapping(target = "id", source = "id")
|
||||
@Mapping(target = "clientCollateralId", source = "clientCollateralManagement.id")
|
||||
@Mapping(target = "quantity", source = "quantity")
|
||||
@Mapping(target = "total", ignore = true)
|
||||
@Mapping(target = "totalCollateral", ignore = true)
|
||||
LoanCollateralManagementData map(LoanCollateralManagement loanCollateralManagement);
|
||||
|
||||
Set<LoanCollateralManagementData> map(Set<LoanCollateralManagement> loanCollateralManagements);
|
||||
}
|
||||
+3
-5
@@ -725,8 +725,6 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength);
|
||||
|
||||
this.loanProductRelatedDetail.validateRepaymentPeriodWithGraceSettings();
|
||||
|
||||
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
|
||||
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
|
||||
defaultMaxNumberOfInstallments);
|
||||
@@ -1298,21 +1296,21 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
this.loanProductRelatedDetail.isEnableDownPayment())) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_DOWN_PAYMENT);
|
||||
actualChanges.put(LoanProductConstants.ENABLE_DOWN_PAYMENT, newValue);
|
||||
this.loanProductRelatedDetail.updateEnableDownPayment(newValue);
|
||||
this.loanProductRelatedDetail.setEnableDownPayment(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT,
|
||||
this.loanProductRelatedDetail.getDisbursedAmountPercentageForDownPayment())) {
|
||||
BigDecimal newValue = command.bigDecimalValueOfParameterNamed(LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT);
|
||||
actualChanges.put(LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, newValue);
|
||||
this.loanProductRelatedDetail.updateDisbursedAmountPercentageForDownPayment(newValue);
|
||||
this.loanProductRelatedDetail.setDisbursedAmountPercentageForDownPayment(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT,
|
||||
this.loanProductRelatedDetail.isEnableAutoRepaymentForDownPayment())) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT);
|
||||
actualChanges.put(LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, newValue);
|
||||
this.loanProductRelatedDetail.updateEnableAutoRepaymentForDownPayment(newValue);
|
||||
this.loanProductRelatedDetail.setEnableAutoRepaymentForDownPayment(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.REPAYMENT_START_DATE_TYPE,
|
||||
|
||||
+5
-5
@@ -32,13 +32,13 @@ public interface LoanProductMinimumRepaymentScheduleRelatedDetail {
|
||||
|
||||
Money getPrincipal();
|
||||
|
||||
Integer graceOnInterestCharged();
|
||||
Integer getGraceOnInterestCharged();
|
||||
|
||||
Integer graceOnInterestPayment();
|
||||
Integer getGraceOnInterestPayment();
|
||||
|
||||
Integer graceOnPrincipalPayment();
|
||||
Integer getGraceOnPrincipalPayment();
|
||||
|
||||
Integer recurringMoratoriumOnPrincipalPeriods();
|
||||
Integer getRecurringMoratoriumOnPrincipalPeriods();
|
||||
|
||||
Money getInArrearsTolerance();
|
||||
|
||||
@@ -60,5 +60,5 @@ public interface LoanProductMinimumRepaymentScheduleRelatedDetail {
|
||||
|
||||
AmortizationMethod getAmortizationMethod();
|
||||
|
||||
Integer getGraceOnDueDate();
|
||||
Integer getGraceOnArrearsAgeing();
|
||||
}
|
||||
|
||||
+9
-254
@@ -24,14 +24,11 @@ import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
@@ -48,6 +45,8 @@ import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
* a {@link Loan}.
|
||||
*/
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentScheduleRelatedDetail {
|
||||
|
||||
@Embedded
|
||||
@@ -241,82 +240,32 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
return Money.of(this.currency, this.principal);
|
||||
}
|
||||
|
||||
public void setPrincipal(BigDecimal principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer graceOnInterestCharged() {
|
||||
return this.graceOnInterestCharged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer graceOnInterestPayment() {
|
||||
return this.graceOnInterestPayment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer graceOnPrincipalPayment() {
|
||||
return this.graceOnPrincipalPayment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer recurringMoratoriumOnPrincipalPeriods() {
|
||||
return this.recurringMoratoriumOnPrincipalPeriods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Money getInArrearsTolerance() {
|
||||
return Money.of(this.currency, this.inArrearsTolerance);
|
||||
}
|
||||
|
||||
// TODO: REVIEW
|
||||
@Override
|
||||
public BigDecimal getNominalInterestRatePerPeriod() {
|
||||
return this.nominalInterestRatePerPeriod == null ? null
|
||||
: BigDecimal.valueOf(Double.parseDouble(this.nominalInterestRatePerPeriod.stripTrailingZeros().toString()));
|
||||
}
|
||||
|
||||
// TODO: REVIEW
|
||||
@Override
|
||||
public PeriodFrequencyType getInterestPeriodFrequencyType() {
|
||||
return this.interestPeriodFrequencyType == null ? PeriodFrequencyType.INVALID : this.interestPeriodFrequencyType;
|
||||
}
|
||||
|
||||
// TODO: REVIEW
|
||||
@Override
|
||||
public BigDecimal getAnnualNominalInterestRate() {
|
||||
return this.annualNominalInterestRate == null ? null
|
||||
: BigDecimal.valueOf(Double.parseDouble(this.annualNominalInterestRate.stripTrailingZeros().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterestMethod getInterestMethod() {
|
||||
return this.interestMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() {
|
||||
return this.interestCalculationPeriodMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getRepayEvery() {
|
||||
return this.repayEvery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PeriodFrequencyType getRepaymentPeriodFrequencyType() {
|
||||
return this.repaymentPeriodFrequencyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getNumberOfRepayments() {
|
||||
return this.numberOfRepayments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmortizationMethod getAmortizationMethod() {
|
||||
return this.amortizationMethod;
|
||||
}
|
||||
|
||||
// TODO: Move into assembler
|
||||
public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(20);
|
||||
@@ -367,18 +316,6 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
loanScheduleProcessingType = newLoanScheduleProcessingType;
|
||||
}
|
||||
|
||||
final Map<String, Object> loanApplicationAttributeChanges = updateLoanApplicationAttributes(command, aprCalculator);
|
||||
|
||||
actualChanges.putAll(loanApplicationAttributeChanges);
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public Map<String, Object> updateLoanApplicationAttributes(final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(20);
|
||||
|
||||
final String localeAsInput = command.locale();
|
||||
|
||||
final String principalParamName = "principal";
|
||||
if (command.isChangeInBigDecimalParameterNamed(principalParamName, this.principal)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(principalParamName);
|
||||
@@ -557,63 +494,10 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
this.isEqualAmortization = newValue;
|
||||
}
|
||||
|
||||
validateRepaymentPeriodWithGraceSettings();
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public void updateCurrency(final MonetaryCurrency currency) {
|
||||
this.currency = currency;
|
||||
}
|
||||
|
||||
public void validateRepaymentPeriodWithGraceSettings() {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanproduct");
|
||||
|
||||
if (this.numberOfRepayments <= defaultToZeroIfNull(this.graceOnPrincipalPayment)) {
|
||||
baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(this.graceOnPrincipalPayment)
|
||||
.failWithCode(".mustBeLessThan.numberOfRepayments");
|
||||
}
|
||||
|
||||
if (this.numberOfRepayments <= defaultToZeroIfNull(this.graceOnInterestPayment)) {
|
||||
baseDataValidator.reset().parameter("graceOnInterestPayment").value(this.graceOnInterestPayment)
|
||||
.failWithCode(".mustBeLessThan.numberOfRepayments");
|
||||
}
|
||||
|
||||
if (this.numberOfRepayments < defaultToZeroIfNull(this.graceOnInterestCharged)) {
|
||||
baseDataValidator.reset().parameter("graceOnInterestCharged").value(this.graceOnInterestCharged)
|
||||
.failWithCode(".mustBeLessThan.numberOfRepayments");
|
||||
}
|
||||
|
||||
int graceOnPrincipal = 0;
|
||||
if (this.getGraceOnPrincipalPayment() != null) {
|
||||
graceOnPrincipal = this.getGraceOnPrincipalPayment();
|
||||
}
|
||||
int recurMoratoriumOnPrincipal = 0;
|
||||
if (this.recurringMoratoriumOnPrincipalPeriods() != null) {
|
||||
recurMoratoriumOnPrincipal = this.recurringMoratoriumOnPrincipalPeriods();
|
||||
}
|
||||
|
||||
if ((recurMoratoriumOnPrincipal > 0) && ((this.numberOfRepayments - graceOnPrincipal) % (recurMoratoriumOnPrincipal + 1) != 1)) {
|
||||
baseDataValidator.reset().parameter("graceOnPrincipalPayments.and.recurringMoratoriumOnPrincipalPeriods")
|
||||
.value(graceOnPrincipal).value(recurMoratoriumOnPrincipal)
|
||||
.failWithCode("causes.principal.moratorium.for.last.installment");
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
private Integer defaultToZeroIfNull(final Integer value) {
|
||||
Integer result = value;
|
||||
if (value == null) {
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void updateInterestRateDerivedFields(final AprCalculator aprCalculator) {
|
||||
public void updateInterestRateDerivedFields(final AprCalculator aprCalculator) {
|
||||
this.annualNominalInterestRate = aprCalculator.calculateFrom(this.interestPeriodFrequencyType, this.nominalInterestRatePerPeriod,
|
||||
this.numberOfRepayments, this.repayEvery, this.repaymentPeriodFrequencyType);
|
||||
|
||||
@@ -623,15 +507,6 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
return this.currency.getCode().equalsIgnoreCase(currencyCode);
|
||||
}
|
||||
|
||||
public void updateInterestPeriodFrequencyType(final PeriodFrequencyType interestPeriodFrequencyType) {
|
||||
this.interestPeriodFrequencyType = interestPeriodFrequencyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getGraceOnDueDate() {
|
||||
return this.graceOnArrearsAgeing;
|
||||
}
|
||||
|
||||
public DaysInMonthType fetchDaysInMonthType() {
|
||||
return DaysInMonthType.fromInt(this.daysInMonthType);
|
||||
}
|
||||
@@ -640,129 +515,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
return DaysInYearType.fromInt(this.daysInYearType);
|
||||
}
|
||||
|
||||
public boolean isInterestRecalculationEnabled() {
|
||||
return this.isInterestRecalculationEnabled;
|
||||
}
|
||||
|
||||
public void updateIsInterestRecalculationEnabled(final boolean isInterestRecalculationEnabled) {
|
||||
this.isInterestRecalculationEnabled = isInterestRecalculationEnabled;
|
||||
}
|
||||
|
||||
public void updateNumberOfRepayments(Integer numberOfRepayments) {
|
||||
this.numberOfRepayments = numberOfRepayments;
|
||||
}
|
||||
|
||||
public Integer getGraceOnPrincipalPayment() {
|
||||
return graceOnPrincipalPayment;
|
||||
}
|
||||
|
||||
public void setGraceOnPrincipalPayment(Integer graceOnPrincipalPayment) {
|
||||
this.graceOnPrincipalPayment = graceOnPrincipalPayment;
|
||||
}
|
||||
|
||||
public Integer getGraceOnInterestPayment() {
|
||||
return graceOnInterestPayment;
|
||||
}
|
||||
|
||||
public void setGraceOnInterestPayment(Integer graceOnInterestPayment) {
|
||||
this.graceOnInterestPayment = graceOnInterestPayment;
|
||||
}
|
||||
|
||||
public Integer getGraceOnArrearsAgeing() {
|
||||
return graceOnArrearsAgeing;
|
||||
}
|
||||
|
||||
public void setGraceOnArrearsAgeing(Integer graceOnArrearsAgeing) {
|
||||
this.graceOnArrearsAgeing = graceOnArrearsAgeing;
|
||||
}
|
||||
|
||||
public void setInterestMethod(InterestMethod interestMethod) {
|
||||
this.interestMethod = interestMethod;
|
||||
}
|
||||
|
||||
public void setInterestCalculationPeriodMethod(InterestCalculationPeriodMethod interestCalculationPeriodMethod) {
|
||||
this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
|
||||
}
|
||||
|
||||
public void setRepayEvery(Integer repayEvery) {
|
||||
this.repayEvery = repayEvery;
|
||||
}
|
||||
|
||||
public void setRepaymentPeriodFrequencyType(PeriodFrequencyType repaymentPeriodFrequencyType) {
|
||||
this.repaymentPeriodFrequencyType = repaymentPeriodFrequencyType;
|
||||
}
|
||||
|
||||
public void setAmortizationMethod(AmortizationMethod amortizationMethod) {
|
||||
this.amortizationMethod = amortizationMethod;
|
||||
}
|
||||
|
||||
public void setInArrearsTolerance(BigDecimal inArrearsTolerance) {
|
||||
this.inArrearsTolerance = inArrearsTolerance;
|
||||
}
|
||||
|
||||
public BigDecimal getArrearsTolerance() {
|
||||
return this.inArrearsTolerance;
|
||||
}
|
||||
|
||||
public void updateForFloatingInterestRates() {
|
||||
this.nominalInterestRatePerPeriod = null;
|
||||
this.interestPeriodFrequencyType = PeriodFrequencyType.INVALID;
|
||||
this.annualNominalInterestRate = null;
|
||||
}
|
||||
|
||||
public boolean isAllowPartialPeriodInterestCalcualtion() {
|
||||
return this.allowPartialPeriodInterestCalcualtion;
|
||||
}
|
||||
|
||||
public boolean isEqualAmortization() {
|
||||
return isEqualAmortization;
|
||||
}
|
||||
|
||||
public void setEqualAmortization(boolean isEqualAmortization) {
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
}
|
||||
|
||||
public void setNominalInterestRatePerPeriod(BigDecimal nominalInterestRatePerPeriod) {
|
||||
this.nominalInterestRatePerPeriod = nominalInterestRatePerPeriod;
|
||||
}
|
||||
|
||||
public boolean isEnableDownPayment() {
|
||||
return enableDownPayment;
|
||||
}
|
||||
|
||||
public BigDecimal getDisbursedAmountPercentageForDownPayment() {
|
||||
return disbursedAmountPercentageForDownPayment;
|
||||
}
|
||||
|
||||
public void updateEnableDownPayment(boolean enableDownPayment) {
|
||||
this.enableDownPayment = enableDownPayment;
|
||||
}
|
||||
|
||||
public void updateDisbursedAmountPercentageForDownPayment(BigDecimal disbursedAmountPercentageForDownPayment) {
|
||||
this.disbursedAmountPercentageForDownPayment = disbursedAmountPercentageForDownPayment;
|
||||
}
|
||||
|
||||
public Boolean isEnableAutoRepaymentForDownPayment() {
|
||||
return enableAutoRepaymentForDownPayment;
|
||||
}
|
||||
|
||||
public void updateEnableAutoRepaymentForDownPayment(boolean enableAutoRepaymentForDownPayment) {
|
||||
this.enableAutoRepaymentForDownPayment = enableAutoRepaymentForDownPayment;
|
||||
}
|
||||
|
||||
public LoanScheduleType getLoanScheduleType() {
|
||||
return loanScheduleType;
|
||||
}
|
||||
|
||||
public LoanScheduleProcessingType getLoanScheduleProcessingType() {
|
||||
return loanScheduleProcessingType;
|
||||
}
|
||||
|
||||
public Integer getFixedLength() {
|
||||
return this.fixedLength;
|
||||
}
|
||||
|
||||
public void setFixedLength(Integer fixedLength) {
|
||||
this.fixedLength = fixedLength;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -175,7 +175,7 @@ public class EntityDatatableChecksWritePlatformServiceImpl implements EntityData
|
||||
List<EntityDatatableChecks> tableRequiredBeforAction = entityDatatableChecksRepository.findByEntityStatusAndProduct(entityName,
|
||||
statusCode, productId);
|
||||
|
||||
if (tableRequiredBeforAction == null || tableRequiredBeforAction.size() < 1) {
|
||||
if (tableRequiredBeforAction == null || tableRequiredBeforAction.isEmpty()) {
|
||||
tableRequiredBeforAction = entityDatatableChecksRepository.findByEntityStatusAndNoProduct(entityName, statusCode);
|
||||
}
|
||||
if (tableRequiredBeforAction != null) {
|
||||
@@ -190,7 +190,7 @@ public class EntityDatatableChecksWritePlatformServiceImpl implements EntityData
|
||||
reqDatatables.add(datatableName);
|
||||
}
|
||||
}
|
||||
if (reqDatatables.size() > 0) {
|
||||
if (!reqDatatables.isEmpty()) {
|
||||
throw new DatatableEntryRequiredException(reqDatatables.toString());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -81,7 +81,6 @@ import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstr
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferTransaction;
|
||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
||||
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
@@ -250,7 +249,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
// changes to closed
|
||||
disableStandingInstructionsLinkedToClosedLoan(loan);
|
||||
|
||||
if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
|
||||
if (loan.getLoanType().isIndividualAccount()) {
|
||||
// Mark Post Dated Check as paid.
|
||||
final Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = newRepaymentTransaction
|
||||
.getLoanTransactionToRepaymentScheduleMappings();
|
||||
|
||||
+269
-45
@@ -34,8 +34,10 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
@@ -100,6 +102,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.VariableLoanScheduleFromApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanDisbursementDetailsAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
@@ -115,10 +118,10 @@ import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyTy
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class LoanScheduleAssembler {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
@@ -137,35 +140,7 @@ public class LoanScheduleAssembler {
|
||||
private final VariableLoanScheduleFromApiJsonValidator variableLoanScheduleFromApiJsonValidator;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final LoanUtilService loanUtilService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final AprCalculator aprCalculator,
|
||||
final LoanChargeAssembler loanChargeAssembler, final CalendarRepository calendarRepository,
|
||||
final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService,
|
||||
final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService,
|
||||
final VariableLoanScheduleFromApiJsonValidator variableLoanScheduleFromApiJsonValidator,
|
||||
final CalendarInstanceRepository calendarInstanceRepository, final LoanUtilService loanUtilService) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
this.aprCalculator = aprCalculator;
|
||||
this.loanChargeAssembler = loanChargeAssembler;
|
||||
this.calendarRepository = calendarRepository;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.clientRepository = clientRepository;
|
||||
this.groupRepository = groupRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
this.variableLoanScheduleFromApiJsonValidator = variableLoanScheduleFromApiJsonValidator;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.loanUtilService = loanUtilService;
|
||||
}
|
||||
private final LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler;
|
||||
|
||||
public LoanApplicationTerms assembleLoanTerms(final JsonElement element) {
|
||||
final Long loanProductId = this.fromApiJsonHelper.extractLongNamed("productId", element);
|
||||
@@ -177,6 +152,15 @@ public class LoanScheduleAssembler {
|
||||
|
||||
private LoanApplicationTerms assembleLoanApplicationTermsFrom(final JsonElement element, final LoanProduct loanProduct) {
|
||||
|
||||
final Boolean allowOverridingAmortization = loanProduct.getLoanConfigurableAttributes().getAmortizationBoolean();
|
||||
final Boolean allowOverridingArrearsTolerance = loanProduct.getLoanConfigurableAttributes().getArrearsToleranceBoolean();
|
||||
final Boolean allowOverridingGraceOnArrearsAging = loanProduct.getLoanConfigurableAttributes().getGraceOnArrearsAgingBoolean();
|
||||
final Boolean allowOverridingInterestCalcPeriod = loanProduct.getLoanConfigurableAttributes().getInterestCalcPeriodBoolean();
|
||||
final Boolean allowOverridingInterestMethod = loanProduct.getLoanConfigurableAttributes().getInterestMethodBoolean();
|
||||
final Boolean allowOverridingGraceOnPrincipalAndInterestPayment = loanProduct.getLoanConfigurableAttributes()
|
||||
.getGraceOnPrincipalAndInterestPaymentBoolean();
|
||||
final Boolean allowOverridingRepaymentEvery = loanProduct.getLoanConfigurableAttributes().getRepaymentEveryBoolean();
|
||||
|
||||
final MonetaryCurrency currency = loanProduct.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
|
||||
@@ -186,7 +170,9 @@ public class LoanScheduleAssembler {
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(loanTermFrequencyType);
|
||||
|
||||
final Integer numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
|
||||
final Integer repaymentEvery = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
|
||||
final Integer repaymentEvery = allowOverridingRepaymentEvery
|
||||
? this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element)
|
||||
: loanProduct.getLoanProductRelatedDetail().getRepayEvery();
|
||||
final Integer repaymentFrequencyType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = PeriodFrequencyType.fromInt(repaymentFrequencyType);
|
||||
final Integer nthDay = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyNthDayType", element);
|
||||
@@ -194,7 +180,8 @@ public class LoanScheduleAssembler {
|
||||
final DayOfWeekType weekDayType = DayOfWeekType.fromInt(dayOfWeek);
|
||||
|
||||
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
|
||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);
|
||||
final AmortizationMethod amortizationMethod = allowOverridingAmortization ? AmortizationMethod.fromInt(amortizationType)
|
||||
: loanProduct.getLoanProductRelatedDetail().getAmortizationMethod();
|
||||
|
||||
boolean isEqualAmortization = false;
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isEqualAmortizationParam, element)) {
|
||||
@@ -206,12 +193,14 @@ public class LoanScheduleAssembler {
|
||||
|
||||
// interest terms
|
||||
final Integer interestType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(interestType);
|
||||
final InterestMethod interestMethod = allowOverridingInterestMethod ? InterestMethod.fromInt(interestType)
|
||||
: loanProduct.getLoanProductRelatedDetail().getInterestMethod();
|
||||
|
||||
final Integer interestCalculationPeriodType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType",
|
||||
element);
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod
|
||||
.fromInt(interestCalculationPeriodType);
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = allowOverridingInterestCalcPeriod
|
||||
? InterestCalculationPeriodMethod.fromInt(interestCalculationPeriodType)
|
||||
: loanProduct.getLoanProductRelatedDetail().getInterestCalculationPeriodMethod();
|
||||
Boolean allowPartialPeriodInterestCalcualtion = this.fromApiJsonHelper
|
||||
.extractBooleanNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, element);
|
||||
if (allowPartialPeriodInterestCalcualtion == null) {
|
||||
@@ -322,23 +311,29 @@ public class LoanScheduleAssembler {
|
||||
}
|
||||
|
||||
// grace details
|
||||
final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
|
||||
final Integer graceOnPrincipalPayment = allowOverridingGraceOnPrincipalAndInterestPayment
|
||||
? this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element)
|
||||
: loanProduct.getLoanProductRelatedDetail().getGraceOnPrincipalPayment();
|
||||
final Integer recurringMoratoriumOnPrincipalPeriods = this.fromApiJsonHelper
|
||||
.extractIntegerWithLocaleNamed("recurringMoratoriumOnPrincipalPeriods", element);
|
||||
final Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
|
||||
final Integer graceOnInterestPayment = allowOverridingGraceOnPrincipalAndInterestPayment
|
||||
? this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element)
|
||||
: loanProduct.getLoanProductRelatedDetail().getGraceOnInterestPayment();
|
||||
final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
|
||||
final LocalDate interestChargedFromDate = this.fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element);
|
||||
final Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = this.configurationDomainService
|
||||
.isInterestChargedFromDateSameAsDisbursementDate();
|
||||
|
||||
final Integer graceOnArrearsAgeing = this.fromApiJsonHelper
|
||||
.extractIntegerWithLocaleNamed(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, element);
|
||||
final Integer graceOnArrearsAgeing = allowOverridingGraceOnArrearsAging
|
||||
? this.fromApiJsonHelper.extractIntegerWithLocaleNamed(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, element)
|
||||
: loanProduct.getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
|
||||
|
||||
// other
|
||||
final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
|
||||
final Money inArrearsToleranceMoney = Money.of(currency, inArrearsTolerance);
|
||||
final Money inArrearsToleranceMoney = allowOverridingArrearsTolerance ? Money.of(currency, inArrearsTolerance)
|
||||
: loanProduct.getLoanProductRelatedDetail().getInArrearsTolerance();
|
||||
|
||||
final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
||||
final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedEmiAmountParameterName,
|
||||
element);
|
||||
final BigDecimal maxOutstandingBalance = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.maxOutstandingBalanceParameterName, element);
|
||||
@@ -638,8 +633,8 @@ public class LoanScheduleAssembler {
|
||||
}
|
||||
}
|
||||
|
||||
public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final JsonElement element) {
|
||||
final LoanApplicationTerms loanApplicationTerms = assembleLoanTerms(element);
|
||||
public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final JsonElement element, final LoanProduct loanProduct) {
|
||||
final LoanApplicationTerms loanApplicationTerms = assembleLoanApplicationTermsFrom(element, loanProduct);
|
||||
return loanApplicationTerms.toLoanProductRelatedDetail();
|
||||
}
|
||||
|
||||
@@ -671,7 +666,8 @@ public class LoanScheduleAssembler {
|
||||
validateDisbursementDateIsOnNonWorkingDay(loanApplicationTerms.getExpectedDisbursementDate(), workingDays);
|
||||
validateDisbursementDateIsOnHoliday(loanApplicationTerms.getExpectedDisbursementDate(), isHolidayEnabled, holidays);
|
||||
|
||||
List<LoanDisbursementDetails> loanDisbursementDetails = this.loanUtilService.fetchDisbursementData(element.getAsJsonObject());
|
||||
List<LoanDisbursementDetails> loanDisbursementDetails = this.loanDisbursementDetailsAssembler
|
||||
.fetchDisbursementData(element.getAsJsonObject());
|
||||
|
||||
return assembleLoanScheduleFrom(loanApplicationTerms, isHolidayEnabled, holidays, workingDays, element, loanDisbursementDetails);
|
||||
}
|
||||
@@ -769,7 +765,7 @@ public class LoanScheduleAssembler {
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
Set<LocalDate> dueDates = new TreeSet<>();
|
||||
LocalDate graceApplicable = loan.getExpectedDisbursedOnLocalDate();
|
||||
Integer graceOnPrincipal = loan.getLoanProductRelatedDetail().graceOnPrincipalPayment();
|
||||
Integer graceOnPrincipal = loan.getLoanProductRelatedDetail().getGraceOnPrincipalPayment();
|
||||
if (graceOnPrincipal == null) {
|
||||
graceOnPrincipal = 0;
|
||||
}
|
||||
@@ -1160,4 +1156,232 @@ public class LoanScheduleAssembler {
|
||||
minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateProductRelatedDetails(LoanProductRelatedDetail productRelatedDetail, Loan loan) {
|
||||
final Boolean amortization = loan.loanProduct().getLoanConfigurableAttributes().getAmortizationBoolean();
|
||||
final Boolean arrearsTolerance = loan.loanProduct().getLoanConfigurableAttributes().getArrearsToleranceBoolean();
|
||||
final Boolean graceOnArrearsAging = loan.loanProduct().getLoanConfigurableAttributes().getGraceOnArrearsAgingBoolean();
|
||||
final Boolean interestCalcPeriod = loan.loanProduct().getLoanConfigurableAttributes().getInterestCalcPeriodBoolean();
|
||||
final Boolean interestMethod = loan.loanProduct().getLoanConfigurableAttributes().getInterestMethodBoolean();
|
||||
final Boolean graceOnPrincipalAndInterestPayment = loan.loanProduct().getLoanConfigurableAttributes()
|
||||
.getGraceOnPrincipalAndInterestPaymentBoolean();
|
||||
final Boolean repaymentEvery = loan.loanProduct().getLoanConfigurableAttributes().getRepaymentEveryBoolean();
|
||||
|
||||
if (!amortization) {
|
||||
productRelatedDetail.setAmortizationMethod(loan.loanProduct().getLoanProductRelatedDetail().getAmortizationMethod());
|
||||
}
|
||||
if (!arrearsTolerance) {
|
||||
productRelatedDetail
|
||||
.setInArrearsTolerance(loan.loanProduct().getLoanProductRelatedDetail().getInArrearsTolerance().getAmount());
|
||||
}
|
||||
if (!graceOnArrearsAging) {
|
||||
productRelatedDetail.setGraceOnArrearsAgeing(loan.loanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing());
|
||||
}
|
||||
if (!interestCalcPeriod) {
|
||||
productRelatedDetail.setInterestCalculationPeriodMethod(
|
||||
loan.loanProduct().getLoanProductRelatedDetail().getInterestCalculationPeriodMethod());
|
||||
}
|
||||
if (!interestMethod) {
|
||||
productRelatedDetail.setInterestMethod(loan.loanProduct().getLoanProductRelatedDetail().getInterestMethod());
|
||||
}
|
||||
if (!graceOnPrincipalAndInterestPayment) {
|
||||
productRelatedDetail.setGraceOnInterestPayment(loan.loanProduct().getLoanProductRelatedDetail().getGraceOnInterestPayment());
|
||||
productRelatedDetail.setGraceOnPrincipalPayment(loan.loanProduct().getLoanProductRelatedDetail().getGraceOnPrincipalPayment());
|
||||
}
|
||||
if (!repaymentEvery) {
|
||||
productRelatedDetail.setRepayEvery(loan.loanProduct().getLoanProductRelatedDetail().getRepayEvery());
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLoanApplicationAttributes(JsonCommand command, Loan loan, Map<String, Object> changes) {
|
||||
final String localeAsInput = command.locale();
|
||||
|
||||
final String principalParamName = "principal";
|
||||
LoanProductRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
if (command.isChangeInBigDecimalParameterNamed(principalParamName, loanProductRelatedDetail.getPrincipal().getAmount())) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(principalParamName);
|
||||
changes.put(principalParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setPrincipal(newValue);
|
||||
}
|
||||
|
||||
final String repaymentEveryParamName = "repaymentEvery";
|
||||
if (command.isChangeInIntegerParameterNamed(repaymentEveryParamName, loanProductRelatedDetail.getRepayEvery())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(repaymentEveryParamName);
|
||||
changes.put(repaymentEveryParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setRepayEvery(newValue);
|
||||
}
|
||||
|
||||
final String repaymentFrequencyTypeParamName = "repaymentFrequencyType";
|
||||
if (command.isChangeInIntegerParameterNamed(repaymentFrequencyTypeParamName,
|
||||
loanProductRelatedDetail.getRepaymentPeriodFrequencyType().getValue())) {
|
||||
Integer newValue = command.integerValueOfParameterNamed(repaymentFrequencyTypeParamName);
|
||||
changes.put(repaymentFrequencyTypeParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setRepaymentPeriodFrequencyType(PeriodFrequencyType.fromInt(newValue));
|
||||
}
|
||||
if (PeriodFrequencyType.MONTHS.equals(loanProductRelatedDetail.getRepaymentPeriodFrequencyType())) {
|
||||
final String repaymentFrequencyNthDayTypeParamName = "repaymentFrequencyNthDayType";
|
||||
Integer newValue = command.integerValueOfParameterNamed(repaymentFrequencyNthDayTypeParamName);
|
||||
changes.put(repaymentFrequencyNthDayTypeParamName, newValue);
|
||||
|
||||
final String repaymentFrequencyDayOfWeekTypeParamName = "repaymentFrequencyDayOfWeekType";
|
||||
newValue = command.integerValueOfParameterNamed(repaymentFrequencyDayOfWeekTypeParamName);
|
||||
changes.put(repaymentFrequencyDayOfWeekTypeParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
}
|
||||
|
||||
final String numberOfRepaymentsParamName = "numberOfRepayments";
|
||||
if (command.isChangeInIntegerParameterNamed(numberOfRepaymentsParamName, loanProductRelatedDetail.getNumberOfRepayments())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(numberOfRepaymentsParamName);
|
||||
changes.put(numberOfRepaymentsParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setNumberOfRepayments(newValue);
|
||||
}
|
||||
|
||||
final String amortizationTypeParamName = "amortizationType";
|
||||
if (command.isChangeInIntegerParameterNamed(amortizationTypeParamName,
|
||||
loanProductRelatedDetail.getAmortizationMethod().getValue())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(amortizationTypeParamName);
|
||||
changes.put(amortizationTypeParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setAmortizationMethod(AmortizationMethod.fromInt(newValue));
|
||||
}
|
||||
|
||||
final String inArrearsToleranceParamName = "inArrearsTolerance";
|
||||
if (command.isChangeInBigDecimalParameterNamed(inArrearsToleranceParamName,
|
||||
loanProductRelatedDetail.getInArrearsTolerance().getAmount())) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(inArrearsToleranceParamName);
|
||||
changes.put(inArrearsToleranceParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setInArrearsTolerance(newValue);
|
||||
}
|
||||
|
||||
final String interestRatePerPeriodParamName = "interestRatePerPeriod";
|
||||
if (command.isChangeInBigDecimalParameterNamed(interestRatePerPeriodParamName,
|
||||
loanProductRelatedDetail.getNominalInterestRatePerPeriod())) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(interestRatePerPeriodParamName);
|
||||
changes.put(interestRatePerPeriodParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setNominalInterestRatePerPeriod(newValue);
|
||||
loanProductRelatedDetail.updateInterestRateDerivedFields(aprCalculator);
|
||||
}
|
||||
|
||||
final String interestRateFrequencyTypeParamName = "interestRateFrequencyType";
|
||||
final int interestPeriodFrequencyType = loanProductRelatedDetail.getInterestPeriodFrequencyType() == null
|
||||
? PeriodFrequencyType.INVALID.getValue()
|
||||
: loanProductRelatedDetail.getInterestPeriodFrequencyType().getValue();
|
||||
if (command.isChangeInIntegerParameterNamed(interestRateFrequencyTypeParamName, interestPeriodFrequencyType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(interestRateFrequencyTypeParamName);
|
||||
changes.put(interestRateFrequencyTypeParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setInterestPeriodFrequencyType(PeriodFrequencyType.fromInt(newValue));
|
||||
loanProductRelatedDetail.updateInterestRateDerivedFields(aprCalculator);
|
||||
}
|
||||
|
||||
final String interestTypeParamName = "interestType";
|
||||
if (command.isChangeInIntegerParameterNamed(interestTypeParamName, loanProductRelatedDetail.getInterestMethod().getValue())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(interestTypeParamName);
|
||||
changes.put(interestTypeParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setInterestMethod(InterestMethod.fromInt(newValue));
|
||||
}
|
||||
|
||||
final String interestCalculationPeriodTypeParamName = "interestCalculationPeriodType";
|
||||
if (command.isChangeInIntegerParameterNamed(interestCalculationPeriodTypeParamName,
|
||||
loanProductRelatedDetail.getInterestCalculationPeriodMethod().getValue())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(interestCalculationPeriodTypeParamName);
|
||||
changes.put(interestCalculationPeriodTypeParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setInterestCalculationPeriodMethod(InterestCalculationPeriodMethod.fromInt(newValue));
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
|
||||
loanProductRelatedDetail.isAllowPartialPeriodInterestCalcualtion())) {
|
||||
final boolean newValue = command
|
||||
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME);
|
||||
changes.put(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, newValue);
|
||||
loanProductRelatedDetail.setAllowPartialPeriodInterestCalcualtion(newValue);
|
||||
}
|
||||
|
||||
if (loanProductRelatedDetail.getInterestCalculationPeriodMethod().isDaily()) {
|
||||
loanProductRelatedDetail.setAllowPartialPeriodInterestCalcualtion(false);
|
||||
}
|
||||
|
||||
final String graceOnPrincipalPaymentParamName = "graceOnPrincipalPayment";
|
||||
if (command.isChangeInIntegerParameterNamed(graceOnPrincipalPaymentParamName,
|
||||
loanProductRelatedDetail.getGraceOnPrincipalPayment())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(graceOnPrincipalPaymentParamName);
|
||||
changes.put(graceOnPrincipalPaymentParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setGraceOnPrincipalPayment(newValue);
|
||||
}
|
||||
|
||||
final String recurringMoratoriumOnPrincipalPeriodsParamName = "recurringMoratoriumOnPrincipalPeriods";
|
||||
if (command.isChangeInIntegerParameterNamed(recurringMoratoriumOnPrincipalPeriodsParamName,
|
||||
loanProductRelatedDetail.getRecurringMoratoriumOnPrincipalPeriods())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(recurringMoratoriumOnPrincipalPeriodsParamName);
|
||||
changes.put(recurringMoratoriumOnPrincipalPeriodsParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setRecurringMoratoriumOnPrincipalPeriods(newValue);
|
||||
}
|
||||
|
||||
final String graceOnInterestPaymentParamName = "graceOnInterestPayment";
|
||||
if (command.isChangeInIntegerParameterNamed(graceOnInterestPaymentParamName,
|
||||
loanProductRelatedDetail.getGraceOnInterestPayment())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(graceOnInterestPaymentParamName);
|
||||
changes.put(graceOnInterestPaymentParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setGraceOnInterestPayment(newValue);
|
||||
}
|
||||
|
||||
final String graceOnInterestChargedParamName = "graceOnInterestCharged";
|
||||
if (command.isChangeInIntegerParameterNamed(graceOnInterestChargedParamName,
|
||||
loanProductRelatedDetail.getGraceOnInterestCharged())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(graceOnInterestChargedParamName);
|
||||
changes.put(graceOnInterestChargedParamName, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setGraceOnInterestCharged(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME,
|
||||
loanProductRelatedDetail.getGraceOnArrearsAgeing())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME);
|
||||
changes.put(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setGraceOnArrearsAgeing(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.DAYS_IN_MONTH_TYPE_PARAMETER_NAME,
|
||||
loanProductRelatedDetail.fetchDaysInMonthType().getValue())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.DAYS_IN_MONTH_TYPE_PARAMETER_NAME);
|
||||
changes.put(LoanProductConstants.DAYS_IN_MONTH_TYPE_PARAMETER_NAME, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setDaysInMonthType(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME,
|
||||
loanProductRelatedDetail.fetchDaysInYearType().getValue())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME);
|
||||
changes.put(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME, newValue);
|
||||
changes.put("locale", localeAsInput);
|
||||
loanProductRelatedDetail.setDaysInYearType(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME,
|
||||
loanProductRelatedDetail.isInterestRecalculationEnabled())) {
|
||||
final boolean newValue = command
|
||||
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME);
|
||||
changes.put(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME, newValue);
|
||||
loanProductRelatedDetail.setInterestRecalculationEnabled(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM,
|
||||
loanProductRelatedDetail.isEqualAmortization())) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM);
|
||||
changes.put(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, newValue);
|
||||
loanProductRelatedDetail.setEqualAmortization(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-81
@@ -24,80 +24,46 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonQuery;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
||||
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.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
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.serialization.CalculateLoanScheduleQueryFromApiJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanScheduleValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanproduct.serialization.LoanProductDataValidator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleCalculationPlatformService {
|
||||
|
||||
private final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer;
|
||||
private final LoanScheduleValidator fromApiJsonDeserializer;
|
||||
private final LoanScheduleAssembler loanScheduleAssembler;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
private final LoanProductDataValidator loanProductCommandFromApiJsonDeserializer;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final LoanApplicationCommandFromApiJsonHelper loanApiJsonDeserializer;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanApplicationValidator loanApiJsonDeserializer;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final CurrencyReadPlatformService currencyReadPlatformService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleCalculationPlatformServiceImpl(final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer,
|
||||
final LoanScheduleAssembler loanScheduleAssembler, final FromJsonHelper fromJsonHelper,
|
||||
final LoanProductRepository loanProductRepository, final LoanProductDataValidator loanProductCommandFromApiJsonDeserializer,
|
||||
final LoanReadPlatformService loanReadPlatformService, final LoanApplicationCommandFromApiJsonHelper loanApiJsonDeserializer,
|
||||
final LoanAssembler loanAssembler,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final ConfigurationDomainService configurationDomainService, final CurrencyReadPlatformService currencyReadPlatformService,
|
||||
final LoanUtilService loanUtilService) {
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
this.loanScheduleAssembler = loanScheduleAssembler;
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.loanProductCommandFromApiJsonDeserializer = loanProductCommandFromApiJsonDeserializer;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.loanApiJsonDeserializer = loanApiJsonDeserializer;
|
||||
this.loanAssembler = loanAssembler;
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.currencyReadPlatformService = currencyReadPlatformService;
|
||||
this.loanUtilService = loanUtilService;
|
||||
}
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanSummaryWrapper loanSummaryWrapper;
|
||||
|
||||
@Override
|
||||
public LoanScheduleModel calculateLoanSchedule(final JsonQuery query, Boolean validateParams) {
|
||||
@@ -105,44 +71,19 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
/***
|
||||
* TODO: Vishwas, this is probably not required, test and remove the same
|
||||
**/
|
||||
final Long productId = this.fromJsonHelper.extractLongNamed("productId", query.parsedJson());
|
||||
final LoanProduct loanProduct = this.loanProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new LoanProductNotFoundException(productId));
|
||||
|
||||
if (validateParams) {
|
||||
boolean isMeetingMandatoryForJLGLoans = configurationDomainService.isMeetingMandatoryForJLGLoans();
|
||||
this.loanApiJsonDeserializer.validateForCreate(query.json(), isMeetingMandatoryForJLGLoans, loanProduct);
|
||||
this.loanApiJsonDeserializer.validateForCreate(query);
|
||||
}
|
||||
this.fromApiJsonDeserializer.validate(query.json());
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
|
||||
if (loanProduct.isUseBorrowerCycle()) {
|
||||
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", query.parsedJson());
|
||||
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", query.parsedJson());
|
||||
Integer cycleNumber = 0;
|
||||
if (clientId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId, loanProduct.getId());
|
||||
} else if (groupId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue(), loanProduct.getId());
|
||||
}
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(query.parsedJson(), baseDataValidator, loanProduct,
|
||||
cycleNumber);
|
||||
} else {
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(query.parsedJson(), baseDataValidator, loanProduct);
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
|
||||
return this.loanScheduleAssembler.assembleLoanScheduleFrom(query.parsedJson());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFutureSchedule(LoanScheduleData loanScheduleData, final Long loanId) {
|
||||
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
final Loan loan = fetchLoan(loanId);
|
||||
|
||||
LocalDate today = DateUtils.getBusinessLocalDate();
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
@@ -210,7 +151,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public LoanScheduleData generateLoanScheduleForVariableInstallmentRequest(Long loanId, final String json) {
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
final Loan loan = fetchLoan(loanId);
|
||||
this.loanScheduleAssembler.assempleVariableScheduleFrom(loan, json);
|
||||
return constructLoanScheduleData(loan);
|
||||
}
|
||||
@@ -262,19 +203,23 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
|
||||
CurrencyData currencyData = this.currencyReadPlatformService.retrieveCurrency(currency.getCode());
|
||||
|
||||
LoanScheduleData scheduleData = new LoanScheduleData(currencyData, installmentData,
|
||||
loan.getLoanRepaymentScheduleDetail().getNumberOfRepayments(), principal.getAmount(), principal.getAmount(),
|
||||
totalInterest.getAmount(), totalCharge.getAmount(), totalPenalty.getAmount(),
|
||||
return new LoanScheduleData(currencyData, installmentData, loan.getLoanRepaymentScheduleDetail().getNumberOfRepayments(),
|
||||
principal.getAmount(), principal.getAmount(), totalInterest.getAmount(), totalCharge.getAmount(), totalPenalty.getAmount(),
|
||||
principal.plus(totalCharge).plus(totalInterest).plus(totalPenalty).getAmount());
|
||||
|
||||
return scheduleData;
|
||||
}
|
||||
|
||||
private LoanApplicationTerms constructLoanApplicationTerms(final Loan loan) {
|
||||
final LocalDate recalculateFrom = null;
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
return loanApplicationTerms;
|
||||
return loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
private Loan fetchLoan(final Long accountId) {
|
||||
final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true);
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
|
||||
return loanAccount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+628
-187
File diff suppressed because it is too large
Load Diff
+175
@@ -18,7 +18,9 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.serialization;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
@@ -27,6 +29,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -36,8 +39,20 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.monetary.exception.InvalidCurrencyException;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@@ -45,6 +60,8 @@ import org.springframework.stereotype.Component;
|
||||
public final class LoanChargeApiJsonValidator {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final LoanChargeRepository loanChargeRepository;
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
@@ -274,4 +291,162 @@ public final class LoanChargeApiJsonValidator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void validateLoanCharges(JsonElement element, LoanProduct loanProduct, DataValidatorBuilder baseDataValidator) {
|
||||
if (element.isJsonObject() && this.fromApiJsonHelper.parameterExists(LoanApiConstants.chargesParameterName, element)) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
|
||||
if (topLevelJsonElement.get(LoanApiConstants.chargesParameterName).isJsonArray()) {
|
||||
final Type arrayObjectParameterTypeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
|
||||
}.getType();
|
||||
final Set<String> supportedParameters = new HashSet<>(
|
||||
Arrays.asList(LoanApiConstants.idParameterName, LoanApiConstants.chargeIdParameterName,
|
||||
LoanApiConstants.amountParameterName, LoanApiConstants.chargeTimeTypeParameterName,
|
||||
LoanApiConstants.chargeCalculationTypeParameterName, LoanApiConstants.dueDateParamName,
|
||||
LoanApiConstants.chargePaymentModeParameterName, LoanApiConstants.externalIdParameterName));
|
||||
|
||||
final JsonArray array = topLevelJsonElement.get(LoanApiConstants.chargesParameterName).getAsJsonArray();
|
||||
for (int i = 1; i <= array.size(); i++) {
|
||||
final JsonObject loanChargeElement = array.get(i - 1).getAsJsonObject();
|
||||
final String arrayObjectJson = this.fromApiJsonHelper.toJson(loanChargeElement);
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(arrayObjectParameterTypeOfMap, arrayObjectJson,
|
||||
supportedParameters);
|
||||
|
||||
String chargeCurrencyCode;
|
||||
Charge chargeDefinition;
|
||||
ChargeTimeType chargeTime;
|
||||
ChargeCalculationType chargeCalculation;
|
||||
ChargePaymentMode chargePaymentModeEnum;
|
||||
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale);
|
||||
final Integer chargeCalculationType = this.fromApiJsonHelper.extractIntegerNamed("chargeCalculationType",
|
||||
loanChargeElement, locale);
|
||||
|
||||
final Integer chargePaymentMode = this.fromApiJsonHelper.extractIntegerNamed("chargePaymentMode", loanChargeElement,
|
||||
locale);
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.chargeIdParameterName,
|
||||
loanChargeElement);
|
||||
final Long loanChargeId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.idParameterName, element);
|
||||
if (loanChargeId == null) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.chargesParameterName)
|
||||
.parameterAtIndexArray(LoanApiConstants.chargeIdParameterName, i).value(chargeId).notNull()
|
||||
.integerGreaterThanZero();
|
||||
|
||||
} else if (chargeId == null) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.idParameterName)
|
||||
.parameterAtIndexArray(LoanApiConstants.idParameterName, i).value(loanChargeId).notNull()
|
||||
.integerGreaterThanZero();
|
||||
}
|
||||
|
||||
if (loanChargeId == null && chargeId == null) {
|
||||
return;
|
||||
}
|
||||
if (chargeId != null) {
|
||||
|
||||
chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
chargeCurrencyCode = chargeDefinition.getCurrencyCode();
|
||||
if (chargeTimeType != null) {
|
||||
chargeTime = ChargeTimeType.fromInt(chargeTimeType);
|
||||
} else {
|
||||
chargeTime = ChargeTimeType.fromInt(chargeDefinition.getChargeTimeType());
|
||||
}
|
||||
|
||||
if (chargeCalculationType != null) {
|
||||
chargeCalculation = ChargeCalculationType.fromInt(chargeCalculationType);
|
||||
} else {
|
||||
chargeCalculation = ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation());
|
||||
}
|
||||
|
||||
if (chargePaymentMode != null) {
|
||||
chargePaymentModeEnum = ChargePaymentMode.fromInt(chargePaymentMode);
|
||||
} else {
|
||||
chargePaymentModeEnum = ChargePaymentMode.fromInt(chargeDefinition.getChargePaymentMode());
|
||||
}
|
||||
} else {
|
||||
LoanCharge loanCharge = this.loanChargeRepository.findById(loanChargeId)
|
||||
.orElseThrow(() -> new LoanChargeNotFoundException(loanChargeId));
|
||||
chargeDefinition = loanCharge.getCharge();
|
||||
chargeCurrencyCode = chargeDefinition.getCurrencyCode();
|
||||
chargeTime = loanCharge.getChargeTimeType();
|
||||
chargeCalculation = loanCharge.getChargeCalculation();
|
||||
chargePaymentModeEnum = loanCharge.getChargePaymentMode();
|
||||
}
|
||||
|
||||
if (!loanProduct.hasCurrencyCodeOf(chargeCurrencyCode)) {
|
||||
final String errorMessage = "Charge and Loan must have the same currency.";
|
||||
// TODO: GeneralPlatformDomainRuleException vs PlatformApiDataValidationException
|
||||
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
|
||||
}
|
||||
if (chargeDefinition.isOverdueInstallment()) {
|
||||
final String defaultUserMessage = "Installment charge cannot be added to the loan.";
|
||||
throw new LoanChargeCannotBeAddedException("loanCharge", "overdue.charge", defaultUserMessage, null,
|
||||
chargeDefinition.getName());
|
||||
}
|
||||
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.amountParameterName,
|
||||
loanChargeElement, locale);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.chargesParameterName)
|
||||
.parameterAtIndexArray(LoanApiConstants.amountParameterName, i).value(amount).notNull().positiveAmount();
|
||||
|
||||
if (chargeTime.isSpecifiedDueDate()) {
|
||||
LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.dueDateParamName,
|
||||
loanChargeElement, dateFormat, locale);
|
||||
LocalDate expectedDisbursementDate = this.fromApiJsonHelper
|
||||
.extractLocalDateNamed(LoanApiConstants.expectedDisbursementDateParameterName, element);
|
||||
if (DateUtils.isBefore(dueDate, expectedDisbursementDate)) {
|
||||
final String defaultUserMessage = "This charge with specified due date cannot be added as the it is not in schedule range.";
|
||||
throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage,
|
||||
expectedDisbursementDate, chargeDefinition.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (chargePaymentMode != null) {
|
||||
if (chargePaymentModeEnum.isPaymentModeAccountTransfer()) {
|
||||
final Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed("linkAccountId", element);
|
||||
if (savingsAccountId == null) {
|
||||
final String errorMessage = "one of the charges requires linked savings account for payment";
|
||||
throw new LinkedAccountRequiredException("loanCharge", errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateInterestBearingLoanProductRestriction(chargeCalculation, chargeTime, loanProduct, baseDataValidator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateInterestBearingLoanProductRestriction(ChargeCalculationType chargeCalculationType, ChargeTimeType chargeTime,
|
||||
LoanProduct loanProduct, DataValidatorBuilder baseDataValidator) {
|
||||
if (loanProduct.isInterestRecalculationEnabled()) {
|
||||
String errorcode = null;
|
||||
switch (chargeCalculationType) {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
if (chargeTime.isInstalmentFee()) {
|
||||
errorcode = "installment." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE;
|
||||
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
if (chargeTime.isInstalmentFee()) {
|
||||
errorcode = "installment." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE;
|
||||
} else if (chargeTime.isSpecifiedDueDate()) {
|
||||
errorcode = "specific." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE;
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
if (chargeTime.isSpecifiedDueDate()) {
|
||||
errorcode = "specific." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (errorcode != null) {
|
||||
baseDataValidator.reset().parameter("charges").failWithCode(errorcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -59,7 +59,7 @@ import org.springframework.stereotype.Component;
|
||||
public final class LoanEventApiJsonValidator {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer;
|
||||
private final LoanApplicationValidator fromApiJsonDeserializer;
|
||||
private final LoanRepository loanRepository;
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
@@ -79,12 +79,12 @@ public final class LoanEventApiJsonValidator {
|
||||
|
||||
if (isAccountTransfer) {
|
||||
disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale", "dateFormat",
|
||||
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName,
|
||||
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.fixedEmiAmountParameterName,
|
||||
LoanApiConstants.disbursementNetDisbursalAmountParameterName));
|
||||
} else {
|
||||
disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale", "dateFormat",
|
||||
"paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber", "adjustRepaymentDate",
|
||||
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName,
|
||||
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.fixedEmiAmountParameterName,
|
||||
LoanApiConstants.postDatedChecks, LoanApiConstants.disbursementNetDisbursalAmountParameterName));
|
||||
}
|
||||
|
||||
@@ -111,9 +111,9 @@ public final class LoanEventApiJsonValidator {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementNetDisbursalAmountParameterName).value(netDisbursalAmount)
|
||||
.ignoreIfNull().positiveAmount();
|
||||
|
||||
final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
||||
final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedEmiAmountParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.emiAmountParameterName).value(emiAmount).ignoreIfNull().positiveAmount()
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.fixedEmiAmountParameterName).value(emiAmount).ignoreIfNull().positiveAmount()
|
||||
.notGreaterThanMax(principal);
|
||||
|
||||
validatePaymentDetails(baseDataValidator, element);
|
||||
|
||||
+3
-3
@@ -40,7 +40,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
public final class LoanScheduleValidator {
|
||||
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
@@ -64,7 +64,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
LoanApiConstants.submittedOnNoteParameterName, LoanApiConstants.localeParameterName, LoanApiConstants.dateFormatParameterName,
|
||||
LoanApiConstants.chargesParameterName, LoanApiConstants.collateralParameterName,
|
||||
LoanApiConstants.syncDisbursementWithMeetingParameterName, LoanApiConstants.linkAccountIdParameterName,
|
||||
LoanApiConstants.disbursementDataParameterName, LoanApiConstants.emiAmountParameterName,
|
||||
LoanApiConstants.disbursementDataParameterName, LoanApiConstants.fixedEmiAmountParameterName,
|
||||
LoanApiConstants.maxOutstandingBalanceParameterName, LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME,
|
||||
LoanApiConstants.createStandingInstructionAtDisbursementParameterName, LoanApiConstants.isFloatingInterestRateParameterName,
|
||||
LoanApiConstants.interestRateDifferentialParameterName, LoanApiConstants.repaymentFrequencyNthDayTypeParameterName,
|
||||
@@ -76,7 +76,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public CalculateLoanScheduleQueryFromApiJsonHelper(final FromJsonHelper fromApiJsonHelper) {
|
||||
public LoanScheduleValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
+2
-2
@@ -31,6 +31,6 @@ public interface GLIMAccountInfoWritePlatformService {
|
||||
|
||||
void incrementChildAccountCount(GroupLoanIndividualMonitoringAccount glimAccount);
|
||||
|
||||
void addGLIMAccountInfo(String accountNumber, Group group, BigDecimal principalAmount, Long childAccountsCount,
|
||||
Boolean isAcceptingChild, Integer loanStatus, BigDecimal applicationId);
|
||||
GroupLoanIndividualMonitoringAccount createGLIMAccount(String accountNumber, Group group, BigDecimal principalAmount,
|
||||
Long childAccountsCount, Boolean isAcceptingChild, Integer loanStatus, BigDecimal applicationId);
|
||||
}
|
||||
|
||||
+3
-10
@@ -21,30 +21,23 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GLIMAccountInfoWritePlatformServiceImpl implements GLIMAccountInfoWritePlatformService {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
|
||||
private final GLIMAccountInfoRepository glimAccountRepository;
|
||||
|
||||
private final LoanRepository loanRepository;
|
||||
|
||||
@Override
|
||||
public void addGLIMAccountInfo(String accountNumber, Group group, BigDecimal principalAmount, Long childAccountsCount,
|
||||
Boolean isAcceptingChild, Integer loanStatus, BigDecimal applicationId) {
|
||||
public GroupLoanIndividualMonitoringAccount createGLIMAccount(String accountNumber, Group group, BigDecimal principalAmount,
|
||||
Long childAccountsCount, Boolean isAcceptingChild, Integer loanStatus, BigDecimal applicationId) {
|
||||
|
||||
GroupLoanIndividualMonitoringAccount glimAccountInfo = GroupLoanIndividualMonitoringAccount.getInstance(accountNumber, group,
|
||||
principalAmount, childAccountsCount, isAcceptingChild, loanStatus, applicationId);
|
||||
|
||||
this.glimAccountRepository.save(glimAccountInfo);
|
||||
|
||||
return this.glimAccountRepository.save(glimAccountInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+272
-1032
File diff suppressed because it is too large
Load Diff
+600
-172
@@ -18,29 +18,35 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormat;
|
||||
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper;
|
||||
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigurationServiceContainer;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonQuery;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayStatusType;
|
||||
import org.apache.fineract.organisation.monetary.exception.InvalidCurrencyException;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.staff.domain.Staff;
|
||||
import org.apache.fineract.organisation.staff.domain.StaffRepository;
|
||||
import org.apache.fineract.organisation.staff.exception.StaffNotFoundException;
|
||||
@@ -48,21 +54,20 @@ import org.apache.fineract.organisation.staff.exception.StaffRoleException;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
||||
import org.apache.fineract.portfolio.accountdetails.service.AccountEnumerations;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.client.domain.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
||||
import org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementDomain;
|
||||
import org.apache.fineract.portfolio.collateralmanagement.service.LoanCollateralAssembler;
|
||||
import org.apache.fineract.portfolio.fund.domain.Fund;
|
||||
import org.apache.fineract.portfolio.fund.domain.FundRepository;
|
||||
import org.apache.fineract.portfolio.fund.exception.FundNotFoundException;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.group.domain.GroupRepository;
|
||||
import org.apache.fineract.portfolio.group.exception.ClientNotInGroupException;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotFoundException;
|
||||
import org.apache.fineract.portfolio.group.domain.GroupRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
||||
@@ -72,21 +77,24 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachin
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTopupDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataNotAllowedException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.apache.fineract.portfolio.rate.service.RateAssembler;
|
||||
@@ -98,7 +106,7 @@ public class LoanAssembler {
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
private final ClientRepositoryWrapper clientRepository;
|
||||
private final GroupRepository groupRepository;
|
||||
private final GroupRepositoryWrapper groupRepository;
|
||||
private final FundRepository fundRepository;
|
||||
private final StaffRepository staffRepository;
|
||||
private final CodeValueRepositoryWrapper codeValueRepository;
|
||||
@@ -110,10 +118,18 @@ public class LoanAssembler {
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final RateAssembler rateAssembler;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
|
||||
private final GLIMAccountInfoRepository glimRepository;
|
||||
private final AccountNumberGenerator accountNumberGenerator;
|
||||
private final GLIMAccountInfoWritePlatformService glimAccountInfoWritePlatformService;
|
||||
private final LoanCollateralAssembler loanCollateralAssembler;
|
||||
private final LoanScheduleCalculationPlatformService calculationPlatformService;
|
||||
private final LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler;
|
||||
private final LoanChargeMapper loanChargeMapper;
|
||||
private final LoanCollateralManagementMapper loanCollateralManagementMapper;
|
||||
|
||||
public Loan assembleFrom(final Long accountId) {
|
||||
final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true);
|
||||
@@ -133,20 +149,10 @@ public class LoanAssembler {
|
||||
|
||||
final Long clientId = this.fromApiJsonHelper.extractLongNamed("clientId", element);
|
||||
final Long groupId = this.fromApiJsonHelper.extractLongNamed("groupId", element);
|
||||
|
||||
return assembleApplication(element, clientId, groupId);
|
||||
}
|
||||
|
||||
private Loan assembleApplication(final JsonElement element, final Long clientId, final Long groupId) {
|
||||
|
||||
final String accountNo = this.fromApiJsonHelper.extractStringNamed("accountNo", element);
|
||||
final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element);
|
||||
final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element);
|
||||
final Long loanOfficerId = this.fromApiJsonHelper.extractLongNamed("loanOfficerId", element);
|
||||
final String transactionProcessingStrategyCode = this.fromApiJsonHelper.extractStringNamed("transactionProcessingStrategyCode",
|
||||
element);
|
||||
final String transactionProcessingStrategyName = this.loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(transactionProcessingStrategyCode).getName();
|
||||
final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed("loanPurposeId", element);
|
||||
final Boolean syncDisbursementWithMeeting = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
|
||||
final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper
|
||||
@@ -154,8 +160,13 @@ public class LoanAssembler {
|
||||
|
||||
final LoanProduct loanProduct = this.loanProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new LoanProductNotFoundException(productId));
|
||||
final BigDecimal amount = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementPrincipalParameterName, element);
|
||||
final Boolean allowOverridingTransactionProcessingStrategy = loanProduct.getLoanConfigurableAttributes()
|
||||
.getTransactionProcessingStrategyBoolean();
|
||||
final String transactionProcessingStrategyCode = allowOverridingTransactionProcessingStrategy
|
||||
? this.fromApiJsonHelper.extractStringNamed("transactionProcessingStrategyCode", element)
|
||||
: loanProduct.getTransactionProcessingStrategyCode();
|
||||
final LoanRepaymentScheduleTransactionProcessor transactionProcessingStrategy = this.loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(transactionProcessingStrategyCode);
|
||||
final Fund fund = findFundByIdIfProvided(fundId);
|
||||
final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId);
|
||||
CodeValue loanPurpose = null;
|
||||
@@ -165,76 +176,26 @@ public class LoanAssembler {
|
||||
List<LoanDisbursementDetails> disbursementDetails = new ArrayList<>();
|
||||
BigDecimal fixedEmiAmount = null;
|
||||
if (loanProduct.isMultiDisburseLoan() || loanProduct.isCanDefineInstallmentAmount()) {
|
||||
fixedEmiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName, element);
|
||||
fixedEmiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedEmiAmountParameterName, element);
|
||||
}
|
||||
BigDecimal maxOutstandingLoanBalance = null;
|
||||
if (loanProduct.isMultiDisburseLoan()) {
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
|
||||
maxOutstandingLoanBalance = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
element, locale);
|
||||
|
||||
disbursementDetails = this.loanUtilService.fetchDisbursementData(element.getAsJsonObject());
|
||||
if (loanProduct.isDisallowExpectedDisbursements()) {
|
||||
if (!disbursementDetails.isEmpty()) {
|
||||
final String errorMessage = "For this loan product, disbursement details are not allowed";
|
||||
throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage);
|
||||
}
|
||||
} else {
|
||||
if (disbursementDetails.isEmpty()) {
|
||||
final String errorMessage = "For this loan product, disbursement details must be provided";
|
||||
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (disbursementDetails.size() > loanProduct.maxTrancheCount()) {
|
||||
final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
|
||||
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
|
||||
loanProduct.maxTrancheCount(), disbursementDetails.size());
|
||||
}
|
||||
disbursementDetails = this.loanDisbursementDetailsAssembler.fetchDisbursementData(element.getAsJsonObject());
|
||||
}
|
||||
|
||||
final String loanTypeParameterName = "loanType";
|
||||
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
|
||||
final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeStr);
|
||||
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.loanTypeParameterName, element);
|
||||
Set<LoanCollateralManagement> collateral = new HashSet<>();
|
||||
|
||||
if (!StringUtils.isBlank(loanTypeStr)) {
|
||||
final AccountType loanAccountType = AccountType.fromName(loanTypeStr);
|
||||
|
||||
if (loanAccountType.isIndividualAccount()) {
|
||||
collateral = this.collateralAssembler.fromParsedJson(element);
|
||||
|
||||
if (collateral.size() > 0) {
|
||||
BigDecimal totalValue = BigDecimal.ZERO;
|
||||
for (LoanCollateralManagement collateralManagement : collateral) {
|
||||
final CollateralManagementDomain collateralManagementDomain = collateralManagement.getClientCollateralManagement()
|
||||
.getCollaterals();
|
||||
BigDecimal totalCollateral = collateralManagement.getQuantity().multiply(collateralManagementDomain.getBasePrice())
|
||||
.multiply(collateralManagementDomain.getPctToBase()).divide(BigDecimal.valueOf(100));
|
||||
totalValue = totalValue.add(totalCollateral);
|
||||
}
|
||||
|
||||
if (amount.compareTo(totalValue) > 0) {
|
||||
throw new InvalidAmountOfCollaterals(totalValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
final AccountType loanAccountType = AccountType.fromName(loanTypeStr);
|
||||
if (loanAccountType.isIndividualAccount()) {
|
||||
collateral = this.collateralAssembler.fromParsedJson(element);
|
||||
}
|
||||
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element, disbursementDetails);
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (!loanProduct.hasCurrencyCodeOf(loanCharge.currencyCode())) {
|
||||
final String errorMessage = "Charge and Loan must have the same currency.";
|
||||
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
|
||||
}
|
||||
if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||
final Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed("linkAccountId", element);
|
||||
if (savingsAccountId == null) {
|
||||
final String errorMessage = "one of the charges requires linked savings account for payment";
|
||||
throw new LinkedAccountRequiredException("loanCharge", errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal fixedPrincipalPercentagePerInstallment = fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, element);
|
||||
|
||||
@@ -245,111 +206,166 @@ public class LoanAssembler {
|
||||
// Here we add Rates to LoanApplication
|
||||
final List<Rate> rates = this.rateAssembler.fromParsedJson(element);
|
||||
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element);
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element,
|
||||
loanProduct);
|
||||
|
||||
final BigDecimal interestRateDifferential = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.interestRateDifferentialParameterName, element);
|
||||
final Boolean isFloatingInterestRate = this.fromApiJsonHelper
|
||||
.extractBooleanNamed(LoanApiConstants.isFloatingInterestRateParameterName, element);
|
||||
|
||||
// PROGRESSIVE: Repayment strategy MUST be only "advanced payment allocation"
|
||||
final LoanScheduleType loanScheduleType = loanProduct.getLoanProductRelatedDetail().getLoanScheduleType();
|
||||
if (loanScheduleType.equals(LoanScheduleType.PROGRESSIVE)) {
|
||||
if (!transactionProcessingStrategyCode.equals(LoanProductConstants.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)) {
|
||||
throw new GeneralPlatformDomainRuleException(
|
||||
"error.msg.loan.repayment.strategy.can.not.be.different.than.advanced.payment.allocation",
|
||||
"Loan repayment strategy can not be different than Advanced Payment Allocation");
|
||||
}
|
||||
// CUMULATIVE: Repayment strategy CANNOT be "advanced payment allocation"
|
||||
} else if (loanScheduleType.equals(LoanScheduleType.CUMULATIVE)) {
|
||||
if (transactionProcessingStrategyCode.equals(LoanProductConstants.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)) {
|
||||
throw new GeneralPlatformDomainRuleException(
|
||||
"error.msg.loan.repayment.strategy.can.not.be.equal.to.advanced.payment.allocation",
|
||||
"Loan repayment strategy can not be equal to Advanced Payment Allocation");
|
||||
}
|
||||
}
|
||||
|
||||
if (clientId != null) {
|
||||
client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
if (client.isNotActive()) {
|
||||
throw new ClientNotActiveException(clientId);
|
||||
}
|
||||
}
|
||||
|
||||
if (groupId != null) {
|
||||
group = this.groupRepository.findById(groupId).orElseThrow(() -> new GroupNotFoundException(groupId));
|
||||
if (group.isNotActive()) {
|
||||
throw new GroupNotActiveException(groupId);
|
||||
}
|
||||
}
|
||||
|
||||
if (client != null && group != null) {
|
||||
|
||||
if (!group.hasClientAsMember(client)) {
|
||||
throw new ClientNotInGroupException(clientId, groupId);
|
||||
}
|
||||
|
||||
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(), loanProduct,
|
||||
fund, loanOfficer, loanPurpose, transactionProcessingStrategyCode, loanProductRelatedDetail, loanCharges, null,
|
||||
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
} else if (group != null) {
|
||||
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
|
||||
loanPurpose, transactionProcessingStrategyCode, loanProductRelatedDetail, loanCharges, null,
|
||||
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
} else if (client != null) {
|
||||
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund,
|
||||
loanOfficer, loanPurpose, transactionProcessingStrategyCode, loanProductRelatedDetail, loanCharges, collateral,
|
||||
fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement,
|
||||
isFloatingInterestRate, interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
|
||||
} else {
|
||||
loanApplication = null;
|
||||
group = this.groupRepository.findOneWithNotFoundDetection(groupId);
|
||||
}
|
||||
|
||||
final String externalIdStr = this.fromApiJsonHelper.extractStringNamed("externalId", element);
|
||||
ExternalId externalId = externalIdFactory.create(externalIdStr);
|
||||
final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element);
|
||||
|
||||
if (loanApplication == null) {
|
||||
throw new IllegalStateException("No loan application exists for either a client or group (or both).");
|
||||
}
|
||||
loanApplication.updateTransactionProcessingStrategy(transactionProcessingStrategyCode, transactionProcessingStrategyName);
|
||||
copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication);
|
||||
loanApplication.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
|
||||
if (loanProduct.isMultiDisburseLoan()) {
|
||||
for (final LoanDisbursementDetails loanDisbursementDetails : loanApplication.getDisbursementDetails()) {
|
||||
loanDisbursementDetails.updateLoan(loanApplication);
|
||||
}
|
||||
}
|
||||
|
||||
final Boolean isEnableInstallmentLevelDelinquency = this.fromApiJsonHelper
|
||||
Boolean isEnableInstallmentLevelDelinquency = this.fromApiJsonHelper
|
||||
.extractBooleanNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, element);
|
||||
if (isEnableInstallmentLevelDelinquency != null) {
|
||||
loanApplication.updateEnableInstallmentLevelDelinquency(isEnableInstallmentLevelDelinquency);
|
||||
} else {
|
||||
loanApplication.updateEnableInstallmentLevelDelinquency(loanProduct.isEnableInstallmentLevelDelinquency());
|
||||
if (isEnableInstallmentLevelDelinquency == null) {
|
||||
isEnableInstallmentLevelDelinquency = loanProduct.isEnableInstallmentLevelDelinquency();
|
||||
}
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element);
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(),
|
||||
Long officeId = client != null ? client.getOffice().getId() : group.getOffice().getId();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId,
|
||||
loanApplicationTerms.getExpectedDisbursementDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||
final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays, element, disbursementDetails);
|
||||
loanApplication.loanApplicationSubmittal(loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine, submittedOnDate,
|
||||
externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay);
|
||||
|
||||
if (client != null && group != null) {
|
||||
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanAccountType, loanProduct, fund,
|
||||
loanOfficer, loanPurpose, transactionProcessingStrategy, loanProductRelatedDetail, loanCharges,
|
||||
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
|
||||
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel,
|
||||
isEnableInstallmentLevelDelinquency, submittedOnDate);
|
||||
} else if (group != null) {
|
||||
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanAccountType, loanProduct, fund, loanOfficer, loanPurpose,
|
||||
transactionProcessingStrategy, loanProductRelatedDetail, loanCharges, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms,
|
||||
loanScheduleModel, isEnableInstallmentLevelDelinquency, submittedOnDate);
|
||||
} else if (client != null) {
|
||||
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanAccountType, loanProduct, fund, loanOfficer,
|
||||
loanPurpose, transactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms,
|
||||
loanScheduleModel, isEnableInstallmentLevelDelinquency, submittedOnDate);
|
||||
} else {
|
||||
throw new IllegalStateException("No loan application exists for either a client or group (or both).");
|
||||
}
|
||||
|
||||
copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication);
|
||||
loanApplication.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
// TODO: review
|
||||
loanApplication.recalculateAllCharges();
|
||||
topUpLoanConfiguration(element, loanApplication);
|
||||
|
||||
return loanApplication;
|
||||
}
|
||||
|
||||
// TODO: Review... it might be better somewhere else and rethink due to the account number generation logic is
|
||||
// intertwined with GLIM logic
|
||||
public void accountNumberGeneration(JsonCommand command, Loan loan) {
|
||||
if (loan.isAccountNumberRequiresAutoGeneration()) {
|
||||
JsonElement element = command.parsedJson();
|
||||
final AccountNumberFormat accountNumberFormat = this.accountNumberFormatRepository.findByAccountType(EntityAccountType.LOAN);
|
||||
// TODO: It is really weird to set GLIM info only if account number was not provided
|
||||
// if application is of GLIM type
|
||||
if (loan.getLoanType().isGLIMAccount()) {
|
||||
Group group = loan.getGroup();
|
||||
String accountNumber = "";
|
||||
BigDecimal applicationId = BigDecimal.ZERO;
|
||||
Boolean isLastChildApplication = false;
|
||||
// GLIM specific parameters
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
|
||||
BigDecimal applicationIdFromParam = this.fromApiJsonHelper.extractBigDecimalNamed("applicationId", element, locale);
|
||||
BigDecimal totalLoan = this.fromApiJsonHelper.extractBigDecimalNamed("totalLoan", element, locale);
|
||||
if (applicationIdFromParam != null) {
|
||||
applicationId = applicationIdFromParam;
|
||||
}
|
||||
|
||||
Boolean isLastChildApplicationFromParam = this.fromApiJsonHelper.extractBooleanNamed("lastApplication", element);
|
||||
if (isLastChildApplicationFromParam != null) {
|
||||
isLastChildApplication = isLastChildApplicationFromParam;
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractBooleanNamed("isParentAccount", element) != null) {
|
||||
// empty table check
|
||||
// TODO: This count here is weird... and seems parent-empty and parent not empty looks the same
|
||||
if (glimRepository.count() != 0) {
|
||||
// **************Parent-Not an empty
|
||||
// table********************
|
||||
createAndSetGLIMAccount(totalLoan, loan, accountNumberFormat, group, applicationId);
|
||||
} else {
|
||||
// ************** Parent-empty
|
||||
// table********************
|
||||
createAndSetGLIMAccount(totalLoan, loan, accountNumberFormat, group, applicationId);
|
||||
}
|
||||
} else {
|
||||
// TODO: This count here is weird...
|
||||
if (glimRepository.count() != 0) {
|
||||
// Child-Not an empty table
|
||||
GroupLoanIndividualMonitoringAccount glimAccount = glimRepository.findOneByIsAcceptingChildAndApplicationId(true,
|
||||
applicationId);
|
||||
accountNumber = glimAccount.getAccountNumber() + (glimAccount.getChildAccountsCount() + 1);
|
||||
loan.updateAccountNo(accountNumber);
|
||||
this.glimAccountInfoWritePlatformService.incrementChildAccountCount(glimAccount);
|
||||
loan.setGlim(glimAccount);
|
||||
} else {
|
||||
// **************Child-empty
|
||||
// table********************
|
||||
// if the glim info is empty set the current account
|
||||
// as parent
|
||||
createAndSetGLIMAccount(totalLoan, loan, accountNumberFormat, group, applicationId);
|
||||
}
|
||||
// reset in cases of last child application of glim
|
||||
if (isLastChildApplication) {
|
||||
this.glimAccountInfoWritePlatformService
|
||||
.resetIsAcceptingChild(glimRepository.findOneByIsAcceptingChildAndApplicationId(true, applicationId));
|
||||
}
|
||||
}
|
||||
} else { // for applications other than GLIM
|
||||
loan.updateAccountNo(this.accountNumberGenerator.generate(loan, accountNumberFormat));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createAndSetGLIMAccount(BigDecimal totalLoan, Loan loan, AccountNumberFormat accountNumberFormat, Group group,
|
||||
BigDecimal applicationId) {
|
||||
String accountNumber;
|
||||
accountNumber = this.accountNumberGenerator.generate(loan, accountNumberFormat);
|
||||
loan.updateAccountNo(accountNumber + "1");
|
||||
GroupLoanIndividualMonitoringAccount glimAccount = glimAccountInfoWritePlatformService.createGLIMAccount(accountNumber, group,
|
||||
totalLoan, 1L, true, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), applicationId);
|
||||
loan.setGlim(glimAccount);
|
||||
}
|
||||
|
||||
private void topUpLoanConfiguration(JsonElement element, Loan loan) {
|
||||
if (loan.getLoanProduct().isCanUseForTopup() && loan.getClientId() != null) {
|
||||
final Boolean isTopUp = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isTopup, element);
|
||||
if (null == isTopUp) {
|
||||
loan.setIsTopup(false);
|
||||
} else {
|
||||
loan.setIsTopup(isTopUp);
|
||||
}
|
||||
if (loan.isTopup()) {
|
||||
final Long loanIdToClose = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.loanIdToClose, element);
|
||||
loan.setTopupLoanDetails(new LoanTopupDetails(loan, loanIdToClose));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CodeValue findCodeValueByIdIfProvided(final Long codeValueId) {
|
||||
CodeValue codeValue = null;
|
||||
if (codeValueId != null) {
|
||||
@@ -377,18 +393,6 @@ public class LoanAssembler {
|
||||
return staff;
|
||||
}
|
||||
|
||||
public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final Loan loanApplication) {
|
||||
|
||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(),
|
||||
loanApplication.getExpectedDisbursedOnLocalDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||
|
||||
loanApplication.validateExpectedDisbursementForHolidayAndNonWorkingDay(workingDays, allowTransactionsOnHoliday, holidays,
|
||||
allowTransactionsOnNonWorkingDay);
|
||||
}
|
||||
|
||||
private void copyAdvancedPaymentRulesIfApplicable(String transactionProcessingStrategyCode, LoanProduct loanProduct,
|
||||
Loan loanApplication) {
|
||||
if (transactionProcessingStrategyCode.equals(AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)) {
|
||||
@@ -398,11 +402,435 @@ public class LoanAssembler {
|
||||
.toList();
|
||||
loanApplication.setPaymentAllocationRules(loanPaymentAllocationRules);
|
||||
|
||||
if (loanProduct.getCreditAllocationRules() != null && loanProduct.getCreditAllocationRules().size() > 0) {
|
||||
if (loanProduct.getCreditAllocationRules() != null && !loanProduct.getCreditAllocationRules().isEmpty()) {
|
||||
List<LoanCreditAllocationRule> loanCreditAllocationRules = loanProduct.getCreditAllocationRules().stream()
|
||||
.map(r -> new LoanCreditAllocationRule(loanApplication, r.getTransactionType(), r.getAllocationTypes())).toList();
|
||||
loanApplication.setCreditAllocationRules(loanCreditAllocationRules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> updateFrom(JsonCommand command, Loan loan) {
|
||||
final Map<String, Object> changes = new HashMap<>();
|
||||
LoanProduct loanProduct;
|
||||
|
||||
final String productIdParamName = "productId";
|
||||
final Long productId = command.longValueOfParameterNamed(productIdParamName);
|
||||
if (productId == null || productId.equals(loan.getLoanProduct().getId())) {
|
||||
loanProduct = loan.getLoanProduct();
|
||||
} else {
|
||||
loanProduct = this.loanProductRepository.findById(productId).orElseThrow(() -> new LoanProductNotFoundException(productId));
|
||||
}
|
||||
|
||||
final Set<LoanCharge> existingCharges = loan.getActiveCharges();
|
||||
Map<Long, LoanChargeData> chargesMap = new HashMap<>();
|
||||
for (LoanCharge charge : existingCharges) {
|
||||
LoanChargeData chargeData = new LoanChargeData(charge.getId(), charge.getDueLocalDate(), charge.amountOrPercentage());
|
||||
chargesMap.put(charge.getId(), chargeData);
|
||||
}
|
||||
List<LoanDisbursementDetails> disbursementDetails = this.loanDisbursementDetailsAssembler
|
||||
.fetchDisbursementData(command.parsedJson().getAsJsonObject());
|
||||
|
||||
/**
|
||||
* Stores all charges which are passed in during modify loan application
|
||||
**/
|
||||
final Set<LoanCharge> possiblyModifiedLoanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson(),
|
||||
disbursementDetails);
|
||||
/** Boolean determines if any charge has been modified **/
|
||||
boolean isChargeModified = false;
|
||||
|
||||
Set<Charge> newTrancheCharges = this.loanChargeAssembler.getNewLoanTrancheCharges(command.parsedJson());
|
||||
for (Charge charge : newTrancheCharges) {
|
||||
loan.addTrancheLoanCharge(charge);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are any charges already present, which are now not passed in as a part of the request, deem the
|
||||
* charges as modified
|
||||
**/
|
||||
if (!possiblyModifiedLoanCharges.isEmpty()) {
|
||||
if (!possiblyModifiedLoanCharges.containsAll(existingCharges)) {
|
||||
isChargeModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If any new charges are added or values of existing charges are modified
|
||||
**/
|
||||
for (LoanCharge loanCharge : possiblyModifiedLoanCharges) {
|
||||
if (loanCharge.getId() == null) {
|
||||
isChargeModified = true;
|
||||
} else {
|
||||
LoanChargeData chargeData = chargesMap.get(loanCharge.getId());
|
||||
if (loanCharge.amountOrPercentage().compareTo(chargeData.getAmountOrPercentage()) != 0
|
||||
|| (loanCharge.isSpecifiedDueDate() && !loanCharge.getDueLocalDate().equals(chargeData.getDueDate()))) {
|
||||
isChargeModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<LoanCollateralManagement> possiblyModifedLoanCollateralItems = null;
|
||||
|
||||
if (command.parameterExists("loanType")) {
|
||||
final String loanTypeStr = command.stringValueOfParameterNamed("loanType");
|
||||
final AccountType loanType = AccountType.fromName(loanTypeStr);
|
||||
|
||||
if (!StringUtils.isBlank(loanTypeStr) && loanType.isIndividualAccount()) {
|
||||
possiblyModifedLoanCollateralItems = this.loanCollateralAssembler.fromParsedJson(command.parsedJson());
|
||||
}
|
||||
}
|
||||
this.loanScheduleAssembler.updateLoanApplicationAttributes(command, loan, changes);
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
final boolean recalculateLoanSchedule = !(changes.size() == 1
|
||||
&& changes.containsKey(LoanApiConstants.inArrearsToleranceParameterName));
|
||||
changes.put(Loan.RECALCULATE_LOAN_SCHEDULE, recalculateLoanSchedule);
|
||||
isChargeModified = true;
|
||||
}
|
||||
|
||||
final String dateFormatAsInput = command.dateFormat();
|
||||
final String localeAsInput = command.locale();
|
||||
|
||||
if (command.isChangeInStringParameterNamed(LoanApiConstants.accountNoParameterName, loan.getAccountNumber())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(LoanApiConstants.accountNoParameterName);
|
||||
changes.put(LoanApiConstants.accountNoParameterName, newValue);
|
||||
loan.setAccountNumber(StringUtils.defaultIfEmpty(newValue, null));
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
||||
loan.shouldCreateStandingInstructionAtDisbursement())) {
|
||||
final Boolean valueAsInput = command
|
||||
.booleanObjectValueOfParameterNamed(LoanApiConstants.createStandingInstructionAtDisbursementParameterName);
|
||||
changes.put(LoanApiConstants.createStandingInstructionAtDisbursementParameterName, valueAsInput);
|
||||
loan.setCreateStandingInstructionAtDisbursement(valueAsInput);
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(LoanApiConstants.externalIdParameterName, loan.getExternalId().getValue())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(LoanApiConstants.externalIdParameterName);
|
||||
ExternalId externalId = ExternalIdFactory.produce(newValue);
|
||||
if (externalId.isEmpty() && TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
changes.put(LoanApiConstants.externalIdParameterName, externalId);
|
||||
loan.setExternalId(externalId);
|
||||
}
|
||||
|
||||
// add clientId, groupId and loanType changes to actual changes
|
||||
|
||||
final Long clientId = loan.getClient() == null ? null : loan.getClient().getId();
|
||||
if (command.isChangeInLongParameterNamed(LoanApiConstants.clientIdParameterName, clientId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(LoanApiConstants.clientIdParameterName);
|
||||
changes.put(LoanApiConstants.clientIdParameterName, newValue);
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(newValue);
|
||||
loan.updateClient(client);
|
||||
}
|
||||
|
||||
// FIXME: AA - We may require separate api command to move loan from one
|
||||
// group to another
|
||||
final Long groupId = loan.getGroup() == null ? null : loan.getGroup().getId();
|
||||
if (command.isChangeInLongParameterNamed(LoanApiConstants.groupIdParameterName, groupId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(LoanApiConstants.groupIdParameterName);
|
||||
changes.put(LoanApiConstants.groupIdParameterName, newValue);
|
||||
final Group group = this.groupRepository.findOneWithNotFoundDetection(newValue);
|
||||
loan.updateGroup(group);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(LoanApiConstants.productIdParameterName, loan.getLoanProduct().getId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(LoanApiConstants.productIdParameterName);
|
||||
changes.put(LoanApiConstants.productIdParameterName, newValue);
|
||||
loan.updateLoanProduct(loanProduct);
|
||||
final MonetaryCurrency currency = new MonetaryCurrency(loanProduct.getCurrency().getCode(),
|
||||
loanProduct.getCurrency().getDigitsAfterDecimal(), loanProduct.getCurrency().getCurrencyInMultiplesOf());
|
||||
loan.getLoanRepaymentScheduleDetail().setCurrency(currency);
|
||||
|
||||
if (!changes.containsKey(LoanApiConstants.interestRateFrequencyTypeParameterName)) {
|
||||
loan.updateInterestRateFrequencyType();
|
||||
}
|
||||
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
loan.getLoanProductRelatedDetail().updateForFloatingInterestRates();
|
||||
} else {
|
||||
loan.setInterestRateDifferential(null);
|
||||
loan.setIsFloatingInterestRate(null);
|
||||
}
|
||||
loan.updateIsInterestRecalculationEnabled();
|
||||
changes.put(Loan.RECALCULATE_LOAN_SCHEDULE, true);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanApiConstants.isFloatingInterestRateParameterName,
|
||||
loan.getIsFloatingInterestRate())) {
|
||||
final Boolean newValue = command.booleanObjectValueOfParameterNamed(LoanApiConstants.isFloatingInterestRateParameterName);
|
||||
changes.put(LoanApiConstants.isFloatingInterestRateParameterName, newValue);
|
||||
loan.setIsFloatingInterestRate(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.interestRateDifferentialParameterName,
|
||||
loan.getInterestRateDifferential())) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(LoanApiConstants.interestRateDifferentialParameterName);
|
||||
changes.put(LoanApiConstants.interestRateDifferentialParameterName, newValue);
|
||||
loan.setInterestRateDifferential(newValue);
|
||||
}
|
||||
|
||||
Long existingFundId = null;
|
||||
if (loan.getFund() != null) {
|
||||
existingFundId = loan.getFund().getId();
|
||||
}
|
||||
if (command.isChangeInLongParameterNamed(LoanApiConstants.fundIdParameterName, existingFundId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(LoanApiConstants.fundIdParameterName);
|
||||
changes.put(LoanApiConstants.fundIdParameterName, newValue);
|
||||
final Fund fund = findFundByIdIfProvided(newValue);
|
||||
loan.updateFund(fund);
|
||||
}
|
||||
|
||||
Long existingLoanOfficerId = null;
|
||||
if (loan.getLoanOfficer() != null) {
|
||||
existingLoanOfficerId = loan.getLoanOfficer().getId();
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(LoanApiConstants.loanOfficerIdParameterName, existingLoanOfficerId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(LoanApiConstants.loanOfficerIdParameterName);
|
||||
changes.put(LoanApiConstants.loanOfficerIdParameterName, newValue);
|
||||
final Staff newOfficer = findLoanOfficerByIdIfProvided(newValue);
|
||||
loan.updateLoanOfficerOnLoanApplication(newOfficer);
|
||||
}
|
||||
|
||||
Long existingLoanPurposeId = null;
|
||||
if (loan.getLoanPurpose() != null) {
|
||||
existingLoanPurposeId = loan.getLoanPurpose().getId();
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(LoanApiConstants.loanPurposeIdParameterName, existingLoanPurposeId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(LoanApiConstants.loanPurposeIdParameterName);
|
||||
changes.put(LoanApiConstants.loanPurposeIdParameterName, newValue);
|
||||
final CodeValue loanPurpose = findCodeValueByIdIfProvided(newValue);
|
||||
loan.updateLoanPurpose(loanPurpose);
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName,
|
||||
loan.getTransactionProcessingStrategyCode())
|
||||
&& loanProduct.getLoanConfigurableAttributes().getTransactionProcessingStrategyBoolean()) {
|
||||
final String newValue = command.stringValueOfParameterNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName);
|
||||
|
||||
final String transactionProcessingStrategyCode = command.stringValueOfParameterNamed("transactionProcessingStrategyCode");
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(transactionProcessingStrategyCode);
|
||||
changes.put(LoanApiConstants.transactionProcessingStrategyCodeParameterName, newValue);
|
||||
loan.updateTransactionProcessingStrategy(transactionProcessingStrategyCode,
|
||||
loanRepaymentScheduleTransactionProcessor.getName());
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(LoanApiConstants.submittedOnDateParameterName, loan.getSubmittedOnDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(LoanApiConstants.submittedOnDateParameterName);
|
||||
changes.put(LoanApiConstants.submittedOnDateParameterName, valueAsInput);
|
||||
changes.put(LoanApiConstants.dateFormatParameterName, dateFormatAsInput);
|
||||
changes.put(LoanApiConstants.localeParameterName, localeAsInput);
|
||||
loan.setSubmittedOnDate(command.localDateValueOfParameterNamed(LoanApiConstants.submittedOnDateParameterName));
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(LoanApiConstants.expectedDisbursementDateParameterName,
|
||||
loan.getExpectedDisbursedOnLocalDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(LoanApiConstants.expectedDisbursementDateParameterName);
|
||||
changes.put(LoanApiConstants.expectedDisbursementDateParameterName, valueAsInput);
|
||||
changes.put(LoanApiConstants.dateFormatParameterName, dateFormatAsInput);
|
||||
changes.put(LoanApiConstants.localeParameterName, localeAsInput);
|
||||
changes.put(Loan.RECALCULATE_LOAN_SCHEDULE, true);
|
||||
loan.setExpectedDisbursementDate(
|
||||
command.localDateValueOfParameterNamed(LoanApiConstants.expectedDisbursementDateParameterName));
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(LoanApiConstants.repaymentsStartingFromDateParameterName,
|
||||
loan.getExpectedFirstRepaymentOnDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(LoanApiConstants.repaymentsStartingFromDateParameterName);
|
||||
changes.put(LoanApiConstants.repaymentsStartingFromDateParameterName, valueAsInput);
|
||||
changes.put(LoanApiConstants.dateFormatParameterName, dateFormatAsInput);
|
||||
changes.put(LoanApiConstants.localeParameterName, localeAsInput);
|
||||
changes.put(Loan.RECALCULATE_LOAN_SCHEDULE, true);
|
||||
loan.setExpectedFirstRepaymentOnDate(
|
||||
command.localDateValueOfParameterNamed(LoanApiConstants.repaymentsStartingFromDateParameterName));
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanApiConstants.syncDisbursementWithMeetingParameterName,
|
||||
loan.isSyncDisbursementWithMeeting())) {
|
||||
final Boolean valueAsInput = command
|
||||
.booleanObjectValueOfParameterNamed(LoanApiConstants.syncDisbursementWithMeetingParameterName);
|
||||
changes.put(LoanApiConstants.syncDisbursementWithMeetingParameterName, valueAsInput);
|
||||
loan.setSyncDisbursementWithMeeting(valueAsInput);
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(LoanApiConstants.interestChargedFromDateParameterName,
|
||||
loan.getInterestChargedFromDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(LoanApiConstants.interestChargedFromDateParameterName);
|
||||
changes.put(LoanApiConstants.interestChargedFromDateParameterName, valueAsInput);
|
||||
changes.put(LoanApiConstants.dateFormatParameterName, dateFormatAsInput);
|
||||
changes.put(LoanApiConstants.localeParameterName, localeAsInput);
|
||||
changes.put(Loan.RECALCULATE_LOAN_SCHEDULE, true);
|
||||
loan.setInterestChargedFromDate(command.localDateValueOfParameterNamed(LoanApiConstants.interestChargedFromDateParameterName));
|
||||
}
|
||||
|
||||
if (isChargeModified) {
|
||||
changes.put(LoanApiConstants.chargesParameterName, loanChargeMapper.map(possiblyModifiedLoanCharges, loan.getCurrency()));
|
||||
changes.put(Loan.RECALCULATE_LOAN_SCHEDULE, true);
|
||||
}
|
||||
|
||||
if (command.parameterExists(LoanApiConstants.collateralParameterName) && possiblyModifedLoanCollateralItems != null
|
||||
&& possiblyModifedLoanCollateralItems.equals(loan.getLoanCollateralManagements())) {
|
||||
changes.put(LoanApiConstants.collateralParameterName, loanCollateralManagementMapper.map(possiblyModifedLoanCollateralItems));
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanApiConstants.loanTermFrequencyParameterName, loan.getTermFrequency())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanApiConstants.loanTermFrequencyParameterName);
|
||||
changes.put(LoanApiConstants.loanTermFrequencyParameterName, newValue);
|
||||
loan.setTermFrequency(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanApiConstants.loanTermFrequencyTypeParameterName,
|
||||
loan.getTermPeriodFrequencyType())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanApiConstants.loanTermFrequencyTypeParameterName);
|
||||
changes.put(LoanApiConstants.loanTermFrequencyTypeParameterName, newValue);
|
||||
loan.setTermPeriodFrequencyType(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.principalParameterName, loan.getApprovedPrincipal())) {
|
||||
loan.setApprovedPrincipal(command.bigDecimalValueOfParameterNamed(LoanApiConstants.principalParameterName));
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.principalParameterName, loan.getProposedPrincipal())) {
|
||||
BigDecimal newValue = command.bigDecimalValueOfParameterNamed(LoanApiConstants.principalParameterName);
|
||||
changes.put(LoanApiConstants.principalParameterName, newValue);
|
||||
loan.setProposedPrincipal(newValue);
|
||||
}
|
||||
|
||||
if (loanProduct.isMultiDisburseLoan()) {
|
||||
loan.updateDisbursementDetails(command, changes);
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
loan.getMaxOutstandingLoanBalance())) {
|
||||
loan.setMaxOutstandingLoanBalance(
|
||||
command.bigDecimalValueOfParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName));
|
||||
}
|
||||
final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
|
||||
|
||||
if (loanProduct.isDisallowExpectedDisbursements()) {
|
||||
if (disbursementDataArray != null && !disbursementDataArray.isEmpty()) {
|
||||
final String errorMessage = "For this loan product, disbursement details are not allowed";
|
||||
throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage);
|
||||
}
|
||||
} else {
|
||||
if (disbursementDataArray == null || disbursementDataArray.isEmpty()) {
|
||||
final String errorMessage = "For this loan product, disbursement details must be provided";
|
||||
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
|
||||
}
|
||||
|
||||
if (disbursementDataArray.size() > loanProduct.maxTrancheCount()) {
|
||||
final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
|
||||
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
|
||||
loanProduct.maxTrancheCount(), disbursementDetails.size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loan.clearDisbursementDetails();
|
||||
}
|
||||
|
||||
if (loanProduct.isMultiDisburseLoan() || loanProduct.isCanDefineInstallmentAmount()) {
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.fixedEmiAmountParameterName, loan.getFixedEmiAmount())) {
|
||||
loan.setFixedEmiAmount(command.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedEmiAmountParameterName));
|
||||
changes.put(LoanApiConstants.fixedEmiAmountParameterName, loan.getFixedEmiAmount());
|
||||
changes.put(Loan.RECALCULATE_LOAN_SCHEDULE, true);
|
||||
}
|
||||
} else {
|
||||
loan.setFixedEmiAmount(null);
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
|
||||
loan.getFixedPrincipalPercentagePerInstallment())) {
|
||||
loan.setFixedPrincipalPercentagePerInstallment(
|
||||
command.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName));
|
||||
changes.put(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, loan.getFixedPrincipalPercentagePerInstallment());
|
||||
}
|
||||
|
||||
final LoanProductRelatedDetail productRelatedDetail = loan.repaymentScheduleDetail();
|
||||
if (loan.loanProduct().getLoanConfigurableAttributes() != null) {
|
||||
loanScheduleAssembler.updateProductRelatedDetails(productRelatedDetail, loan);
|
||||
}
|
||||
|
||||
if (loan.getLoanProduct().isCanUseForTopup() && loan.getClientId() != null) {
|
||||
final Boolean isTopup = command.booleanObjectValueOfParameterNamed(LoanApiConstants.isTopup);
|
||||
if (command.isChangeInBooleanParameterNamed(LoanApiConstants.isTopup, loan.isTopup())) {
|
||||
loan.setIsTopup(isTopup);
|
||||
changes.put(LoanApiConstants.isTopup, isTopup);
|
||||
}
|
||||
|
||||
if (loan.isTopup()) {
|
||||
final Long loanIdToClose = command.longValueOfParameterNamed(LoanApiConstants.loanIdToClose);
|
||||
LoanTopupDetails existingLoanTopupDetails = loan.getTopupLoanDetails();
|
||||
if (existingLoanTopupDetails == null || !existingLoanTopupDetails.getLoanIdToClose().equals(loanIdToClose)
|
||||
|| changes.containsKey("submittedOnDate") || changes.containsKey("expectedDisbursementDate")
|
||||
|| changes.containsKey("principal") || changes.containsKey(LoanApiConstants.disbursementDataParameterName)) {
|
||||
Long existingLoanIdToClose = null;
|
||||
if (existingLoanTopupDetails != null) {
|
||||
existingLoanIdToClose = existingLoanTopupDetails.getLoanIdToClose();
|
||||
}
|
||||
|
||||
if (!loanIdToClose.equals(existingLoanIdToClose)) {
|
||||
final LoanTopupDetails topupDetails = new LoanTopupDetails(loan, loanIdToClose);
|
||||
loan.setTopupLoanDetails(topupDetails);
|
||||
changes.put(LoanApiConstants.loanIdToClose, loanIdToClose);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loan.setTopupLoanDetails(null);
|
||||
}
|
||||
} else {
|
||||
if (loan.isTopup()) {
|
||||
loan.setIsTopup(false);
|
||||
loan.setTopupLoanDetails(null);
|
||||
changes.put(LoanApiConstants.isTopup, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Allow other loan types if needed.
|
||||
*/
|
||||
if (command.parameterExists("loanType")) {
|
||||
final String loanTypeStr = command.stringValueOfParameterNamed("loanType");
|
||||
final AccountType loanType = AccountType.fromName(loanTypeStr);
|
||||
|
||||
if (!StringUtils.isBlank(loanTypeStr) && loanType.isIndividualAccount()) {
|
||||
final String collateralParamName = "collateral";
|
||||
if (changes.containsKey(collateralParamName)) {
|
||||
loan.updateLoanCollateral(possiblyModifedLoanCollateralItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String chargesParamName = "charges";
|
||||
if (changes.containsKey(chargesParamName)) {
|
||||
loan.updateLoanCharges(possiblyModifiedLoanCharges);
|
||||
}
|
||||
|
||||
// update installment level delinquency
|
||||
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY,
|
||||
loan.isEnableInstallmentLevelDelinquency())) {
|
||||
final Boolean enableInstallmentLevelDelinquency = command
|
||||
.booleanObjectValueOfParameterNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY);
|
||||
loan.updateEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency);
|
||||
}
|
||||
|
||||
if (changes.containsKey("recalculateLoanSchedule")) {
|
||||
changes.remove("recalculateLoanSchedule");
|
||||
|
||||
final JsonElement parsedQuery = this.fromApiJsonHelper.parse(command.json());
|
||||
final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromApiJsonHelper);
|
||||
|
||||
final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query, false);
|
||||
loan.updateLoanSchedule(loanSchedule);
|
||||
loan.recalculateAllCharges();
|
||||
}
|
||||
|
||||
// Changes to modify loan rates.
|
||||
if (command.hasParameter(LoanProductConstants.RATES_PARAM_NAME)) {
|
||||
loan.updateLoanRates(rateAssembler.fromParsedJson(command.parsedJson()));
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-21
@@ -40,7 +40,7 @@ import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeNotFoundException;
|
||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeWithoutMandatoryFieldException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
@@ -65,13 +65,13 @@ public class LoanChargeAssembler {
|
||||
JsonArray jsonDisbursement = this.fromApiJsonHelper.extractJsonArrayNamed("disbursementData", element);
|
||||
List<Long> disbursementChargeIds = new ArrayList<>();
|
||||
|
||||
if (jsonDisbursement != null && jsonDisbursement.size() > 0) {
|
||||
if (jsonDisbursement != null && !jsonDisbursement.isEmpty()) {
|
||||
for (int i = 0; i < jsonDisbursement.size(); i++) {
|
||||
final JsonObject jsonObject = jsonDisbursement.get(i).getAsJsonObject();
|
||||
if (jsonObject != null && jsonObject.getAsJsonPrimitive(LoanApiConstants.loanChargeIdParameterName) != null) {
|
||||
String chargeIds = jsonObject.getAsJsonPrimitive(LoanApiConstants.loanChargeIdParameterName).getAsString();
|
||||
if (chargeIds != null) {
|
||||
if (chargeIds.indexOf(",") != -1) {
|
||||
if (chargeIds.contains(",")) {
|
||||
Iterable<String> chargeId = Splitter.on(',').split(chargeIds);
|
||||
for (String loanChargeId : chargeId) {
|
||||
disbursementChargeIds.add(Long.parseLong(loanChargeId));
|
||||
@@ -104,7 +104,7 @@ public class LoanChargeAssembler {
|
||||
|
||||
final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
|
||||
|
||||
final Long id = this.fromApiJsonHelper.extractLongNamed("id", loanChargeElement);
|
||||
final Long loanChargeId = this.fromApiJsonHelper.extractLongNamed("id", loanChargeElement);
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
|
||||
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
|
||||
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale);
|
||||
@@ -116,16 +116,8 @@ public class LoanChargeAssembler {
|
||||
locale);
|
||||
final String externalIdStr = this.fromApiJsonHelper.extractStringNamed("externalId", loanChargeElement);
|
||||
final ExternalId externalId = externalIdFactory.create(externalIdStr);
|
||||
if (id == null) {
|
||||
if (loanChargeId == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
|
||||
if (chargeDefinition.isOverdueInstallment()) {
|
||||
|
||||
final String defaultUserMessage = "Installment charge cannot be added to the loan.";
|
||||
throw new LoanChargeCannotBeAddedException("loanCharge", "overdue.charge", defaultUserMessage, null,
|
||||
chargeDefinition.getName());
|
||||
}
|
||||
|
||||
ChargeTimeType chargeTime = null;
|
||||
if (chargeTimeType != null) {
|
||||
chargeTime = ChargeTimeType.fromInt(chargeTimeType);
|
||||
@@ -145,7 +137,7 @@ public class LoanChargeAssembler {
|
||||
} else {
|
||||
if (topLevelJsonElement.has("disbursementData") && topLevelJsonElement.get("disbursementData").isJsonArray()) {
|
||||
final JsonArray disbursementArray = topLevelJsonElement.get("disbursementData").getAsJsonArray();
|
||||
if (disbursementArray.size() > 0) {
|
||||
if (!disbursementArray.isEmpty()) {
|
||||
JsonObject disbursementDataElement = disbursementArray.get(0).getAsJsonObject();
|
||||
expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||
LoanApiConstants.expectedDisbursementDateParameterName, disbursementDataElement, dateFormat,
|
||||
@@ -201,14 +193,14 @@ public class LoanChargeAssembler {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final Long loanChargeId = id;
|
||||
final LoanCharge loanCharge = this.loanChargeRepository.findById(loanChargeId).orElse(null);
|
||||
if (loanCharge != null) {
|
||||
if (!loanCharge.isTrancheDisbursementCharge() || disbursementChargeIds.contains(loanChargeId)) {
|
||||
loanCharge.update(amount, dueDate, numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
}
|
||||
final LoanCharge loanCharge = this.loanChargeRepository.findById(loanChargeId)
|
||||
.orElseThrow(() -> new LoanChargeNotFoundException(loanChargeId));
|
||||
|
||||
if (!loanCharge.isTrancheDisbursementCharge() || disbursementChargeIds.contains(loanChargeId)) {
|
||||
loanCharge.update(amount, dueDate, numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -70,7 +70,6 @@ import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
||||
import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException;
|
||||
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
@@ -710,7 +709,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
paymentDetail);
|
||||
|
||||
// Update loan transaction on repayment.
|
||||
if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
|
||||
if (loan.getLoanType().isIndividualAccount()) {
|
||||
Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
|
||||
for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
|
||||
loanCollateralManagement.setLoanTransactionData(loanTransaction);
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoanDisbursementDetailsAssembler {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
public List<LoanDisbursementDetails> fetchDisbursementData(final JsonObject command) {
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(command);
|
||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(command);
|
||||
List<LoanDisbursementDetails> disbursementDatas = new ArrayList<>();
|
||||
JsonArray disbursementDataArray = command.getAsJsonArray(LoanApiConstants.disbursementDataParameterName);
|
||||
if (disbursementDataArray != null && !disbursementDataArray.isEmpty()) {
|
||||
disbursementDataArray.forEach(jsonElement -> {
|
||||
LocalDate expectedDisbursementDate = this.fromApiJsonHelper
|
||||
.extractLocalDateNamed(LoanApiConstants.expectedDisbursementDateParameterName, jsonElement, dateFormat, locale);
|
||||
BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.disbursementPrincipalParameterName,
|
||||
jsonElement, locale);
|
||||
BigDecimal netDisbursalAmount = this.fromApiJsonHelper
|
||||
.extractBigDecimalNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName, jsonElement, locale);
|
||||
boolean isReversed = Boolean.TRUE.equals(
|
||||
this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.disbursementReversedParameterName, jsonElement));
|
||||
disbursementDatas
|
||||
.add(new LoanDisbursementDetails(expectedDisbursementDate, null, principal, netDisbursalAmount, isReversed));
|
||||
});
|
||||
}
|
||||
return disbursementDatas;
|
||||
}
|
||||
}
|
||||
-54
@@ -18,19 +18,13 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayStatusType;
|
||||
@@ -52,11 +46,9 @@ import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException;
|
||||
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
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.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
@@ -71,7 +63,6 @@ public class LoanUtilService {
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CalendarReadPlatformService calendarReadPlatformService;
|
||||
|
||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom) {
|
||||
@@ -288,51 +279,6 @@ public class LoanUtilService {
|
||||
return calculatedRepaymentsStartingFromDate;
|
||||
}
|
||||
|
||||
public List<LoanDisbursementDetails> fetchDisbursementData(final JsonObject command) {
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(command);
|
||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(command);
|
||||
List<LoanDisbursementDetails> disbursementDatas = new ArrayList<>();
|
||||
if (command.has(LoanApiConstants.disbursementDataParameterName)) {
|
||||
final JsonArray disbursementDataArray = command.getAsJsonArray(LoanApiConstants.disbursementDataParameterName);
|
||||
if (disbursementDataArray != null && disbursementDataArray.size() > 0) {
|
||||
int i = 0;
|
||||
do {
|
||||
final JsonObject jsonObject = disbursementDataArray.get(i).getAsJsonObject();
|
||||
LocalDate expectedDisbursementDate = null;
|
||||
LocalDate actualDisbursementDate = null;
|
||||
BigDecimal principal = null;
|
||||
BigDecimal netDisbursalAmount = null;
|
||||
|
||||
if (jsonObject.has(LoanApiConstants.expectedDisbursementDateParameterName)) {
|
||||
expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||
LoanApiConstants.expectedDisbursementDateParameterName, jsonObject, dateFormat, locale);
|
||||
}
|
||||
if (jsonObject.has(LoanApiConstants.disbursementPrincipalParameterName)
|
||||
&& jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive()
|
||||
&& StringUtils.isNotBlank(jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString())) {
|
||||
principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal();
|
||||
}
|
||||
if (jsonObject.has(LoanApiConstants.disbursementNetDisbursalAmountParameterName)
|
||||
&& jsonObject.get(LoanApiConstants.disbursementNetDisbursalAmountParameterName).isJsonPrimitive()
|
||||
&& StringUtils.isNotBlank(
|
||||
jsonObject.get(LoanApiConstants.disbursementNetDisbursalAmountParameterName).getAsString())) {
|
||||
netDisbursalAmount = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementNetDisbursalAmountParameterName)
|
||||
.getAsBigDecimal();
|
||||
}
|
||||
boolean isReversed = false;
|
||||
if (jsonObject.has(LoanApiConstants.disbursementReversedParameterName)) {
|
||||
isReversed = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.disbursementReversedParameterName,
|
||||
jsonObject);
|
||||
}
|
||||
disbursementDatas.add(new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate, principal,
|
||||
netDisbursalAmount, isReversed));
|
||||
i++;
|
||||
} while (i < disbursementDataArray.size());
|
||||
}
|
||||
}
|
||||
return disbursementDatas;
|
||||
}
|
||||
|
||||
public void validateRepaymentTransactionType(LoanTransactionType repaymentTransactionType) {
|
||||
if (!repaymentTransactionType.isRepaymentType()) {
|
||||
throw new PlatformServiceUnavailableException("error.msg.repaymentTransactionType.provided.not.a.repayment.type",
|
||||
|
||||
+7
-10
@@ -147,7 +147,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
@@ -180,7 +179,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
@@ -225,7 +224,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CalendarRepository calendarRepository;
|
||||
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
|
||||
private final LoanApplicationCommandFromApiJsonHelper loanApplicationCommandFromApiJsonHelper;
|
||||
private final LoanApplicationValidator loanApplicationCommandFromApiJsonHelper;
|
||||
private final AccountAssociationsRepository accountAssociationRepository;
|
||||
private final AccountTransferDetailRepository accountTransferDetailRepository;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
@@ -240,7 +239,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final LoanRepository loanRepository;
|
||||
private final RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler;
|
||||
private final PostDatedChecksRepository postDatedChecksRepository;
|
||||
private final LoanDisbursementDetailsRepository loanDisbursementDetailsRepository;
|
||||
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanAccountLockService loanAccountLockService;
|
||||
@@ -327,8 +325,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
|
||||
|
||||
// Get relevant loan collateral modules
|
||||
if ((loanCollateralManagements != null && !loanCollateralManagements.isEmpty())
|
||||
&& AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
|
||||
if ((loanCollateralManagements != null && !loanCollateralManagements.isEmpty()) && loan.getLoanType().isIndividualAccount()) {
|
||||
|
||||
BigDecimal totalCollateral = BigDecimal.valueOf(0);
|
||||
|
||||
@@ -996,7 +993,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
holidayDetailDto, isHolidayValidationDone);
|
||||
loan = loanTransaction.getLoan();
|
||||
// Update loan transaction on repayment.
|
||||
if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
|
||||
if (loan.getLoanType().isIndividualAccount()) {
|
||||
Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
|
||||
for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
|
||||
loanCollateralManagement.setLoanTransactionData(loanTransaction);
|
||||
@@ -1597,7 +1594,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanCloseBusinessEvent(loan));
|
||||
|
||||
// Update loan transaction on repayment.
|
||||
if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
|
||||
if (loan.getLoanType().isIndividualAccount()) {
|
||||
Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
|
||||
for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
|
||||
ClientCollateralManagement clientCollateralManagement = loanCollateralManagement.getClientCollateralManagement();
|
||||
@@ -1678,7 +1675,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.loanAccountDomainService.disableStandingInstructionsLinkedToClosedLoan(loan);
|
||||
|
||||
// Update loan transaction on repayment.
|
||||
if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
|
||||
if (loan.getLoanType().isIndividualAccount()) {
|
||||
Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
|
||||
for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
|
||||
ClientCollateralManagement clientCollateralManagement = loanCollateralManagement.getClientCollateralManagement();
|
||||
@@ -2486,7 +2483,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate nextPossibleRepaymentDate,
|
||||
final LocalDate rescheduledRepaymentDate) {
|
||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
||||
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedEmiAmountParameterName);
|
||||
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount,
|
||||
nextPossibleRepaymentDate, rescheduledRepaymentDate);
|
||||
}
|
||||
|
||||
+48
-60
@@ -24,15 +24,12 @@ import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumb
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.GlobalConfigurationRepositoryWrapper;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityRelationRepository;
|
||||
import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityToEntityMappingRepository;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||
@@ -65,14 +62,12 @@ import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatform
|
||||
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.apache.fineract.portfolio.fund.domain.FundRepository;
|
||||
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
|
||||
import org.apache.fineract.portfolio.group.domain.GroupRepository;
|
||||
import org.apache.fineract.portfolio.group.domain.GroupRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainServiceJpa;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
@@ -82,14 +77,15 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
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.LoanChargeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionRelationMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationTransitionApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
||||
@@ -118,6 +114,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformS
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanDisbursementDetailsAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
@@ -131,7 +128,6 @@ import org.apache.fineract.portfolio.loanaccount.service.RecalculateInterestPost
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.serialization.LoanProductDataValidator;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||
@@ -140,7 +136,7 @@ import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatform
|
||||
import org.apache.fineract.portfolio.rate.service.RateAssembler;
|
||||
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecksRepository;
|
||||
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.service.RepaymentWithPostDatedChecksAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.savings.service.GSIMReadPlatformService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -168,11 +164,8 @@ public class LoanAccountConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GLIMAccountInfoWritePlatformService.class)
|
||||
public GLIMAccountInfoWritePlatformService glimAccountInfoWritePlatformService(PlatformSecurityContext context,
|
||||
GLIMAccountInfoRepository glimAccountRepository,
|
||||
|
||||
LoanRepository loanRepository) {
|
||||
return new GLIMAccountInfoWritePlatformServiceImpl(context, glimAccountRepository, loanRepository);
|
||||
public GLIMAccountInfoWritePlatformService glimAccountInfoWritePlatformService(GLIMAccountInfoRepository glimAccountRepository) {
|
||||
return new GLIMAccountInfoWritePlatformServiceImpl(glimAccountRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -210,38 +203,23 @@ public class LoanAccountConfiguration {
|
||||
@ConditionalOnMissingBean(LoanApplicationWritePlatformService.class)
|
||||
public LoanApplicationWritePlatformService loanApplicationWritePlatformService(PlatformSecurityContext context,
|
||||
FromJsonHelper fromJsonHelper, LoanApplicationTransitionApiJsonValidator loanApplicationTransitionApiJsonValidator,
|
||||
LoanProductDataValidator loanProductCommandFromApiJsonDeserializer,
|
||||
LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer, LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
NoteRepository noteRepository, LoanScheduleCalculationPlatformService calculationPlatformService, LoanAssembler loanAssembler,
|
||||
ClientRepositoryWrapper clientRepository, LoanProductRepository loanProductRepository, LoanChargeAssembler loanChargeAssembler,
|
||||
LoanCollateralAssembler loanCollateralAssembler, AprCalculator aprCalculator, AccountNumberGenerator accountNumberGenerator,
|
||||
LoanSummaryWrapper loanSummaryWrapper, GroupRepositoryWrapper groupRepository,
|
||||
LoanApplicationValidator loanApplicationValidator, LoanRepositoryWrapper loanRepositoryWrapper, NoteRepository noteRepository,
|
||||
LoanAssembler loanAssembler, LoanSummaryWrapper loanSummaryWrapper,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
CalendarRepository calendarRepository, CalendarInstanceRepository calendarInstanceRepository,
|
||||
SavingsAccountAssembler savingsAccountAssembler, AccountAssociationsRepository accountAssociationsRepository,
|
||||
LoanReadPlatformService loanReadPlatformService, AccountNumberFormatRepositoryWrapper accountNumberFormatRepository,
|
||||
BusinessEventNotifierService businessEventNotifierService, ConfigurationDomainService configurationDomainService,
|
||||
LoanScheduleAssembler loanScheduleAssembler, LoanUtilService loanUtilService,
|
||||
CalendarReadPlatformService calendarReadPlatformService,
|
||||
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService,
|
||||
GlobalConfigurationRepositoryWrapper globalConfigurationRepository,
|
||||
FineractEntityToEntityMappingRepository entityMappingRepository,
|
||||
FineractEntityRelationRepository fineractEntityRelationRepository,
|
||||
LoanProductReadPlatformService loanProductReadPlatformService,
|
||||
|
||||
RateAssembler rateAssembler, GLIMAccountInfoWritePlatformService glimAccountInfoWritePlatformService,
|
||||
GLIMAccountInfoRepository glimRepository, LoanRepository loanRepository, GSIMReadPlatformService gsimReadPlatformService,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, LoanProductDataValidator loanProductDataValidator) {
|
||||
SavingsAccountRepositoryWrapper savingsAccountRepository, AccountAssociationsRepository accountAssociationsRepository,
|
||||
LoanReadPlatformService loanReadPlatformService, BusinessEventNotifierService businessEventNotifierService,
|
||||
ConfigurationDomainService configurationDomainService, LoanScheduleAssembler loanScheduleAssembler,
|
||||
LoanUtilService loanUtilService, CalendarReadPlatformService calendarReadPlatformService,
|
||||
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, GLIMAccountInfoRepository glimRepository,
|
||||
LoanRepository loanRepository, GSIMReadPlatformService gsimReadPlatformService,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine) {
|
||||
return new LoanApplicationWritePlatformServiceJpaRepositoryImpl(context, fromJsonHelper, loanApplicationTransitionApiJsonValidator,
|
||||
loanProductCommandFromApiJsonDeserializer, fromApiJsonDeserializer, loanRepositoryWrapper, noteRepository,
|
||||
calculationPlatformService, loanAssembler, clientRepository, loanProductRepository, loanChargeAssembler,
|
||||
loanCollateralAssembler, aprCalculator, accountNumberGenerator, loanSummaryWrapper, groupRepository,
|
||||
loanRepaymentScheduleTransactionProcessorFactory, calendarRepository, calendarInstanceRepository, savingsAccountAssembler,
|
||||
accountAssociationsRepository, loanReadPlatformService, accountNumberFormatRepository, businessEventNotifierService,
|
||||
configurationDomainService, loanScheduleAssembler, loanUtilService, calendarReadPlatformService,
|
||||
entityDatatableChecksWritePlatformService, globalConfigurationRepository, entityMappingRepository,
|
||||
fineractEntityRelationRepository, loanProductReadPlatformService, rateAssembler, glimAccountInfoWritePlatformService,
|
||||
glimRepository, loanRepository, gsimReadPlatformService, defaultLoanLifecycleStateMachine, loanProductDataValidator);
|
||||
loanApplicationValidator, loanRepositoryWrapper, noteRepository, loanAssembler, loanSummaryWrapper,
|
||||
loanRepaymentScheduleTransactionProcessorFactory, calendarRepository, calendarInstanceRepository, savingsAccountRepository,
|
||||
accountAssociationsRepository, loanReadPlatformService, businessEventNotifierService, configurationDomainService,
|
||||
loanScheduleAssembler, loanUtilService, calendarReadPlatformService, entityDatatableChecksWritePlatformService,
|
||||
glimRepository, loanRepository, gsimReadPlatformService, defaultLoanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -254,18 +232,25 @@ public class LoanAccountConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanAssembler.class)
|
||||
public LoanAssembler loanAssembler(FromJsonHelper fromApiJsonHelper, LoanRepositoryWrapper loanRepository,
|
||||
LoanProductRepository loanProductRepository, ClientRepositoryWrapper clientRepository, GroupRepository groupRepository,
|
||||
LoanProductRepository loanProductRepository, ClientRepositoryWrapper clientRepository, GroupRepositoryWrapper groupRepository,
|
||||
FundRepository fundRepository, StaffRepository staffRepository, CodeValueRepositoryWrapper codeValueRepository,
|
||||
LoanScheduleAssembler loanScheduleAssembler, LoanChargeAssembler loanChargeAssembler,
|
||||
LoanCollateralAssembler collateralAssembler, LoanSummaryWrapper loanSummaryWrapper,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
HolidayRepository holidayRepository, ConfigurationDomainService configurationDomainService,
|
||||
WorkingDaysRepositoryWrapper workingDaysRepository, LoanUtilService loanUtilService, RateAssembler rateAssembler,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, ExternalIdFactory externalIdFactory) {
|
||||
WorkingDaysRepositoryWrapper workingDaysRepository, RateAssembler rateAssembler,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, ExternalIdFactory externalIdFactory,
|
||||
AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, GLIMAccountInfoRepository glimRepository,
|
||||
AccountNumberGenerator accountNumberGenerator, GLIMAccountInfoWritePlatformService glimAccountInfoWritePlatformService,
|
||||
LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||
LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler, LoanChargeMapper loanChargeMapper,
|
||||
LoanCollateralManagementMapper loanCollateralManagementMapper) {
|
||||
return new LoanAssembler(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
|
||||
fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler,
|
||||
loanSummaryWrapper, loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService,
|
||||
workingDaysRepository, loanUtilService, rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory);
|
||||
workingDaysRepository, rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository,
|
||||
glimRepository, accountNumberGenerator, glimAccountInfoWritePlatformService, loanCollateralAssembler,
|
||||
calculationPlatformService, loanDisbursementDetailsAssembler, loanChargeMapper, loanCollateralManagementMapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -369,10 +354,9 @@ public class LoanAccountConfiguration {
|
||||
CalendarInstanceRepository calendarInstanceRepository, ConfigurationDomainService configurationDomainService,
|
||||
HolidayRepository holidayRepository, WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
LoanScheduleGeneratorFactory loanScheduleFactory, FloatingRatesReadPlatformService floatingRatesReadPlatformService,
|
||||
FromJsonHelper fromApiJsonHelper, CalendarReadPlatformService calendarReadPlatformService) {
|
||||
CalendarReadPlatformService calendarReadPlatformService) {
|
||||
return new LoanUtilService(applicationCurrencyRepository, calendarInstanceRepository, configurationDomainService, holidayRepository,
|
||||
workingDaysRepository, loanScheduleFactory, floatingRatesReadPlatformService, fromApiJsonHelper,
|
||||
calendarReadPlatformService);
|
||||
workingDaysRepository, loanScheduleFactory, floatingRatesReadPlatformService, calendarReadPlatformService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -390,15 +374,14 @@ public class LoanAccountConfiguration {
|
||||
AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, LoanReadPlatformService loanReadPlatformService,
|
||||
FromJsonHelper fromApiJsonHelper, CalendarRepository calendarRepository,
|
||||
LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
|
||||
LoanApplicationCommandFromApiJsonHelper loanApplicationCommandFromApiJsonHelper,
|
||||
AccountAssociationsRepository accountAssociationRepository, AccountTransferDetailRepository accountTransferDetailRepository,
|
||||
BusinessEventNotifierService businessEventNotifierService, GuarantorDomainService guarantorDomainService,
|
||||
LoanUtilService loanUtilService, LoanSummaryWrapper loanSummaryWrapper,
|
||||
LoanApplicationValidator loanApplicationValidator, AccountAssociationsRepository accountAssociationRepository,
|
||||
AccountTransferDetailRepository accountTransferDetailRepository, BusinessEventNotifierService businessEventNotifierService,
|
||||
GuarantorDomainService guarantorDomainService, LoanUtilService loanUtilService, LoanSummaryWrapper loanSummaryWrapper,
|
||||
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy, CodeValueRepositoryWrapper codeValueRepository,
|
||||
CashierTransactionDataValidator cashierTransactionDataValidator, GLIMAccountInfoRepository glimRepository,
|
||||
LoanRepository loanRepository, RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler,
|
||||
PostDatedChecksRepository postDatedChecksRepository, LoanDisbursementDetailsRepository loanDisbursementDetailsRepository,
|
||||
PostDatedChecksRepository postDatedChecksRepository,
|
||||
LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, LoanAccountLockService loanAccountLockService,
|
||||
ExternalIdFactory externalIdFactory, ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService,
|
||||
@@ -410,11 +393,10 @@ public class LoanAccountConfiguration {
|
||||
paymentDetailWritePlatformService, holidayRepository, configurationDomainService, workingDaysRepository,
|
||||
accountTransfersWritePlatformService, accountTransfersReadPlatformService, accountAssociationsReadPlatformService,
|
||||
loanReadPlatformService, fromApiJsonHelper, calendarRepository, loanScheduleHistoryWritePlatformService,
|
||||
loanApplicationCommandFromApiJsonHelper, accountAssociationRepository, accountTransferDetailRepository,
|
||||
businessEventNotifierService, guarantorDomainService, loanUtilService, loanSummaryWrapper,
|
||||
entityDatatableChecksWritePlatformService, transactionProcessingStrategy, codeValueRepository,
|
||||
cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
|
||||
postDatedChecksRepository, loanDisbursementDetailsRepository, loanRepaymentScheduleInstallmentRepository,
|
||||
loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
|
||||
guarantorDomainService, loanUtilService, loanSummaryWrapper, entityDatatableChecksWritePlatformService,
|
||||
transactionProcessingStrategy, codeValueRepository, cashierTransactionDataValidator, glimRepository, loanRepository,
|
||||
repaymentWithPostDatedChecksAssembler, postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository,
|
||||
defaultLoanLifecycleStateMachine, loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
|
||||
loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService);
|
||||
}
|
||||
@@ -439,4 +421,10 @@ public class LoanAccountConfiguration {
|
||||
BusinessEventNotifierService businessEventNotifierService) {
|
||||
return new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanDisbursementDetailsAssembler.class)
|
||||
public LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler(FromJsonHelper fromApiJsonHelper) {
|
||||
return new LoanDisbursementDetailsAssembler(fromApiJsonHelper);
|
||||
}
|
||||
}
|
||||
|
||||
+71
-22
@@ -48,7 +48,6 @@ import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
@@ -401,7 +400,7 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
|
||||
// interest rates
|
||||
BigDecimal interestRatePerPeriod = null;
|
||||
boolean isInterestBearing = false;
|
||||
if (this.fromApiJsonHelper.parameterExists(IS_LINKED_TO_FLOATING_INTEREST_RATES, element)
|
||||
&& this.fromApiJsonHelper.extractBooleanNamed(IS_LINKED_TO_FLOATING_INTEREST_RATES, element)) {
|
||||
if (isEqualAmortization) {
|
||||
@@ -485,6 +484,7 @@ public final class LoanProductDataValidator {
|
||||
.extractBooleanNamed(IS_FLOATING_INTEREST_RATE_CALCULATION_ALLOWED, element);
|
||||
baseDataValidator.reset().parameter(IS_FLOATING_INTEREST_RATE_CALCULATION_ALLOWED)
|
||||
.value(isFloatingInterestRateCalculationAllowed).notNull().isOneOfTheseValues(true, false);
|
||||
isInterestBearing = true;
|
||||
} else {
|
||||
if (this.fromApiJsonHelper.parameterExists(FLOATING_RATES_ID, element)) {
|
||||
baseDataValidator.reset().parameter(FLOATING_RATES_ID).failWithCode(
|
||||
@@ -522,7 +522,7 @@ public final class LoanProductDataValidator {
|
||||
"isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(INTEREST_RATE_PER_PERIOD, element);
|
||||
BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(INTEREST_RATE_PER_PERIOD, element);
|
||||
baseDataValidator.reset().parameter(INTEREST_RATE_PER_PERIOD).value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
|
||||
|
||||
final String minInterestRatePerPeriodParameterName = MIN_INTEREST_RATE_PER_PERIOD;
|
||||
@@ -564,10 +564,12 @@ public final class LoanProductDataValidator {
|
||||
Locale.getDefault());
|
||||
baseDataValidator.reset().parameter(INTEREST_RATE_FREQUENCY_TYPE).value(interestRateFrequencyType).notNull().inMinMaxRange(0,
|
||||
4);
|
||||
isInterestBearing = interestRatePerPeriod.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
|
||||
// Fixed Length validation
|
||||
fixedLengthValidations(transactionProcessingStrategyCode, interestRatePerPeriod, numberOfRepayments, repaymentEvery, element,
|
||||
|
||||
fixedLengthValidations(transactionProcessingStrategyCode, isInterestBearing, numberOfRepayments, repaymentEvery, element,
|
||||
baseDataValidator);
|
||||
|
||||
// Guarantee Funds
|
||||
@@ -819,6 +821,10 @@ public final class LoanProductDataValidator {
|
||||
advancedPaymentAllocationsValidator.checkGroupingOfAllocationRules(advancedPaymentAllocationsJsonParser
|
||||
.assembleLoanProductPaymentAllocationRules(command, transactionProcessingStrategyCode));
|
||||
}
|
||||
final Integer recurringMoratoriumOnPrincipalPeriods = this.fromApiJsonHelper
|
||||
.extractIntegerWithLocaleNamed("recurringMoratoriumOnPrincipalPeriods", element);
|
||||
validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||
graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
@@ -1267,19 +1273,21 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
|
||||
// grace validation
|
||||
Integer graceOnPrincipalPayment = loanProduct.getLoanProductRelatedDetail().getGraceOnPrincipalPayment();
|
||||
if (this.fromApiJsonHelper.parameterExists(GRACE_ON_PRINCIPAL_PAYMENT, element)) {
|
||||
final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(GRACE_ON_PRINCIPAL_PAYMENT,
|
||||
element);
|
||||
graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(GRACE_ON_PRINCIPAL_PAYMENT, element);
|
||||
baseDataValidator.reset().parameter(GRACE_ON_PRINCIPAL_PAYMENT).value(graceOnPrincipalPayment).zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
Integer graceOnInterestPayment = loanProduct.getLoanProductRelatedDetail().getGraceOnInterestPayment();
|
||||
if (this.fromApiJsonHelper.parameterExists(GRACE_ON_INTEREST_PAYMENT, element)) {
|
||||
final Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(GRACE_ON_INTEREST_PAYMENT, element);
|
||||
graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(GRACE_ON_INTEREST_PAYMENT, element);
|
||||
baseDataValidator.reset().parameter(GRACE_ON_INTEREST_PAYMENT).value(graceOnInterestPayment).zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
Integer graceOnInterestCharged = loanProduct.getLoanProductRelatedDetail().getGraceOnInterestCharged();
|
||||
if (this.fromApiJsonHelper.parameterExists(GRACE_ON_INTEREST_CHARGED, element)) {
|
||||
final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(GRACE_ON_INTEREST_CHARGED, element);
|
||||
graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(GRACE_ON_INTEREST_CHARGED, element);
|
||||
baseDataValidator.reset().parameter(GRACE_ON_INTEREST_CHARGED).value(graceOnInterestCharged).zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
@@ -1368,11 +1376,11 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
|
||||
// interest rates
|
||||
BigDecimal interestRatePerPeriod = null;
|
||||
boolean isLinkedToFloatingInterestRates = loanProduct.isLinkedToFloatingInterestRate();
|
||||
if (this.fromApiJsonHelper.parameterExists(IS_LINKED_TO_FLOATING_INTEREST_RATES, element)) {
|
||||
isLinkedToFloatingInterestRates = this.fromApiJsonHelper.extractBooleanNamed(IS_LINKED_TO_FLOATING_INTEREST_RATES, element);
|
||||
}
|
||||
boolean isInterestBearing = false;
|
||||
if (isLinkedToFloatingInterestRates) {
|
||||
if (isEqualAmortization) {
|
||||
throw new EqualAmortizationUnsupportedFeatureException("floating.interest.rate", "floating interest rate");
|
||||
@@ -1481,6 +1489,7 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
baseDataValidator.reset().parameter(IS_FLOATING_INTEREST_RATE_CALCULATION_ALLOWED)
|
||||
.value(isFloatingInterestRateCalculationAllowed).notNull().isOneOfTheseValues(true, false);
|
||||
isInterestBearing = true;
|
||||
} else {
|
||||
if (this.fromApiJsonHelper.parameterExists(FLOATING_RATES_ID, element)) {
|
||||
baseDataValidator.reset().parameter(FLOATING_RATES_ID).failWithCode(
|
||||
@@ -1536,7 +1545,7 @@ public final class LoanProductDataValidator {
|
||||
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
interestRatePerPeriod = loanProduct.getLoanProductRelatedDetail().getNominalInterestRatePerPeriod();
|
||||
BigDecimal interestRatePerPeriod = loanProduct.getLoanProductRelatedDetail().getNominalInterestRatePerPeriod();
|
||||
if (this.fromApiJsonHelper.parameterExists(INTEREST_RATE_PER_PERIOD, element)) {
|
||||
interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(INTEREST_RATE_PER_PERIOD, element);
|
||||
}
|
||||
@@ -1549,10 +1558,11 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
baseDataValidator.reset().parameter(INTEREST_RATE_FREQUENCY_TYPE).value(interestRateFrequencyType).notNull().inMinMaxRange(0,
|
||||
4);
|
||||
isInterestBearing = interestRatePerPeriod.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
|
||||
// Fixed Length validation
|
||||
fixedLengthValidations(transactionProcessingStrategyCode, interestRatePerPeriod, numberOfRepayments, repaymentEvery, element,
|
||||
fixedLengthValidations(transactionProcessingStrategyCode, isInterestBearing, numberOfRepayments, repaymentEvery, element,
|
||||
baseDataValidator);
|
||||
|
||||
// Guarantee Funds
|
||||
@@ -1806,6 +1816,11 @@ public final class LoanProductDataValidator {
|
||||
"supported.only.for.progressive.loan.schedule.handling",
|
||||
"Vertical repayment schedule processing is only available with `Advanced payment allocation` strategy");
|
||||
}
|
||||
final Integer recurringMoratoriumOnPrincipalPeriods = this.fromApiJsonHelper
|
||||
.extractIntegerWithLocaleNamed("recurringMoratoriumOnPrincipalPeriods", element);
|
||||
|
||||
validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||
graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator);
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -2400,13 +2415,7 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
}
|
||||
|
||||
public void fixedLengthValidations(final String transactionProcessingStrategyCode, final LoanApplicationTerms loanApplicationTerms,
|
||||
final JsonElement element, final DataValidatorBuilder baseDataValidator) {
|
||||
fixedLengthValidations(transactionProcessingStrategyCode, loanApplicationTerms.getAnnualNominalInterestRate(),
|
||||
loanApplicationTerms.getNumberOfRepayments(), loanApplicationTerms.getRepaymentEvery(), element, baseDataValidator);
|
||||
}
|
||||
|
||||
public void fixedLengthValidations(final String transactionProcessingStrategyCode, final BigDecimal interestRatePerPeriod,
|
||||
public void fixedLengthValidations(final String transactionProcessingStrategyCode, final boolean thereIsInterest,
|
||||
final Integer numberOfRepayments, final Integer repayEvery, final JsonElement element,
|
||||
final DataValidatorBuilder baseDataValidator) {
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.FIXED_LENGTH, element)) {
|
||||
@@ -2420,15 +2429,14 @@ public final class LoanProductDataValidator {
|
||||
if (!AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY
|
||||
.equals(transactionProcessingStrategyCode)) {
|
||||
final String errorMsg = "Fixed Length configuration is only allowed with "
|
||||
+ AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY + " stratefy";
|
||||
+ AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY + " strategy";
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.fixed.length.only.supported.for.advanced.payment.allocation",
|
||||
errorMsg, AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY);
|
||||
}
|
||||
|
||||
if (interestRatePerPeriod.compareTo(BigDecimal.ZERO) > 0) {
|
||||
if (thereIsInterest) {
|
||||
final String errorMsg = "Fixed Length configuration is only allowed for zero interest products";
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.fixed.length.only.supported.for.zero.interest", errorMsg,
|
||||
interestRatePerPeriod);
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.fixed.length.only.supported.for.zero.interest", errorMsg);
|
||||
}
|
||||
|
||||
final Integer valueToCompare = ((numberOfRepayments - 1) * repayEvery) + 1;
|
||||
@@ -2465,4 +2473,45 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
}
|
||||
|
||||
public void validateRepaymentPeriodWithGraceSettings(final Integer numberOfRepayments, final Integer graceOnPrincipalPayment,
|
||||
final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final Integer recurringMoratoriumOnPrincipalPeriods,
|
||||
DataValidatorBuilder baseDataValidator) {
|
||||
if (numberOfRepayments <= defaultToZeroIfNull(graceOnPrincipalPayment)) {
|
||||
baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(graceOnPrincipalPayment)
|
||||
.failWithCode(".mustBeLessThan.numberOfRepayments");
|
||||
}
|
||||
|
||||
if (numberOfRepayments <= defaultToZeroIfNull(graceOnInterestPayment)) {
|
||||
baseDataValidator.reset().parameter("graceOnInterestPayment").value(graceOnInterestPayment)
|
||||
.failWithCode(".mustBeLessThan.numberOfRepayments");
|
||||
}
|
||||
|
||||
if (numberOfRepayments < defaultToZeroIfNull(graceOnInterestCharged)) {
|
||||
baseDataValidator.reset().parameter("graceOnInterestCharged").value(graceOnInterestCharged)
|
||||
.failWithCode(".mustBeLessThan.numberOfRepayments");
|
||||
}
|
||||
|
||||
int graceOnPrincipal = 0;
|
||||
if (graceOnPrincipalPayment != null) {
|
||||
graceOnPrincipal = graceOnPrincipalPayment;
|
||||
}
|
||||
int recurMoratoriumOnPrincipal = 0;
|
||||
if (recurringMoratoriumOnPrincipalPeriods != null) {
|
||||
recurMoratoriumOnPrincipal = recurringMoratoriumOnPrincipalPeriods;
|
||||
}
|
||||
|
||||
if ((recurMoratoriumOnPrincipal > 0) && ((numberOfRepayments - graceOnPrincipal) % (recurMoratoriumOnPrincipal + 1) != 1)) {
|
||||
baseDataValidator.reset().parameter("graceOnPrincipalPayments.and.recurringMoratoriumOnPrincipalPeriods")
|
||||
.value(graceOnPrincipal).value(recurMoratoriumOnPrincipal)
|
||||
.failWithCode("causes.principal.moratorium.for.last.installment");
|
||||
}
|
||||
}
|
||||
|
||||
private Integer defaultToZeroIfNull(final Integer value) {
|
||||
Integer result = value;
|
||||
if (value == null) {
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+21
-1
@@ -72,6 +72,7 @@ import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.EarlyPaymentLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
@@ -2079,8 +2080,12 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
overpaymentAccount);
|
||||
assertNotNull(localLoanProductId);
|
||||
|
||||
Integer localLoanProductId2 = createLoanProduct("500", "15", "4", false, LoanScheduleType.CUMULATIVE, assetAccount,
|
||||
incomeAccount, expenseAccount, overpaymentAccount);
|
||||
assertNotNull(localLoanProductId2);
|
||||
|
||||
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
|
||||
() -> applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(500.0), 45, 15, 3,
|
||||
() -> applyForLoanApplication(client.getClientId(), localLoanProductId2, BigDecimal.valueOf(500.0), 45, 15, 3,
|
||||
BigDecimal.ZERO, "01 January 2023", "01 January 2023",
|
||||
FineractStyleLoanRepaymentScheduleTransactionProcessor.STRATEGY_CODE,
|
||||
LoanScheduleProcessingType.VERTICAL.name()));
|
||||
@@ -4396,6 +4401,21 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
return loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private static Integer createLoanProduct(final String principal, final String repaymentAfterEvery, final String numberOfRepayments,
|
||||
boolean autoPayForDownPayment, LoanScheduleType loanScheduleType, final Account... accounts) {
|
||||
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withMinPrincipal(principal).withPrincipal(principal)
|
||||
.withRepaymentTypeAsDays().withRepaymentAfterEvery(repaymentAfterEvery).withNumberOfRepayments(numberOfRepayments)
|
||||
.withEnableDownPayment(true, "25", autoPayForDownPayment).withinterestRatePerPeriod("0")
|
||||
.withInterestRateFrequencyTypeAsMonths()
|
||||
.withRepaymentStrategy(EarlyPaymentLoanRepaymentScheduleTransactionProcessor.STRATEGY_CODE)
|
||||
.withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRulePeriodicAccrual(accounts)
|
||||
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withInterestTypeAsDecliningBalance().withMultiDisburse()
|
||||
.withDisallowExpectedDisbursements(true).withLoanScheduleType(loanScheduleType).withDaysInMonth("30").withDaysInYear("365")
|
||||
.withMoratorium("0", "0").build(null);
|
||||
return loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private static ArrayList<HashMap<String, Object>> createLoanProductGetError(final String principal, final String repaymentAfterEvery,
|
||||
final String numberOfRepayments, boolean autoPayForDownPayment, LoanScheduleType loanScheduleType,
|
||||
LoanScheduleProcessingType loanScheduleProcessingType, AdvancedPaymentData allocationRuleData, final Account... accounts) {
|
||||
|
||||
+5
-1
@@ -7479,12 +7479,16 @@ public class ClientLoanIntegrationTest {
|
||||
map.put("amount", amount);
|
||||
}
|
||||
if (charge.get("dueDate") != null) {
|
||||
map.put("dueDate", charge.get("dueDate"));
|
||||
map.put("dueDate", DATE_TIME_FORMATTER.format(fromArrayToLocalDate((List) charge.get("dueDate"))));
|
||||
}
|
||||
map.put("chargeId", charge.get("chargeId"));
|
||||
return map;
|
||||
}
|
||||
|
||||
private LocalDate fromArrayToLocalDate(List<Integer> dueDate) {
|
||||
return LocalDate.of(dueDate.get(0), dueDate.get(1), dueDate.get(2));
|
||||
}
|
||||
|
||||
private HashMap createTrancheDetail(final String date, final String amount) {
|
||||
HashMap detail = new HashMap();
|
||||
detail.put("expectedDisbursementDate", date);
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ public class LoanApplicationApprovalTest {
|
||||
log.info("---------------------------------CLIENT CREATED WITH ID--------------------------------------------------- {}", clientId);
|
||||
|
||||
final Integer loanProductId = this.loanTransactionHelper
|
||||
.getLoanProductId(new LoanProductTestBuilder().withInterestTypeAsDecliningBalance().withTranches(true)
|
||||
.getLoanProductId(new LoanProductTestBuilder().withInterestTypeAsDecliningBalance().withTranches(false)
|
||||
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).build(null));
|
||||
log.info("----------------------------------LOAN PRODUCT CREATED WITH ID------------------------------------------- {}",
|
||||
loanProductId);
|
||||
|
||||
Reference in New Issue
Block a user