FINERACT-1724: Add final accrual when loan is overpaid

This commit is contained in:
Adam Saghy
2023-04-21 14:20:03 +02:00
committed by Arnold Gálovics
parent 0e3ad8f73b
commit cefcb53424
8 changed files with 131 additions and 44 deletions
@@ -610,17 +610,25 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
assertEquals(100.0, response.getTotalOverpaid());
assertTrue(response.getStatus().getOverpaid());
assertEquals(secondRepaymentId, response.getTransactions().get(2).getId().intValue());
assertNull(response.getTransactions().get(2).getReversedOnDate());
assertTrue(response.getTransactions().get(2).getTransactionRelations().isEmpty());
assertTrue(response.getTransactions().get(2).getType().getRepayment());
assertEquals(650.0, response.getTransactions().get(2).getAmount());
assertEquals(550.0, response.getTransactions().get(2).getPrincipalPortion());
assertEquals(0.0, response.getTransactions().get(2).getPenaltyChargesPortion());
assertEquals(100.0, response.getTransactions().get(2).getOverpaymentPortion());
assertEquals(0.0, response.getTransactions().get(2).getInterestPortion());
assertEquals(0.0, response.getTransactions().get(2).getFeeChargesPortion());
assertEquals(0.0, response.getTransactions().get(2).getOutstandingLoanBalance());
int secondRepaymentIndex;
// The repayment and accrual order is not consistent
if (response.getTransactions().get(2).getType().getRepayment()) {
secondRepaymentIndex = 2;
} else {
secondRepaymentIndex = 3;
}
assertEquals(secondRepaymentId, response.getTransactions().get(secondRepaymentIndex).getId().intValue());
assertNull(response.getTransactions().get(secondRepaymentIndex).getReversedOnDate());
assertTrue(response.getTransactions().get(secondRepaymentIndex).getTransactionRelations().isEmpty());
assertTrue(response.getTransactions().get(secondRepaymentIndex).getType().getRepayment());
assertEquals(650.0, response.getTransactions().get(secondRepaymentIndex).getAmount());
assertEquals(550.0, response.getTransactions().get(secondRepaymentIndex).getPrincipalPortion());
assertEquals(0.0, response.getTransactions().get(secondRepaymentIndex).getPenaltyChargesPortion());
assertEquals(100.0, response.getTransactions().get(secondRepaymentIndex).getOverpaymentPortion());
assertEquals(0.0, response.getTransactions().get(secondRepaymentIndex).getInterestPortion());
assertEquals(0.0, response.getTransactions().get(secondRepaymentIndex).getFeeChargesPortion());
assertEquals(0.0, response.getTransactions().get(secondRepaymentIndex).getOutstandingLoanBalance());
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
@@ -30,8 +30,6 @@ import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.HashMap;
import java.util.UUID;
import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
@@ -57,12 +55,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
public class LoanAccountOverpaidDateStatusTest {
private ResponseSpecification responseSpec;
private ResponseSpecification responseSpecErr400;
private ResponseSpecification responseSpecErr503;
private RequestSpecification requestSpec;
private ClientHelper clientHelper;
private LoanTransactionHelper loanTransactionHelper;
private DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
@BeforeEach
public void setup() {
@@ -70,8 +65,6 @@ public class LoanAccountOverpaidDateStatusTest {
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.responseSpecErr400 = new ResponseSpecBuilder().expectStatusCode(400).build();
this.responseSpecErr503 = new ResponseSpecBuilder().expectStatusCode(503).build();
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
this.clientHelper = new ClientHelper(this.requestSpec, this.responseSpec);
}
@@ -124,7 +117,7 @@ public class LoanAccountOverpaidDateStatusTest {
GetLoansLoanIdResponse loanDetailsOverpaid = loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetailsOverpaid.getStatus().getOverpaid());
assertNotNull(loanDetailsOverpaid.getOverpaidOnDate());
assertEquals(loanDetailsOverpaid.getOverpaidOnDate(), todaysDate);
assertEquals(loanDetailsOverpaid.getOverpaidOnDate(), LocalDate.of(2022, 9, 9));
// reverse repayment to make loan not overpaid and overpaid date is reset
loanTransactionHelper.reverseRepayment(loanId, repaymentTransaction_4.getResourceId().intValue(), "10 September 2022");
@@ -141,7 +134,7 @@ public class LoanAccountOverpaidDateStatusTest {
GetLoansLoanIdResponse loanDetailsOverpaid_1 = loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetailsOverpaid_1.getStatus().getOverpaid());
assertNotNull(loanDetailsOverpaid_1.getOverpaidOnDate());
assertEquals(loanDetailsOverpaid_1.getOverpaidOnDate(), todaysDate);
assertEquals(loanDetailsOverpaid_1.getOverpaidOnDate(), LocalDate.of(2022, 9, 11));
// Credit balance refund to reset overpaid status
loanTransactionHelper.creditBalanceRefund("12 September 2022", Float.valueOf(100), null, loanId, "");
@@ -164,7 +157,7 @@ public class LoanAccountOverpaidDateStatusTest {
GetLoansLoanIdResponse loanDetailsOverpaid_3 = loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetailsOverpaid_3.getStatus().getOverpaid());
assertNotNull(loanDetailsOverpaid_3.getOverpaidOnDate());
assertEquals(loanDetailsOverpaid_3.getOverpaidOnDate(), todaysDate);
assertEquals(loanDetailsOverpaid_3.getOverpaidOnDate(), LocalDate.of(2022, 9, 14));
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
@@ -427,6 +427,84 @@ public class LoanChargeSpecificDueDateTest {
}
}
@Test
public void testApplyFeeAccrualWhenLoanOverpaid() {
try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
final LocalDate todaysDate = Utils.getLocalDateOfTenant();
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, todaysDate);
// Client and Loan account creation
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProductWithPeriodicAccrual(
loanTransactionHelper, null);
assertNotNull(getLoanProductsProductResponse);
LocalDate transactionDate = LocalDate.of(Utils.getLocalDateOfTenant().getYear(), 1, 1);
String operationDate = Utils.dateFormatter.format(transactionDate);
log.info("Disbursement date {}", transactionDate);
// Create Loan Account
final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
getLoanProductsProductResponse.getId().toString(), operationDate, "1", "0");
// 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
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 chargeId = postChargesResponse.getResourceId();
assertNotNull(chargeId);
// First Loan Charge
transactionDate = transactionDate.plusDays(1);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, transactionDate);
operationDate = Utils.dateFormatter.format(transactionDate);
log.info("Operation date {}", transactionDate);
payloadJSON = LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(chargeId.toString(), operationDate, feeAmount);
PostLoansLoanIdChargesResponse postLoansLoanIdChargesResponse = loanTransactionHelper.addChargeForLoan(loanId, payloadJSON,
responseSpec);
assertNotNull(postLoansLoanIdChargesResponse);
final Long loanChargeId01 = postLoansLoanIdChargesResponse.getResourceId();
assertNotNull(loanChargeId01);
Float amount = Float.valueOf("1020.00");
transactionDate = transactionDate.plusDays(2);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, transactionDate);
operationDate = Utils.dateFormatter.format(transactionDate);
log.info("Operation date {}", transactionDate);
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);
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.overpaid");
final Long transactionId = loanTransactionHelper.evaluateLastLoanTransactionData(getLoansLoanIdResponse,
"loanTransactionType.accrual", operationDate, Double.valueOf("10.00"));
assertNotNull(transactionId);
log.info("transactionId {}", transactionId);
final GetJournalEntriesTransactionIdResponse journalEntriesResponse = journalEntryHelper.getJournalEntries("L" + transactionId);
assertNotNull(journalEntriesResponse);
final List<JournalEntryTransactionItem> journalEntries = journalEntriesResponse.getPageItems();
assertEquals(2, journalEntries.size());
assertEquals(10, journalEntries.get(0).getAmount());
assertEquals(10, journalEntries.get(1).getAmount());
assertEquals(transactionDate, journalEntries.get(0).getTransactionDate());
assertEquals(transactionDate, journalEntries.get(1).getTransactionDate());
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
}
private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
final Integer delinquencyBucketId) {
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId);