FINERACT-1785: Allow to add charge (penalty) with same disbursement date
This commit is contained in:
committed by
Arnold Gálovics
parent
e938104b84
commit
a987956cbc
-11
@@ -632,17 +632,6 @@ public class LoanCharge extends AbstractPersistableCustom {
|
|||||||
return target != null && target.isAfter(fromNotInclusive) && !target.isAfter(upToAndInclusive);
|
return target != null && target.isAfter(fromNotInclusive) && !target.isAfter(upToAndInclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDueForCollectionFromIncludingAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) {
|
|
||||||
final LocalDate dueDate = getDueLocalDate();
|
|
||||||
return occursOnDayFromAndIncludingAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, dueDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean occursOnDayFromAndIncludingAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive,
|
|
||||||
final LocalDate target) {
|
|
||||||
return target != null && (target.isEqual(fromAndInclusive) || target.isAfter(fromAndInclusive))
|
|
||||||
&& !target.isAfter(upToAndInclusive);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFeeCharge() {
|
public boolean isFeeCharge() {
|
||||||
return !this.penaltyCharge;
|
return !this.penaltyCharge;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -119,8 +119,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||||
cumulative = cumulative.plus(loanCharge.amount());
|
cumulative = cumulative.plus(loanCharge.amount());
|
||||||
// Special case for Loan Charges (Due Date) added the same disbursement date
|
// Special case for Loan Charges (Due Date) added the same disbursement date
|
||||||
} else if (period.isFirstPeriod()
|
} else if (period.isFirstPeriod() && periodStart.equals(loanCharge.getDueDate())) {
|
||||||
&& loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)) {
|
|
||||||
cumulative = cumulative.plus(loanCharge.amount());
|
cumulative = cumulative.plus(loanCharge.amount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,6 +212,9 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
cumulative = cumulative.plus(loanChargeAmt);
|
cumulative = cumulative.plus(loanChargeAmt);
|
||||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||||
cumulative = cumulative.plus(loanCharge.amount());
|
cumulative = cumulative.plus(loanCharge.amount());
|
||||||
|
// Special case for Loan Charges (Due Date) added the same disbursement date
|
||||||
|
} else if (period.isFirstPeriod() && periodStart.equals(loanCharge.getDueDate())) {
|
||||||
|
cumulative = cumulative.plus(loanCharge.amount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -21,13 +21,11 @@ package org.apache.fineract.portfolio.loanaccount.domain;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LoanRepaymentScheduleTransactionProcessorFactory {
|
public class LoanRepaymentScheduleTransactionProcessorFactory {
|
||||||
|
|
||||||
|
|||||||
+65
-6
@@ -62,7 +62,7 @@ public class LoanChargeSpecificDueDateTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testApplyLoanSpecificDueDateChargeWithDisbursementDate() {
|
public void testApplyLoanSpecificDueDateFeeWithDisbursementDate() {
|
||||||
|
|
||||||
final LocalDate todaysDate = Utils.getLocalDateOfTenant();
|
final LocalDate todaysDate = Utils.getLocalDateOfTenant();
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ public class LoanChargeSpecificDueDateTest {
|
|||||||
|
|
||||||
// Get loan details
|
// Get loan details
|
||||||
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||||
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("0.00"));
|
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("0.00"), false);
|
||||||
|
|
||||||
// Apply Loan Charge with specific due date
|
// Apply Loan Charge with specific due date
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ public class LoanChargeSpecificDueDateTest {
|
|||||||
|
|
||||||
// Get loan details expecting to have a delinquency classification
|
// Get loan details expecting to have a delinquency classification
|
||||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||||
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("10.00"));
|
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("10.00"), false);
|
||||||
|
|
||||||
// Make a full repayment to close the Loan
|
// Make a full repayment to close the Loan
|
||||||
Float amount = Float.valueOf("1010.00");
|
Float amount = Float.valueOf("1010.00");
|
||||||
@@ -110,7 +110,62 @@ public class LoanChargeSpecificDueDateTest {
|
|||||||
|
|
||||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||||
assertNotNull(getLoansLoanIdResponse);
|
assertNotNull(getLoansLoanIdResponse);
|
||||||
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf("0.00"), Double.valueOf("0.00"));
|
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf("0.00"), Double.valueOf("0.00"), false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApplyLoanSpecificDueDatePenaltyWithDisbursementDate() {
|
||||||
|
|
||||||
|
final LocalDate todaysDate = Utils.getLocalDateOfTenant();
|
||||||
|
|
||||||
|
// Client and Loan account creation
|
||||||
|
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
|
||||||
|
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, null);
|
||||||
|
assertNotNull(getLoanProductsProductResponse);
|
||||||
|
|
||||||
|
// Older date to have more than one overdue installment
|
||||||
|
LocalDate transactionDate = todaysDate;
|
||||||
|
String operationDate = Utils.dateFormatter.format(transactionDate);
|
||||||
|
log.info("Operation date {}", transactionDate);
|
||||||
|
|
||||||
|
// Create Loan Account
|
||||||
|
final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
|
||||||
|
getLoanProductsProductResponse.getId().toString(), operationDate);
|
||||||
|
|
||||||
|
// Get loan details
|
||||||
|
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||||
|
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("0.00"), true);
|
||||||
|
|
||||||
|
// Apply Loan Charge with specific due date
|
||||||
|
|
||||||
|
final String feeAmount = "10.00";
|
||||||
|
String payloadJSON = ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, feeAmount, true);
|
||||||
|
final PostChargesResponse postChargesResponse = ChargesHelper.createLoanCharge(requestSpec, responseSpec, payloadJSON);
|
||||||
|
assertNotNull(postChargesResponse);
|
||||||
|
final Long loanChargeId = postChargesResponse.getResourceId();
|
||||||
|
assertNotNull(loanChargeId);
|
||||||
|
|
||||||
|
payloadJSON = LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(loanChargeId.toString(), operationDate, feeAmount);
|
||||||
|
PostLoansLoanIdChargesResponse postLoansLoanIdChargesResponse = loanTransactionHelper.addChargeForLoan(loanId, payloadJSON,
|
||||||
|
responseSpec);
|
||||||
|
assertNotNull(postLoansLoanIdChargesResponse);
|
||||||
|
|
||||||
|
// Get loan details expecting to have a delinquency classification
|
||||||
|
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||||
|
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("10.00"), true);
|
||||||
|
|
||||||
|
// Make a full repayment to close the Loan
|
||||||
|
Float amount = Float.valueOf("1010.00");
|
||||||
|
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||||
|
loanId);
|
||||||
|
assertNotNull(loanIdTransactionsResponse);
|
||||||
|
log.info("Loan Transaction Id: {} {}", loanId, loanIdTransactionsResponse.getResourceId());
|
||||||
|
|
||||||
|
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||||
|
assertNotNull(getLoansLoanIdResponse);
|
||||||
|
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf("0.00"), Double.valueOf("0.00"), true);
|
||||||
|
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.closed.obligations.met");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +192,15 @@ public class LoanChargeSpecificDueDateTest {
|
|||||||
return loanId;
|
return loanId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateLoanAccount(GetLoansLoanIdResponse getLoansLoanIdResponse, Double principal, Double fees) {
|
private void validateLoanAccount(GetLoansLoanIdResponse getLoansLoanIdResponse, Double principal, Double fees, boolean isPenalty) {
|
||||||
assertNotNull(getLoansLoanIdResponse);
|
assertNotNull(getLoansLoanIdResponse);
|
||||||
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
|
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
|
||||||
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, principal);
|
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, principal);
|
||||||
loanTransactionHelper.validateLoanFeesOustandingBalance(getLoansLoanIdResponse, fees);
|
if (isPenalty) {
|
||||||
|
loanTransactionHelper.validateLoanPenaltiesOustandingBalance(getLoansLoanIdResponse, fees);
|
||||||
|
} else {
|
||||||
|
loanTransactionHelper.validateLoanFeesOustandingBalance(getLoansLoanIdResponse, fees);
|
||||||
|
}
|
||||||
loanTransactionHelper.validateLoanTotalOustandingBalance(getLoansLoanIdResponse, (principal + fees));
|
loanTransactionHelper.validateLoanTotalOustandingBalance(getLoansLoanIdResponse, (principal + fees));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
@@ -1563,6 +1563,13 @@ public class LoanTransactionHelper extends IntegrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validateLoanPenaltiesOustandingBalance(GetLoansLoanIdResponse getLoansLoanIdResponse, Double amountExpected) {
|
||||||
|
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
||||||
|
assertNotNull(getLoansLoanIdSummary);
|
||||||
|
log.info("Loan with Fees Outstanding Balance {} expected {}", getLoansLoanIdSummary.getFeeChargesOutstanding(), amountExpected);
|
||||||
|
assertEquals(amountExpected, getLoansLoanIdSummary.getPenaltyChargesOutstanding());
|
||||||
|
}
|
||||||
|
|
||||||
public void validateLoanTotalOustandingBalance(GetLoansLoanIdResponse getLoansLoanIdResponse, Double amountExpected) {
|
public void validateLoanTotalOustandingBalance(GetLoansLoanIdResponse getLoansLoanIdResponse, Double amountExpected) {
|
||||||
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
||||||
if (getLoansLoanIdSummary != null) {
|
if (getLoansLoanIdSummary != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user