FINERACT-1981: Advanced payment allocation validation between Loan product and Loan account

This commit is contained in:
Jose Alberto Hernandez
2024-01-12 15:41:36 +01:00
committed by Adam Saghy
parent fec768c192
commit e3a771aa71
19 changed files with 145 additions and 60 deletions
@@ -1215,8 +1215,6 @@ final class LoansApiResourceSwagger {
public Integer graceOnInterestPayment;
@Schema(example = "1")
public Integer graceOnArrearsAgeing;
@Schema(example = "CUMULATIVE")
public String loanScheduleType;
@Schema(example = "HORIZONTAL")
public String loanScheduleProcessingType;
}
@@ -1362,8 +1360,6 @@ final class LoansApiResourceSwagger {
public List<PutLoansLoanIdChanges> charges;
public List<PutLoansLoanIdCollateral> collateral;
public List<PutLoansLoanIdDisbursementData> disbursementData;
@Schema(example = "CUMULATIVE")
public String loanScheduleType;
@Schema(example = "HORIZONTAL")
public String loanScheduleProcessingType;
@@ -53,7 +53,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollateralQuantity;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals;
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;
import org.apache.fineract.portfolio.loanproduct.domain.AdvancedPaymentAllocationsValidator;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
@@ -105,7 +104,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
LoanApiConstants.lastApplication, // glim specific
LoanApiConstants.daysInYearTypeParameterName, LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
LoanApiConstants.DISALLOW_EXPECTED_DISBURSEMENTS, LoanApiConstants.FRAUD_ATTRIBUTE_NAME,
LoanProductConstants.LOAN_SCHEDULE_TYPE, LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE));
LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE));
public static final String LOANAPPLICATION_UNDO = "loanapplication.undo";
private final FromJsonHelper fromApiJsonHelper;
@@ -559,10 +558,6 @@ public final class LoanApplicationCommandFromApiJsonHelper {
validateLoanMultiDisbursementDate(element, baseDataValidator, expectedDisbursementDate, principal);
final String loanScheduleType = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.LOAN_SCHEDULE_TYPE, element);
baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_TYPE).value(loanScheduleType).ignoreIfNull()
.isOneOfEnumValues(LoanScheduleType.class);
String loanScheduleProcessingType = loanProduct.getLoanProductRelatedDetail().getLoanScheduleProcessingType().name();
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, element)) {
loanScheduleProcessingType = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE,
@@ -1100,12 +1095,6 @@ public final class LoanApplicationCommandFromApiJsonHelper {
validateLoanMultiDisbursementDate(element, baseDataValidator, expectedDisbursementDate, principal);
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct);
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.LOAN_SCHEDULE_TYPE, element)) {
final String loanScheduleType = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.LOAN_SCHEDULE_TYPE, element);
baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_TYPE).value(loanScheduleType).ignoreIfNull()
.isOneOfEnumValues(LoanScheduleType.class);
}
String loanScheduleProcessingType = existingLoanApplication.getLoanRepaymentScheduleDetail().getLoanScheduleProcessingType().name();
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, element)) {
loanScheduleProcessingType = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE,
@@ -116,12 +116,14 @@ 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.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
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.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.serialization.LoanApplicationCommandFromApiJsonHelper;
@@ -689,7 +691,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
public CommandProcessingResult modifyApplication(final Long loanId, final JsonCommand command) {
try {
AppUser currentUser = getAppUserIfPresent();
final Loan existingLoanApplication = retrieveLoanBy(loanId);
if (!existingLoanApplication.isSubmittedAndPendingApproval()) {
throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(loanId);
@@ -938,8 +939,12 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
if (changes.containsKey(strategyCodeParamName)) {
final String strategy = command.stringValueOfParameterNamed(strategyCodeParamName);
existingLoanApplication.updateTransactionProcessingStrategy(strategy,
this.loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(strategy).getName());
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(strategy);
validateTransactionProcessingStrategy(existingLoanApplication.getLoanProductRelatedDetail().getLoanScheduleType(),
loanRepaymentScheduleTransactionProcessor.getCode());
existingLoanApplication.updateTransactionProcessingStrategy(strategy, loanRepaymentScheduleTransactionProcessor.getName());
}
/**
@@ -1703,4 +1708,22 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
}
private void validateTransactionProcessingStrategy(final LoanScheduleType loanScheduleType, final String loanRepaymentStrategy) {
// PROGRESSIVE: Repayment strategy MUST be only "advanced payment allocation"
if (loanScheduleType.equals(LoanScheduleType.PROGRESSIVE)) {
if (!loanRepaymentStrategy.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 (loanRepaymentStrategy.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");
}
}
}
}
@@ -34,6 +34,7 @@ import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDoma
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
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;
@@ -77,7 +78,9 @@ import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementData
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.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;
@@ -248,6 +251,23 @@ public class LoanAssembler {
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()) {