FINERACT-2174: Allow for recognition of Interest on first day end of day - Introducing the flag

This commit is contained in:
Jose Alberto Hernandez
2025-02-05 19:52:44 +01:00
committed by Adam Saghy
parent 7d0474cc17
commit 3a5888ee4b
36 changed files with 366 additions and 414 deletions
@@ -815,6 +815,14 @@
"null", "null",
"boolean" "boolean"
] ]
},
{
"default": null,
"name": "interestRecognitionOnDisbursementDate",
"type": [
"null",
"boolean"
]
} }
] ]
} }
@@ -641,6 +641,14 @@
"null", "null",
"org.apache.fineract.avro.loan.v1.DelinquencyBucketDataV1" "org.apache.fineract.avro.loan.v1.DelinquencyBucketDataV1"
] ]
},
{
"default": null,
"name": "interestRecognitionOnDisbursementDate",
"type": [
"null",
"boolean"
]
} }
] ]
} }
@@ -177,6 +177,8 @@ public interface LoanApiConstants {
// Data Validator names // Data Validator names
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud"; String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";
String INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE = "interestRecognitionOnDisbursementDate";
// Loan Summary Transaction Types // Loan Summary Transaction Types
List<Integer> LOAN_SUMMARY_TRANSACTION_TYPES = List.of(LoanTransactionType.CHARGE_ADJUSTMENT.getValue(), // List<Integer> LOAN_SUMMARY_TRANSACTION_TYPES = List.of(LoanTransactionType.CHARGE_ADJUSTMENT.getValue(), //
LoanTransactionType.CHARGEBACK.getValue(), // LoanTransactionType.CHARGEBACK.getValue(), //
@@ -229,6 +229,7 @@ public final class LoanApplicationTerms {
private boolean enableAccrualActivityPosting; private boolean enableAccrualActivityPosting;
private List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes; private List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes;
private LoanChargeOffBehaviour chargeOffBehaviour; private LoanChargeOffBehaviour chargeOffBehaviour;
private boolean interestRecognitionOnDisbursementDate;
private LoanApplicationTerms(Builder builder) { private LoanApplicationTerms(Builder builder) {
this.currency = builder.currency; this.currency = builder.currency;
@@ -460,7 +461,7 @@ public final class LoanApplicationTerms {
final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType, final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour) { final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null; final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null; final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
@@ -479,7 +480,8 @@ public final class LoanApplicationTerms {
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment, isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment, isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour); fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);
} }
@@ -552,7 +554,8 @@ public final class LoanApplicationTerms {
fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled,
disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
loanScheduleType, loanScheduleProcessingType, fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(), loanScheduleType, loanScheduleProcessingType, fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(),
loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour()); loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour(),
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate());
} }
private LoanApplicationTerms(final CurrencyData currency, final Integer loanTermFrequency, private LoanApplicationTerms(final CurrencyData currency, final Integer loanTermFrequency,
@@ -582,7 +585,8 @@ public final class LoanApplicationTerms {
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean isAutoRepaymentForDownPaymentEnabled, final BigDecimal disbursedAmountPercentageForDownPayment, final boolean isAutoRepaymentForDownPaymentEnabled,
final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType, final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, boolean enableAccrualActivityPosting, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, boolean enableAccrualActivityPosting,
final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final LoanChargeOffBehaviour chargeOffBehaviour) { final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final LoanChargeOffBehaviour chargeOffBehaviour,
final boolean interestRecognitionOnDisbursementDate) {
this.currency = currency; this.currency = currency;
this.loanTermFrequency = loanTermFrequency; this.loanTermFrequency = loanTermFrequency;
@@ -682,6 +686,7 @@ public final class LoanApplicationTerms {
this.fixedLength = fixedLength; this.fixedLength = fixedLength;
this.supportedInterestRefundTypes = supportedInterestRefundTypes; this.supportedInterestRefundTypes = supportedInterestRefundTypes;
this.chargeOffBehaviour = chargeOffBehaviour; this.chargeOffBehaviour = chargeOffBehaviour;
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
} }
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate, public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
@@ -1543,7 +1548,7 @@ public final class LoanApplicationTerms {
this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled, this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled,
this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType, this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType,
this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting, this.supportedInterestRefundTypes, this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting, this.supportedInterestRefundTypes,
this.chargeOffBehaviour); this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate);
} }
public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() { public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() {
@@ -169,4 +169,5 @@ public interface LoanProductConstants {
String SUPPORTED_INTEREST_REFUND_TYPES = "supportedInterestRefundTypes"; String SUPPORTED_INTEREST_REFUND_TYPES = "supportedInterestRefundTypes";
String CHARGE_OFF_BEHAVIOUR = "chargeOffBehaviour"; String CHARGE_OFF_BEHAVIOUR = "chargeOffBehaviour";
String INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE = "interestRecognitionOnDisbursementDate";
} }
@@ -172,6 +172,8 @@ final class LoanProductsApiResourceSwagger {
public Integer repaymentStartDateType; public Integer repaymentStartDateType;
@Schema(example = "false") @Schema(example = "false")
public Boolean enableAccrualActivityPosting; public Boolean enableAccrualActivityPosting;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;
// Interest Recalculation // Interest Recalculation
@Schema(example = "false") @Schema(example = "false")
@@ -1421,6 +1423,8 @@ final class LoanProductsApiResourceSwagger {
public List<StringEnumOptionData> supportedInterestRefundTypes; public List<StringEnumOptionData> supportedInterestRefundTypes;
public List<GetLoanProductsTemplateResponse.GetLoanProductsChargeOffReasonOptions> chargeOffReasonOptions; public List<GetLoanProductsTemplateResponse.GetLoanProductsChargeOffReasonOptions> chargeOffReasonOptions;
public StringEnumOptionData chargeOffBehaviour; public StringEnumOptionData chargeOffBehaviour;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;
} }
@Schema(description = "PutLoanProductsProductIdRequest") @Schema(description = "PutLoanProductsProductIdRequest")
@@ -1558,6 +1562,8 @@ final class LoanProductsApiResourceSwagger {
public Boolean enableAutoRepaymentForDownPayment; public Boolean enableAutoRepaymentForDownPayment;
@Schema(example = "1") @Schema(example = "1")
public Integer repaymentStartDateType; public Integer repaymentStartDateType;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;
// Interest Recalculation // Interest Recalculation
@Schema(example = "false") @Schema(example = "false")
@@ -234,6 +234,7 @@ public class LoanProductData implements Serializable {
private final EnumOptionData loanScheduleType; private final EnumOptionData loanScheduleType;
private final EnumOptionData loanScheduleProcessingType; private final EnumOptionData loanScheduleProcessingType;
private final boolean interestRecognitionOnDisbursementDate;
/** /**
* Used when returning lookup information about loan product for dropdowns. * Used when returning lookup information about loan product for dropdowns.
@@ -338,6 +339,7 @@ public class LoanProductData implements Serializable {
final boolean enableAccrualActivityPosting = false; final boolean enableAccrualActivityPosting = false;
final List<StringEnumOptionData> supportedInterestRefundTypes = null; final List<StringEnumOptionData> supportedInterestRefundTypes = null;
final StringEnumOptionData chargeOffBehaviour = null; final StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -358,7 +360,8 @@ public class LoanProductData implements Serializable {
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour); loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);
} }
@@ -461,6 +464,7 @@ public class LoanProductData implements Serializable {
final boolean enableAccrualActivityPosting = false; final boolean enableAccrualActivityPosting = false;
final List<StringEnumOptionData> supportedInterestRefundTypes = null; final List<StringEnumOptionData> supportedInterestRefundTypes = null;
final StringEnumOptionData chargeOffBehaviour = null; final StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -481,7 +485,8 @@ public class LoanProductData implements Serializable {
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour); loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);
} }
@@ -591,6 +596,7 @@ public class LoanProductData implements Serializable {
final boolean enableAccrualActivityPosting = false; final boolean enableAccrualActivityPosting = false;
final List<StringEnumOptionData> supportedInterestRefundTypes = null; final List<StringEnumOptionData> supportedInterestRefundTypes = null;
final StringEnumOptionData chargeOffBehaviour = LoanChargeOffBehaviour.REGULAR.getValueAsStringEnumOptionData(); final StringEnumOptionData chargeOffBehaviour = LoanChargeOffBehaviour.REGULAR.getValueAsStringEnumOptionData();
final boolean interestRecognitionOnDisbursementDate = false;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -611,7 +617,8 @@ public class LoanProductData implements Serializable {
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour); loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);
} }
@@ -715,6 +722,7 @@ public class LoanProductData implements Serializable {
final boolean enableAccrualActivityPosting = false; final boolean enableAccrualActivityPosting = false;
final List<StringEnumOptionData> supportedInterestRefundTypes = null; final List<StringEnumOptionData> supportedInterestRefundTypes = null;
final StringEnumOptionData chargeOffBehaviour = null; final StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -735,7 +743,8 @@ public class LoanProductData implements Serializable {
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour); loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);
} }
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings, public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
@@ -789,7 +798,7 @@ public class LoanProductData implements Serializable {
final EnumOptionData repaymentStartDateType, final boolean enableInstallmentLevelDelinquency, final EnumOptionData repaymentStartDateType, final boolean enableInstallmentLevelDelinquency,
final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength, final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<StringEnumOptionData> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, final List<StringEnumOptionData> supportedInterestRefundTypes,
StringEnumOptionData chargeOffBehaviour) { StringEnumOptionData chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.shortName = shortName; this.shortName = shortName;
@@ -932,6 +941,7 @@ public class LoanProductData implements Serializable {
this.chargeOffBehaviour = chargeOffBehaviour; this.chargeOffBehaviour = chargeOffBehaviour;
this.chargeOffBehaviourOptions = null; this.chargeOffBehaviourOptions = null;
this.chargeOffReasonOptions = null; this.chargeOffReasonOptions = null;
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
} }
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions, public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
@@ -1116,6 +1126,7 @@ public class LoanProductData implements Serializable {
this.chargeOffBehaviour = productData.chargeOffBehaviour; this.chargeOffBehaviour = productData.chargeOffBehaviour;
this.chargeOffBehaviourOptions = chargeOffBehaviourOptions; this.chargeOffBehaviourOptions = chargeOffBehaviourOptions;
this.chargeOffReasonOptions = chargeOffReasonOptions; this.chargeOffReasonOptions = chargeOffReasonOptions;
this.interestRecognitionOnDisbursementDate = productData.interestRecognitionOnDisbursementDate;
} }
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) { private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
@@ -443,6 +443,12 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
final boolean enableAccrualActivityPosting = command final boolean enableAccrualActivityPosting = command
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING); .booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING);
boolean interestRecognitionOnDisbursementDate = false;
if (command.parameterExists(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE)) {
interestRecognitionOnDisbursementDate = command
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE);
}
List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes = new ArrayList<>(); List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes = new ArrayList<>();
if (command.parameterExists(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES)) { if (command.parameterExists(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES)) {
JsonArray supportedTransactionsForInterestRefund = command JsonArray supportedTransactionsForInterestRefund = command
@@ -480,7 +486,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent, allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour); enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate);
} }
@@ -699,7 +705,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
final boolean enableInstallmentLevelDelinquency, final LoanScheduleType loanScheduleType, final boolean enableInstallmentLevelDelinquency, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour) { final LoanChargeOffBehaviour chargeOffBehaviour, final boolean isInterestRecognitionOnDisbursementDate) {
this.fund = fund; this.fund = fund;
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
@@ -749,7 +755,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(), inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(),
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting,
supportedInterestRefundTypes, chargeOffBehaviour); supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate);
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal, this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
@@ -802,7 +808,6 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
this.repaymentStartDateType = repaymentStartDateType; this.repaymentStartDateType = repaymentStartDateType;
this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency; this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency;
validateLoanProductPreSave(); validateLoanProductPreSave();
} }
@@ -870,6 +875,14 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
} }
} }
if (this.getLoanProductRelatedDetail().isInterestRecognitionOnDisbursementDate()
&& this.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.CUMULATIVE)) {
throw new LoanProductGeneralRuleException(
"interestRecognitionOnDisbursementDate.is.only.supported.for.progressive.loan.schedule.type",
"interestRecognitionOnDisbursementDate is only supported for progressive loan schedule type");
}
} }
public MonetaryCurrency getCurrency() { public MonetaryCurrency getCurrency() {
@@ -1621,4 +1634,5 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
public void updateEnableInstallmentLevelDelinquency(boolean enableInstallmentLevelDelinquency) { public void updateEnableInstallmentLevelDelinquency(boolean enableInstallmentLevelDelinquency) {
this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency; this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency;
} }
} }
@@ -165,6 +165,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private LoanChargeOffBehaviour chargeOffBehaviour; private LoanChargeOffBehaviour chargeOffBehaviour;
@Column(name = "interest_recognition_on_disbursement_date", nullable = false)
private boolean interestRecognitionOnDisbursementDate = false;
public static LoanProductRelatedDetail createFrom(final CurrencyData currencyData, final BigDecimal principal, public static LoanProductRelatedDetail createFrom(final CurrencyData currencyData, final BigDecimal principal,
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod, final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
@@ -178,7 +181,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType, final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour) { final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData); final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData);
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
@@ -188,7 +191,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
chargeOffBehaviour); chargeOffBehaviour, interestRecognitionOnDisbursementDate);
} }
protected LoanProductRelatedDetail() { protected LoanProductRelatedDetail() {
@@ -208,7 +211,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType, final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour) { final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
this.currency = currency; this.currency = currency;
this.principal = defaultPrincipal; this.principal = defaultPrincipal;
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod; this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
@@ -244,6 +247,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.enableAccrualActivityPosting = enableAccrualActivityPosting; this.enableAccrualActivityPosting = enableAccrualActivityPosting;
this.supportedInterestRefundTypes = supportedInterestRefundTypes; this.supportedInterestRefundTypes = supportedInterestRefundTypes;
this.chargeOffBehaviour = chargeOffBehaviour; this.chargeOffBehaviour = chargeOffBehaviour;
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
} }
private Integer defaultToNullIfZero(final Integer value) { private Integer defaultToNullIfZero(final Integer value) {
@@ -520,6 +524,14 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.isEqualAmortization = newValue; this.isEqualAmortization = newValue;
} }
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE,
this.isInterestRecognitionOnDisbursementDate())) {
final boolean newValue = command
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE);
actualChanges.put(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, newValue);
this.updateInterestRecognitionOnDisbursementDate(newValue);
}
return actualChanges; return actualChanges;
} }
@@ -546,4 +558,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.interestPeriodFrequencyType = PeriodFrequencyType.INVALID; this.interestPeriodFrequencyType = PeriodFrequencyType.INVALID;
this.annualNominalInterestRate = null; this.annualNominalInterestRate = null;
} }
public void updateInterestRecognitionOnDisbursementDate(boolean interestRecognitionOnDisbursementDate) {
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
}
} }
@@ -48,4 +48,5 @@
<include relativeToChangelogFile="true" file="parts/1023_add_charge_off_behaviour.xml"/> <include relativeToChangelogFile="true" file="parts/1023_add_charge_off_behaviour.xml"/>
<include relativeToChangelogFile="true" file="parts/1024_accrual_adjustment_transaction_type.xml"/> <include relativeToChangelogFile="true" file="parts/1024_accrual_adjustment_transaction_type.xml"/>
<include relativeToChangelogFile="true" file="parts/1025_store_pre_closure_strategy_to_loan_level.xml"/> <include relativeToChangelogFile="true" file="parts/1025_store_pre_closure_strategy_to_loan_level.xml"/>
<include relativeToChangelogFile="true" file="parts/1026_add_interest_recognition_flag.xml"/>
</databaseChangeLog> </databaseChangeLog>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<changeSet author="fineract" id="1026-1">
<addColumn tableName="m_product_loan">
<column name="interest_recognition_on_disbursement_date" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="1026-2">
<addColumn tableName="m_loan">
<column name="interest_recognition_on_disbursement_date" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -244,7 +244,7 @@ public class LoansApiResource {
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount, LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount,
LoanApiConstants.clientActiveLoanOptions, LoanApiConstants.datatables, LoanProductConstants.RATES_PARAM_NAME, LoanApiConstants.clientActiveLoanOptions, LoanApiConstants.datatables, LoanProductConstants.RATES_PARAM_NAME,
LoanApiConstants.MULTIDISBURSE_DETAILS_PARAMNAME, LoanApiConstants.EMI_AMOUNT_VARIATIONS_PARAMNAME, LoanApiConstants.MULTIDISBURSE_DETAILS_PARAMNAME, LoanApiConstants.EMI_AMOUNT_VARIATIONS_PARAMNAME,
LoanApiConstants.COLLECTION_PARAMNAME)); LoanApiConstants.COLLECTION_PARAMNAME, LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE));
private static final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount")); private static final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount"));
private static final Set<String> GLIM_ACCOUNTS_DATA_PARAMETERS = new HashSet<>(Arrays.asList("glimId", "groupId", "clientId", private static final Set<String> GLIM_ACCOUNTS_DATA_PARAMETERS = new HashSet<>(Arrays.asList("glimId", "groupId", "clientId",
@@ -1204,6 +1204,8 @@ final class LoansApiResourceSwagger {
@Schema(description = "List of GetLoansLoanIdLoanTermVariations") @Schema(description = "List of GetLoansLoanIdLoanTermVariations")
public List<GetLoansLoanIdLoanTermVariations> loanTermVariations; public List<GetLoansLoanIdLoanTermVariations> loanTermVariations;
public StringEnumOptionData chargeOffBehaviour; public StringEnumOptionData chargeOffBehaviour;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;
} }
@Schema(description = "GetLoansResponse") @Schema(description = "GetLoansResponse")
@@ -1303,6 +1305,8 @@ final class LoansApiResourceSwagger {
public Boolean enableAutoRepaymentForDownPayment; public Boolean enableAutoRepaymentForDownPayment;
@Schema(example = "10.00") @Schema(example = "10.00")
public BigDecimal fixedEmiAmount; public BigDecimal fixedEmiAmount;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;
public List<PostLoansRequestChargeData> charges; public List<PostLoansRequestChargeData> charges;
@@ -1473,6 +1477,8 @@ final class LoansApiResourceSwagger {
public BigDecimal disbursedAmountPercentageForDownPayment; public BigDecimal disbursedAmountPercentageForDownPayment;
@Schema(example = "false") @Schema(example = "false")
public Boolean enableAutoRepaymentForDownPayment; public Boolean enableAutoRepaymentForDownPayment;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;
static final class PutLoansLoanIdChanges { static final class PutLoansLoanIdChanges {
@@ -266,6 +266,7 @@ public class LoanAccountData {
private Boolean enableDownPayment; private Boolean enableDownPayment;
private BigDecimal disbursedAmountPercentageForDownPayment; private BigDecimal disbursedAmountPercentageForDownPayment;
private Boolean enableAutoRepaymentForDownPayment; private Boolean enableAutoRepaymentForDownPayment;
private Boolean interestRecognitionOnDisbursementDate;
private EnumOptionData loanScheduleType; private EnumOptionData loanScheduleType;
private EnumOptionData loanScheduleProcessingType; private EnumOptionData loanScheduleProcessingType;
@@ -419,7 +420,8 @@ public class LoanAccountData {
.setIsEqualAmortization(product.isEqualAmortization()) .setIsEqualAmortization(product.isEqualAmortization())
.setFixedPrincipalPercentagePerInstallment(product.getFixedPrincipalPercentagePerInstallment()) .setFixedPrincipalPercentagePerInstallment(product.getFixedPrincipalPercentagePerInstallment())
.setDelinquent(CollectionData.template()).setDisallowExpectedDisbursements(product.getDisallowExpectedDisbursements()) .setDelinquent(CollectionData.template()).setDisallowExpectedDisbursements(product.getDisallowExpectedDisbursements())
.setLoanScheduleType(product.getLoanScheduleType()).setLoanScheduleProcessingType(product.getLoanScheduleProcessingType()); .setLoanScheduleType(product.getLoanScheduleType()).setLoanScheduleProcessingType(product.getLoanScheduleProcessingType())
.setInterestRecognitionOnDisbursementDate(product.isInterestRecognitionOnDisbursementDate());
} }
/* /*
@@ -456,7 +458,8 @@ public class LoanAccountData {
LocalDate lastClosedBusinessDate, LocalDate overpaidOnDate, final boolean chargedOff, final boolean enableDownPayment, LocalDate lastClosedBusinessDate, LocalDate overpaidOnDate, final boolean chargedOff, final boolean enableDownPayment,
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment,
final boolean enableInstallmentLevelDelinquency, final EnumOptionData loanScheduleType, final boolean enableInstallmentLevelDelinquency, final EnumOptionData loanScheduleType,
final EnumOptionData loanScheduleProcessingType, final Integer fixedLength, final StringEnumOptionData chargeOffBehaviour) { final EnumOptionData loanScheduleProcessingType, final Integer fixedLength, final StringEnumOptionData chargeOffBehaviour,
final boolean isInterestRecognitionOnDisbursementDate) {
final CollectionData delinquent = CollectionData.template(); final CollectionData delinquent = CollectionData.template();
@@ -501,7 +504,8 @@ public class LoanAccountData {
.setEnableAutoRepaymentForDownPayment(enableAutoRepaymentForDownPayment) .setEnableAutoRepaymentForDownPayment(enableAutoRepaymentForDownPayment)
.setEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency).setLoanScheduleType(loanScheduleType) .setEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency).setLoanScheduleType(loanScheduleType)
.setLoanScheduleProcessingType(loanScheduleProcessingType).setFixedLength(fixedLength) .setLoanScheduleProcessingType(loanScheduleProcessingType).setFixedLength(fixedLength)
.setChargeOffBehaviour(chargeOffBehaviour); .setChargeOffBehaviour(chargeOffBehaviour)
.setInterestRecognitionOnDisbursementDate(isInterestRecognitionOnDisbursementDate);
} }
/* /*
@@ -518,6 +518,12 @@ public class LoanScheduleAssembler {
fixedLength = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(LoanProductConstants.FIXED_LENGTH, element); fixedLength = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(LoanProductConstants.FIXED_LENGTH, element);
} }
Boolean interestRecognitionOnDisbursementDate = loanProduct.getLoanProductRelatedDetail().isInterestRecognitionOnDisbursementDate();
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, element)) {
interestRecognitionOnDisbursementDate = this.fromApiJsonHelper
.extractBooleanNamed(LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, element);
}
return LoanApplicationTerms.assembleFrom(applicationCurrency.toData(), loanTermFrequency, loanTermPeriodFrequencyType, return LoanApplicationTerms.assembleFrom(applicationCurrency.toData(), loanTermFrequency, loanTermPeriodFrequencyType,
numberOfRepayments, repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod, numberOfRepayments, repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod,
interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
@@ -535,7 +541,7 @@ public class LoanScheduleAssembler {
loanScheduleType, loanScheduleProcessingType, fixedLength, loanScheduleType, loanScheduleProcessingType, fixedLength,
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting(), loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting(),
loanProduct.getLoanProductRelatedDetail().getSupportedInterestRefundTypes(), loanProduct.getLoanProductRelatedDetail().getSupportedInterestRefundTypes(),
loanProduct.getLoanProductRelatedDetail().getChargeOffBehaviour()); loanProduct.getLoanProductRelatedDetail().getChargeOffBehaviour(), interestRecognitionOnDisbursementDate);
} }
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery, private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
@@ -1452,6 +1458,14 @@ public class LoanScheduleAssembler {
} }
loanProductRelatedDetail.setDisbursedAmountPercentageForDownPayment(disbursedAmountPercentageDownPayment); loanProductRelatedDetail.setDisbursedAmountPercentageForDownPayment(disbursedAmountPercentageDownPayment);
} }
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE,
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate())) {
final boolean newValue = command
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE);
changes.put(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, newValue);
loanProductRelatedDetail.updateInterestRecognitionOnDisbursementDate(newValue);
}
} }
public Pair<Loan, Map<String, Object>> assembleLoanApproval(AppUser currentUser, JsonCommand command, Long loanId) { public Pair<Loan, Map<String, Object>> assembleLoanApproval(AppUser currentUser, JsonCommand command, Long loanId) {
@@ -169,7 +169,8 @@ public final class LoanApplicationValidator {
LoanApiConstants.DISALLOW_EXPECTED_DISBURSEMENTS, LoanApiConstants.FRAUD_ATTRIBUTE_NAME, LoanApiConstants.DISALLOW_EXPECTED_DISBURSEMENTS, LoanApiConstants.FRAUD_ATTRIBUTE_NAME,
LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH, LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH,
LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.ENABLE_DOWN_PAYMENT,
LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT)); LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT,
LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE));
public static final String LOANAPPLICATION_UNDO = "loanapplication.undo"; public static final String LOANAPPLICATION_UNDO = "loanapplication.undo";
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -760,6 +761,14 @@ public final class LoanApplicationValidator {
validateTransactionProcessingStrategy(transactionProcessingStrategy, loanProduct); validateTransactionProcessingStrategy(transactionProcessingStrategy, loanProduct);
fixedLengthValidations(element); fixedLengthValidations(element);
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, element)) {
if (!LoanScheduleType.PROGRESSIVE.equals(loanProduct.getLoanProductRelatedDetail().getLoanScheduleType())) {
List<String> unsupportedParameterList = new ArrayList<>();
unsupportedParameterList.add(LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE);
throw new UnsupportedParameterException(unsupportedParameterList);
}
}
} }
private void fixedLengthValidations(final JsonElement element) { private void fixedLengthValidations(final JsonElement element) {
@@ -1442,6 +1451,14 @@ public final class LoanApplicationValidator {
loanProductDataValidator.validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment, loanProductDataValidator.validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment,
graceOnInterestPayment, graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator); graceOnInterestPayment, graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator);
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, element)) {
if (!LoanScheduleType.PROGRESSIVE.equals(loanProduct.getLoanProductRelatedDetail().getLoanScheduleType())) {
List<String> unsupportedParameterList = new ArrayList<>();
unsupportedParameterList.add(LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE);
throw new UnsupportedParameterException(unsupportedParameterList);
}
}
}); });
} }
@@ -75,7 +75,7 @@ public final class LoanScheduleValidator {
LoanApiConstants.daysInYearTypeParameterName, LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, LoanApiConstants.daysInYearTypeParameterName, LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
LoanProductConstants.FIXED_LENGTH, LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.FIXED_LENGTH, LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY,
LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT,
LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT)); LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -702,7 +702,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
+ " topup.topup_amount as topupAmount, l.last_closed_business_date as lastClosedBusinessDate,l.overpaidon_date as overpaidOnDate, " + " topup.topup_amount as topupAmount, l.last_closed_business_date as lastClosedBusinessDate,l.overpaidon_date as overpaidOnDate, "
+ " l.is_charged_off as isChargedOff, l.charge_off_reason_cv_id as chargeOffReasonId, codec.code_value as chargeOffReason, l.charged_off_on_date as chargedOffOnDate, l.enable_down_payment as enableDownPayment, l.disbursed_amount_percentage_for_down_payment as disbursedAmountPercentageForDownPayment, l.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment," + " l.is_charged_off as isChargedOff, l.charge_off_reason_cv_id as chargeOffReasonId, codec.code_value as chargeOffReason, l.charged_off_on_date as chargedOffOnDate, l.enable_down_payment as enableDownPayment, l.disbursed_amount_percentage_for_down_payment as disbursedAmountPercentageForDownPayment, l.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment,"
+ " cobu.username as chargedOffByUsername, cobu.firstname as chargedOffByFirstname, cobu.lastname as chargedOffByLastname, l.loan_schedule_type as loanScheduleType, l.loan_schedule_processing_type as loanScheduleProcessingType, " + " cobu.username as chargedOffByUsername, cobu.firstname as chargedOffByFirstname, cobu.lastname as chargedOffByLastname, l.loan_schedule_type as loanScheduleType, l.loan_schedule_processing_type as loanScheduleProcessingType, "
+ " l.charge_off_behaviour as chargeOffBehaviour " // + " l.charge_off_behaviour as chargeOffBehaviour, l.interest_recognition_on_disbursement_date as interestRecognitionOnDisbursementDate " //
+ " from m_loan l" // + " from m_loan l" //
+ " join m_product_loan lp on lp.id = l.product_id" // + " join m_product_loan lp on lp.id = l.product_id" //
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id join m_currency rc on rc." + " left join m_loan_recalculation_details lir on lir.loan_id = l.id join m_currency rc on rc."
@@ -1068,6 +1068,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
final LoanScheduleProcessingType loanScheduleProcessingType = LoanScheduleProcessingType.valueOf(loanScheduleProcessingTypeStr); final LoanScheduleProcessingType loanScheduleProcessingType = LoanScheduleProcessingType.valueOf(loanScheduleProcessingTypeStr);
final Integer fixedLength = JdbcSupport.getInteger(rs, "fixedLength"); final Integer fixedLength = JdbcSupport.getInteger(rs, "fixedLength");
final LoanChargeOffBehaviour chargeOffBehaviour = LoanChargeOffBehaviour.valueOf(rs.getString("chargeOffBehaviour")); final LoanChargeOffBehaviour chargeOffBehaviour = LoanChargeOffBehaviour.valueOf(rs.getString("chargeOffBehaviour"));
final boolean interestRecognitionOnDisbursementDate = rs.getBoolean("interestRecognitionOnDisbursementDate");
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
@@ -1086,7 +1087,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud, fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud,
lastClosedBusinessDate, overpaidOnDate, isChargedOff, enableDownPayment, disbursedAmountPercentageForDownPayment, lastClosedBusinessDate, overpaidOnDate, isChargedOff, enableDownPayment, disbursedAmountPercentageForDownPayment,
enableAutoRepaymentForDownPayment, enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), enableAutoRepaymentForDownPayment, enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(),
loanScheduleProcessingType.asEnumOptionData(), fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData()); loanScheduleProcessingType.asEnumOptionData(), fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData(),
interestRecognitionOnDisbursementDate);
} }
} }
@@ -185,7 +185,7 @@ public final class LoanProductDataValidator {
LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.LOAN_SCHEDULE_TYPE, LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.LOAN_SCHEDULE_TYPE,
LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH, LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH,
LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES, LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES,
LoanProductConstants.CHARGE_OFF_BEHAVIOUR)); LoanProductConstants.CHARGE_OFF_BEHAVIOUR, LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE));
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName, private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName, LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
@@ -265,7 +265,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lpr.is_compounding_to_be_posted_as_transaction as isCompoundingToBePostedAsTransaction, " + "lpr.is_compounding_to_be_posted_as_transaction as isCompoundingToBePostedAsTransaction, "
+ "lpr.allow_compounding_on_eod as allowCompoundingOnEod, " + "lpr.allow_compounding_on_eod as allowCompoundingOnEod, "
+ "lpr.disallow_interest_calc_on_past_due as disallowInterestCalculationOnPastDue, " + "lpr.disallow_interest_calc_on_past_due as disallowInterestCalculationOnPastDue, "
+ "lp.hold_guarantee_funds as holdGuaranteeFunds, " + "lp.hold_guarantee_funds as holdGuaranteeFunds, lp.interest_recognition_on_disbursement_date as interestRecognitionOnDisbursementDate, "
+ "lp.principal_threshold_for_last_installment as principalThresholdForLastInstallment, " + "lp.principal_threshold_for_last_installment as principalThresholdForLastInstallment, "
+ "lp.fixed_principal_percentage_per_installment fixedPrincipalPercentagePerInstallment, " + "lp.fixed_principal_percentage_per_installment fixedPrincipalPercentagePerInstallment, "
+ "lp.sync_expected_with_disbursement_date as syncExpectedWithDisbursementDate, " + "lp.sync_expected_with_disbursement_date as syncExpectedWithDisbursementDate, "
@@ -547,6 +547,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
} }
final String chargeOffBehaviourStr = rs.getString("chargeOffBehaviour"); final String chargeOffBehaviourStr = rs.getString("chargeOffBehaviour");
final LoanChargeOffBehaviour loanChargeOffBehaviour = LoanChargeOffBehaviour.valueOf(chargeOffBehaviourStr); final LoanChargeOffBehaviour loanChargeOffBehaviour = LoanChargeOffBehaviour.valueOf(chargeOffBehaviourStr);
final boolean interestRecognitionOnDisbursementDate = rs.getBoolean("interestRecognitionOnDisbursementDate");
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -570,7 +571,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
enableAutoRepaymentForDownPayment, advancedPaymentData, creditAllocationData, repaymentStartDateType, enableAutoRepaymentForDownPayment, advancedPaymentData, creditAllocationData, repaymentStartDateType,
enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(), enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(),
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
loanChargeOffBehaviour.getValueAsStringEnumOptionData()); loanChargeOffBehaviour.getValueAsStringEnumOptionData(), interestRecognitionOnDisbursementDate);
} }
} }
@@ -98,7 +98,7 @@ public class DefaultScheduledDateGeneratorTest {
Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST, BigDecimal.valueOf(36_000L), null, Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST, BigDecimal.valueOf(36_000L), null,
DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO, null, NONE, null, ZERO, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO, null, NONE, null, ZERO,
EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, false, DISBURSEMENT_DATE, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, false, DISBURSEMENT_DATE,
submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null); submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null, false);
// when // when
List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(mathContext, expectedDisbursementDate, List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(mathContext, expectedDisbursementDate,
@@ -169,8 +169,8 @@ public class DefaultScheduledDateGeneratorTest {
dueRepaymentPeriodDate, null, null, null, null, null, Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, dueRepaymentPeriodDate, null, null, null, null, null, Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null,
EMPTY_LIST, BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, EMPTY_LIST, BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null,
null, ZERO, null, NONE, null, ZERO, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, ZERO, null, NONE, null, ZERO, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false,
null, false, DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null, false, DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null,
null); false);
} }
private HolidayDetailDTO createHolidayDTO() { private HolidayDetailDTO createHolidayDTO() {
@@ -41,7 +41,6 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.BusinessDateRequest; import org.apache.fineract.client.models.BusinessDateRequest;
import org.apache.fineract.client.models.CreditAllocationData; import org.apache.fineract.client.models.CreditAllocationData;
@@ -6036,16 +6035,6 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
} }
} }
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).collect(Collectors.toList());
}
private static AdvancedPaymentData createDefaultPaymentAllocationWithMixedGrouping() { private static AdvancedPaymentData createDefaultPaymentAllocationWithMixedGrouping() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData(); AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT"); advancedPaymentData.setTransactionType("DEFAULT");
@@ -6061,36 +6050,6 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
return advancedPaymentData; return advancedPaymentData;
} }
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule) {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType(transactionType);
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static Integer createLoanProduct(final String principal, final String repaymentAfterEvery, final String numberOfRepayments, private static Integer createLoanProduct(final String principal, final String repaymentAfterEvery, final String numberOfRepayments,
boolean autoPayForDownPayment, LoanScheduleType loanScheduleType, LoanScheduleProcessingType loanScheduleProcessingType, boolean autoPayForDownPayment, LoanScheduleType loanScheduleType, LoanScheduleProcessingType loanScheduleProcessingType,
AdvancedPaymentData allocationRuleData, final Account... accounts) { AdvancedPaymentData allocationRuleData, final Account... accounts) {
@@ -21,10 +21,7 @@ package org.apache.fineract.integrationtests;
import static org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY; import static org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
@@ -173,16 +170,6 @@ public class AdvancedPaymentAllocationWaiveLoanCharges extends BaseLoanIntegrati
return advancedPaymentData; return advancedPaymentData;
} }
private List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).collect(Collectors.toList());
}
protected Long createLoanProductWithAdvancedAllocation() { protected Long createLoanProductWithAdvancedAllocation() {
PostLoanProductsRequest req = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(); PostLoanProductsRequest req = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
req.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanScheduleType(LoanScheduleType.PROGRESSIVE.name()) req.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanScheduleType(LoanScheduleType.PROGRESSIVE.name())
@@ -495,7 +495,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
.rescheduleStrategyMethod(rescheduleStrategyMethod); .rescheduleStrategyMethod(rescheduleStrategyMethod);
} }
private List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) { protected static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1); AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> { return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder(); PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
@@ -1382,4 +1382,35 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
public static final String MERCHANT_ISSUED_REFUND = "MERCHANT_ISSUED_REFUND"; public static final String MERCHANT_ISSUED_REFUND = "MERCHANT_ISSUED_REFUND";
public static final String PAYOUT_REFUND = "PAYOUT_REFUND"; public static final String PAYOUT_REFUND = "PAYOUT_REFUND";
} }
protected static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
protected static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule) {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType(transactionType);
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
} }
@@ -31,11 +31,9 @@ import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream; import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
@@ -908,31 +906,6 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest extend
return advancedPaymentData; return advancedPaymentData;
} }
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).toList();
}
private static Stream<Arguments> loanProductFactory() { private static Stream<Arguments> loanProductFactory() {
return Stream.of(Arguments.of(Named.of("DEFAULT_STRATEGY", new LoanProductTestBuilder().withRepaymentStrategy(DEFAULT_STRATEGY))), return Stream.of(Arguments.of(Named.of("DEFAULT_STRATEGY", new LoanProductTestBuilder().withRepaymentStrategy(DEFAULT_STRATEGY))),
Arguments.of(Named.of("ADVANCED_PAYMENT_ALLOCATION_STRATEGY", Arguments.of(Named.of("ADVANCED_PAYMENT_ALLOCATION_STRATEGY",
@@ -31,10 +31,8 @@ import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream; import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
@@ -399,31 +397,6 @@ public class DelinquencyAndChargebackIntegrationTest extends BaseLoanIntegration
return advancedPaymentData; return advancedPaymentData;
} }
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).toList();
}
private static Stream<Arguments> loanProductFactory() { private static Stream<Arguments> loanProductFactory() {
return Stream.of(Arguments.of(Named.of("DEFAULT_STRATEGY", new LoanProductTestBuilder().withRepaymentStrategy(DEFAULT_STRATEGY))), return Stream.of(Arguments.of(Named.of("DEFAULT_STRATEGY", new LoanProductTestBuilder().withRepaymentStrategy(DEFAULT_STRATEGY))),
Arguments.of(Named.of("ADVANCED_PAYMENT_ALLOCATION_STRATEGY", Arguments.of(Named.of("ADVANCED_PAYMENT_ALLOCATION_STRATEGY",
@@ -0,0 +1,142 @@
/**
* 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.integrationtests;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoanProductsRequest;
import org.apache.fineract.client.models.PostLoanProductsResponse;
import org.apache.fineract.client.models.PostLoansRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.util.CallFailedRuntimeException;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InterestRecognitionFromDistbusementDateTest extends BaseLoanIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(InterestRecognitionFromDistbusementDateTest.class);
private static final String DATETIME_PATTERN = "dd MMMM yyyy";
private static ResponseSpecification responseSpec;
private static RequestSpecification requestSpec;
private static LoanTransactionHelper loanTransactionHelper;
private static Integer commonLoanProductId;
private static PostClientsResponse client;
@BeforeAll
public static void setup() {
Utils.initializeRESTAssured();
requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
requestSpec.header("Fineract-Platform-TenantId", "default");
responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
}
// UC1: Create Loan Product using Progressive Loan Schedule Type and interestChargedFromDisbursementDate flag
// 1. Create a Loan product with Adv. Pment. Alloc. (PROGRESSIVE) without interestChargedFromDisbursementDate
// 2. Create a Loan product with Adv. Pment. Alloc. (PROGRESSIVE) and interestChargedFromDisbursementDate
// 3. Create a Loan product with Cumulative Loan Schedule and interestChargedFromDisbursementDate
@Test
public void uc1() {
final String operationDate = "1 January 2025";
runAt(operationDate, () -> {
// Create a Loan Product Adv. Pment. Alloc. (PROGRESSIVE) withou interestChargedFromDisbursementDate
LOG.info("Create a Loan Product Adv. Pment. Alloc. (PROGRESSIVE) not using interestChargedFromDisbursementDate flag");
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
.numberOfRepayments(6);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
GetLoanProductsProductIdResponse loanProductData = loanProductHelper
.retrieveLoanProductById(loanProductResponse.getResourceId());
assertEquals(Boolean.FALSE, loanProductData.getInterestRecognitionOnDisbursementDate());
// Create a Loan Product Adv. Pment. Alloc. (PROGRESSIVE) using interestChargedFromDisbursementDate in true
LOG.info("Create a Loan Product Adv. Pment. Alloc. (PROGRESSIVE) using interestChargedFromDisbursementDate flag");
product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation().numberOfRepayments(6)
.interestRecognitionOnDisbursementDate(true);
loanProductResponse = loanProductHelper.createLoanProduct(product);
loanProductData = loanProductHelper.retrieveLoanProductById(loanProductResponse.getResourceId());
assertEquals(Boolean.TRUE, loanProductData.getInterestRecognitionOnDisbursementDate());
// Try to create a Loan Product (CUMULATIVE) using interestChargedFromDisbursementDate in true
LOG.info("Try to create a Loan Product (CUMULATIVE) using interestChargedFromDisbursementDate flag");
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class, () -> loanProductHelper.createLoanProduct(
createOnePeriod30DaysPeriodicAccrualProduct(8.0).numberOfRepayments(6).interestRecognitionOnDisbursementDate(true)));
assertEquals(403, exception.getResponse().code());
assertTrue(exception.getMessage()
.contains("interestRecognitionOnDisbursementDate.is.only.supported.for.progressive.loan.schedule.type"));
});
}
// UC2: Create Loan Product using Progressive Loan Schedule Type and interestChargedFromDisbursementDate flag
// 1. Create a Loan product with Adv. Pment. Alloc. (PROGRESSIVE) and interestChargedFromDisbursementDate
// 2. Create a Loan account and inherit the interestChargedFromDisbursementDate flag
// 3. Create a Loan account and override the interestChargedFromDisbursementDate flag
@Test
public void uc2() {
final String operationDate = "1 January 2025";
runAt(operationDate, () -> {
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
.numberOfRepayments(6).interestRecognitionOnDisbursementDate(true);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
GetLoanProductsProductIdResponse loanProductData = loanProductHelper
.retrieveLoanProductById(loanProductResponse.getResourceId());
client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
PostLoansRequest applicationRequest = applyLoanRequest(client.getClientId(), loanProductResponse.getResourceId(), operationDate,
100.0, 4).transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY);
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
// Create a Loan account and inherit the interestChargedFromDisbursementDate flag
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
assertEquals(loanProductData.getInterestRecognitionOnDisbursementDate(),
loanDetails.getInterestRecognitionOnDisbursementDate());
// Create a Loan account and override the interestChargedFromDisbursementDate flag
applicationRequest = applyLoanRequest(client.getClientId(), loanProductResponse.getResourceId(), operationDate, 100.0, 4)
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)
.interestRecognitionOnDisbursementDate(false);
loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
// Create a Loan account and inherit the interestChargedFromDisbursementDate flag
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
assertNotEquals(loanProductData.getInterestRecognitionOnDisbursementDate(),
loanDetails.getInterestRecognitionOnDisbursementDate());
});
}
}
@@ -31,15 +31,12 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.BusinessDateRequest; import org.apache.fineract.client.models.BusinessDateRequest;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PaymentAllocationOrder;
import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdRequest;
import org.apache.fineract.client.models.PostLoansRequest; import org.apache.fineract.client.models.PostLoansRequest;
@@ -66,7 +63,6 @@ import org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
@@ -334,44 +330,4 @@ public class LoanChargePaymentWithAdvancedPaymentAllocationTest extends BaseLoan
collateral.put("quantity", quantity.toString()); collateral.put("quantity", quantity.toString());
return collateral; return collateral;
} }
private static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule) {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType(transactionType);
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).toList();
}
} }
@@ -1824,21 +1824,6 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
.creditAllocation(List.of(creditAllocationData)); .creditAllocation(List.of(creditAllocationData));
} }
private AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private AdvancedPaymentData createDefaultPaymentAllocationPrincipalFirst() { private AdvancedPaymentData createDefaultPaymentAllocationPrincipalFirst() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData(); AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT"); advancedPaymentData.setTransactionType("DEFAULT");
@@ -1886,14 +1871,4 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
}).toList(); }).toList();
} }
private List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).toList();
}
} }
@@ -21,9 +21,7 @@ package org.apache.fineract.integrationtests;
import static org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY; import static org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
@@ -130,28 +128,4 @@ public class LoanInterestRateFrequencyTest extends BaseLoanIntegrationTest {
return advancedPaymentData; return advancedPaymentData;
} }
private AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).toList();
}
} }
@@ -26,12 +26,7 @@ import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.PaymentAllocationOrder;
import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoanProductsResponse; import org.apache.fineract.client.models.PostLoanProductsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdRequest;
@@ -50,7 +45,6 @@ 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.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -282,44 +276,4 @@ public class LoanOriginationValidationTest extends BaseLoanIntegrationTest {
return loanTransactionHelper.getLoanProductId(loanProductJSON); return loanTransactionHelper.getLoanProductId(loanProductJSON);
} }
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule) {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType(transactionType);
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).collect(Collectors.toList());
}
} }
@@ -30,12 +30,9 @@ import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.AllowAttributeOverrides; import org.apache.fineract.client.models.AllowAttributeOverrides;
import org.apache.fineract.client.models.ChargeData; import org.apache.fineract.client.models.ChargeData;
@@ -44,7 +41,6 @@ import org.apache.fineract.client.models.GetLoanFeeToIncomeAccountMappings;
import org.apache.fineract.client.models.GetLoanPaymentChannelToFundSourceMappings; import org.apache.fineract.client.models.GetLoanPaymentChannelToFundSourceMappings;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdStatus; import org.apache.fineract.client.models.GetLoansLoanIdStatus;
import org.apache.fineract.client.models.PaymentAllocationOrder;
import org.apache.fineract.client.models.PostChargesRequest; import org.apache.fineract.client.models.PostChargesRequest;
import org.apache.fineract.client.models.PostChargesResponse; import org.apache.fineract.client.models.PostChargesResponse;
import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostClientsResponse;
@@ -67,7 +63,6 @@ import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode; import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -1675,31 +1670,6 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
return resourceId; return resourceId;
} }
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).collect(Collectors.toList());
}
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID, private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID,
BigDecimal principal, String applicationDisbursementDate) { BigDecimal principal, String applicationDisbursementDate) {
final PostLoansRequest loanRequest = new PostLoansRequest() // final PostLoansRequest loanRequest = new PostLoansRequest() //
@@ -31,10 +31,8 @@ import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream; import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
@@ -690,31 +688,6 @@ public class LoanTransactionChargebackTest extends BaseLoanIntegrationTest {
return advancedPaymentData; return advancedPaymentData;
} }
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).toList();
}
private static Stream<Arguments> loanProductFactory() { private static Stream<Arguments> loanProductFactory() {
return Stream.of(Arguments.of(Named.of("DEFAULT_STRATEGY", new LoanProductTestBuilder().withRepaymentStrategy(DEFAULT_STRATEGY))), return Stream.of(Arguments.of(Named.of("DEFAULT_STRATEGY", new LoanProductTestBuilder().withRepaymentStrategy(DEFAULT_STRATEGY))),
Arguments.of(Named.of("ADVANCED_PAYMENT_ALLOCATION_STRATEGY", Arguments.of(Named.of("ADVANCED_PAYMENT_ALLOCATION_STRATEGY",
@@ -33,12 +33,9 @@ import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy; import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest; import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse; import org.apache.fineract.batch.domain.BatchResponse;
@@ -47,7 +44,6 @@ import org.apache.fineract.client.models.BusinessDateRequest;
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData; import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
import org.apache.fineract.client.models.PaymentAllocationOrder;
import org.apache.fineract.client.models.PostChargesRequest; import org.apache.fineract.client.models.PostChargesRequest;
import org.apache.fineract.client.models.PostChargesResponse; import org.apache.fineract.client.models.PostChargesResponse;
import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostClientsResponse;
@@ -80,7 +76,6 @@ import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
@@ -1720,46 +1715,6 @@ public class LoanTransactionInterestPaymentWaiverTest extends BaseLoanIntegratio
return loanTransactionHelper.getLoanProductId(loanProductJSON); return loanTransactionHelper.getLoanProductId(loanProductJSON);
} }
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).collect(Collectors.toList());
}
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule) {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType(transactionType);
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal, private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate, final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
final String expectedDisbursementDate, final String submittedOnDate, String transactionProcessorCode, final String expectedDisbursementDate, final String submittedOnDate, String transactionProcessorCode,
@@ -29,15 +29,11 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.BusinessDateRequest; import org.apache.fineract.client.models.BusinessDateRequest;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod; import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PaymentAllocationOrder;
import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdRequest;
import org.apache.fineract.client.models.PostLoansRequest; import org.apache.fineract.client.models.PostLoansRequest;
@@ -56,7 +52,6 @@ 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.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -505,31 +500,6 @@ public class RefundForActiveLoansWithAdvancedPaymentAllocationTest extends BaseL
return loanTransactionHelper.getLoanProductId(loanProductJSON); return loanTransactionHelper.getLoanProductId(loanProductJSON);
} }
private static AdvancedPaymentData createDefaultPaymentAllocation() {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType("DEFAULT");
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_INTEREST, PaymentAllocationType.PAST_DUE_PRINCIPAL,
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_INTEREST,
PaymentAllocationType.DUE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
PaymentAllocationType.IN_ADVANCE_INTEREST, PaymentAllocationType.IN_ADVANCE_PRINCIPAL);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
AtomicInteger integer = new AtomicInteger(1);
return Arrays.stream(paymentAllocationTypes).map(pat -> {
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
paymentAllocationOrder.setOrder(integer.getAndIncrement());
return paymentAllocationOrder;
}).toList();
}
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final Long principal, private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final Long principal,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate, final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
final String expectedDisbursementDate, final String submittedOnDate) { final String expectedDisbursementDate, final String submittedOnDate) {
@@ -165,6 +165,7 @@ public class LoanProductTestBuilder {
private FullAccountingConfig fullAccountingConfig; private FullAccountingConfig fullAccountingConfig;
private List<String> supportedInterestRefundTypes = null; private List<String> supportedInterestRefundTypes = null;
private String chargeOffBehaviour; private String chargeOffBehaviour;
private boolean interestRecognitionOnDisbursementDate = false;
public String build() { public String build() {
final HashMap<String, Object> map = build(null, null); final HashMap<String, Object> map = build(null, null);
@@ -324,6 +325,9 @@ public class LoanProductTestBuilder {
if (enableAutoRepaymentForDownPayment) { if (enableAutoRepaymentForDownPayment) {
map.put("enableAutoRepaymentForDownPayment", enableAutoRepaymentForDownPayment); map.put("enableAutoRepaymentForDownPayment", enableAutoRepaymentForDownPayment);
} }
if (interestRecognitionOnDisbursementDate) {
map.put("interestRecognitionOnDisbursementDate", interestRecognitionOnDisbursementDate);
}
if (this.repaymentStartDateType != null) { if (this.repaymentStartDateType != null) {
map.put("repaymentStartDateType", repaymentStartDateType); map.put("repaymentStartDateType", repaymentStartDateType);