FINERACT-2090: Restructure Loan creation and modification
This commit is contained in:
+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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user