FINERACT-2279: Contract Termination - Undo
This commit is contained in:
committed by
Adam Saghy
parent
11cecb0d45
commit
ee29ce23ac
+9
@@ -3849,4 +3849,13 @@ public class CommandWrapperBuilder {
|
|||||||
this.href = "/loans/" + loanId;
|
this.href = "/loans/" + loanId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder undoContractTermination(final Long loanId) {
|
||||||
|
this.actionName = "CONTRACT_TERMINATION_UNDO";
|
||||||
|
this.entityName = "LOAN";
|
||||||
|
this.entityId = loanId;
|
||||||
|
this.loanId = loanId;
|
||||||
|
this.href = "/loans/" + loanId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -305,6 +305,10 @@ public class LoanRequestFactory {
|
|||||||
.locale(DEFAULT_LOCALE).note("Capitalized Income");
|
.locale(DEFAULT_LOCALE).note("Capitalized Income");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PostLoansLoanIdRequest defaultContractTerminationUndoRequest() {
|
||||||
|
return new PostLoansLoanIdRequest().note("Contract Termination Undo");
|
||||||
|
}
|
||||||
|
|
||||||
public static PostLoansLoanIdRequest defaultLoanContractTerminationRequest() {
|
public static PostLoansLoanIdRequest defaultLoanContractTerminationRequest() {
|
||||||
return new PostLoansLoanIdRequest().dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE).note("Contract Termination");
|
return new PostLoansLoanIdRequest().dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE).note("Contract Termination");
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -79,6 +79,7 @@ import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransaction
|
|||||||
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionMakeRepaymentPostEvent;
|
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionMakeRepaymentPostEvent;
|
||||||
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionMerchantIssuedRefundPostEvent;
|
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionMerchantIssuedRefundPostEvent;
|
||||||
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionPayoutRefundPostEvent;
|
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionPayoutRefundPostEvent;
|
||||||
|
import org.apache.fineract.test.messaging.event.loan.transaction.LoanUndoContractTerminationBusinessEvent;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
@@ -273,6 +274,10 @@ public class EventCheckHelper {
|
|||||||
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
|
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void loanUndoContractTerminationEventCheck(final GetLoansLoanIdTransactions transaction) {
|
||||||
|
eventAssertion.assertEventRaised(LoanUndoContractTerminationBusinessEvent.class, transaction.getId());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean areBigDecimalValuesEqual(BigDecimal actual, BigDecimal expected) {
|
private boolean areBigDecimalValuesEqual(BigDecimal actual, BigDecimal expected) {
|
||||||
log.debug("--- Checking BigDecimal values.... ---");
|
log.debug("--- Checking BigDecimal values.... ---");
|
||||||
log.debug("Actual: {}", actual);
|
log.debug("Actual: {}", actual);
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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.test.messaging.event.loan.transaction;
|
||||||
|
|
||||||
|
public class LoanUndoContractTerminationBusinessEvent extends AbstractLoanTransactionEvent {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventName() {
|
||||||
|
return "LoanUndoContractTerminationBusinessEvent";
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -4471,6 +4471,35 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
.isEqualTo(transactionId);
|
.isEqualTo(transactionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@And("Admin successfully undoes loan contract termination")
|
||||||
|
public void undoLoanContractTermination() throws IOException {
|
||||||
|
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||||
|
assert loanResponse.body() != null;
|
||||||
|
final Response<PostLoansLoanIdResponse> loanContractTerminationResponse = testContext()
|
||||||
|
.get(TestContextKey.LOAN_CONTRACT_TERMINATION_RESPONSE);
|
||||||
|
assert loanContractTerminationResponse.body() != null;
|
||||||
|
final Long loanId = loanResponse.body().getLoanId();
|
||||||
|
|
||||||
|
final List<GetLoansLoanIdTransactions> transactions = Objects
|
||||||
|
.requireNonNull(loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()).getTransactions();
|
||||||
|
|
||||||
|
assert transactions != null;
|
||||||
|
final GetLoansLoanIdTransactions targetTransaction = transactions.stream().filter(t -> {
|
||||||
|
assert t.getType() != null;
|
||||||
|
return Boolean.TRUE.equals(t.getType().getContractTermination());
|
||||||
|
}).findFirst().orElse(null);
|
||||||
|
|
||||||
|
final PostLoansLoanIdRequest request = LoanRequestFactory.defaultContractTerminationUndoRequest();
|
||||||
|
|
||||||
|
final Response<PostLoansLoanIdResponse> response = loansApi.stateTransitions(loanId, request, "undoContractTermination").execute();
|
||||||
|
testContext().set(TestContextKey.LOAN_UNDO_CONTRACT_TERMINATION_RESPONSE, response);
|
||||||
|
ErrorHelper.checkSuccessfulApiCall(response);
|
||||||
|
assert targetTransaction != null;
|
||||||
|
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
|
||||||
|
eventCheckHelper.loanUndoContractTerminationEventCheck(targetTransaction);
|
||||||
|
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
|
||||||
|
}
|
||||||
|
|
||||||
@Then("LoanTransactionContractTerminationPostBusinessEvent is raised on {string}")
|
@Then("LoanTransactionContractTerminationPostBusinessEvent is raised on {string}")
|
||||||
public void checkLoanTransactionContractTerminationPostBusinessEvent(final String date) throws IOException {
|
public void checkLoanTransactionContractTerminationPostBusinessEvent(final String date) throws IOException {
|
||||||
final Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
final Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||||
|
|||||||
+1
@@ -228,4 +228,5 @@ public abstract class TestContextKey {
|
|||||||
public static final String LOAN_CAPITALIZED_INCOME_ADJUSTMENT_RESPONSE = "loanCapitalizedIncomeAdjustmentResponse";
|
public static final String LOAN_CAPITALIZED_INCOME_ADJUSTMENT_RESPONSE = "loanCapitalizedIncomeAdjustmentResponse";
|
||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationContractTermination";
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationContractTermination";
|
||||||
public static final String LOAN_CONTRACT_TERMINATION_RESPONSE = "loanContractTerminationResponse";
|
public static final String LOAN_CONTRACT_TERMINATION_RESPONSE = "loanContractTerminationResponse";
|
||||||
|
public static final String LOAN_UNDO_CONTRACT_TERMINATION_RESPONSE = "loanUndoContractTerminationResponse";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1048,3 +1048,81 @@ Feature: Contract Termination
|
|||||||
| 31 March 2024 | Accrual Adjustment | 0.57 | 0.0 | 0.57 | 0.0 | 0.0 | 0.0 | false | false |
|
| 31 March 2024 | Accrual Adjustment | 0.57 | 0.0 | 0.57 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 31 March 2024 | Contract Termination | 31.15 | 31.07 | 0.08 | 0.0 | 0.0 | 0.0 | false | true |
|
| 31 March 2024 | Contract Termination | 31.15 | 31.07 | 0.08 | 0.0 | 0.0 | 0.0 | false | true |
|
||||||
And Global configuration "is-principal-compounding-disabled-for-overdue-loans" is disabled
|
And Global configuration "is-principal-compounding-disabled-for-overdue-loans" is disabled
|
||||||
|
|
||||||
|
@TestRailId:C3738
|
||||||
|
Scenario: Verify contract termination undo when interest recalculation is enabled - case when contract termination occurs on due date
|
||||||
|
When Admin sets the business date to "01 January 2024"
|
||||||
|
When Admin creates a client with random data
|
||||||
|
When Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024"
|
||||||
|
When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount
|
||||||
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||||
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
|
||||||
|
Then Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||||
|
When Admin sets the business date to "01 February 2024"
|
||||||
|
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 EUR transaction amount
|
||||||
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||||
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 17.01 | 0.0 | 0.0 | 85.04 |
|
||||||
|
Then Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||||
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||||
|
When Admin sets the business date to "1 March 2024"
|
||||||
|
And Admin successfully terminates loan contract
|
||||||
|
Then Loan Repayment schedule has 2 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 29 | 01 March 2024 | | 0.0 | 83.57 | 0.49 | 0.0 | 0.0 | 84.06 | 0.0 | 0.0 | 0.0 | 84.06 |
|
||||||
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 100.0 | 1.07 | 0.0 | 0.0 | 101.07 | 17.01 | 0.0 | 0.0 | 84.06 |
|
||||||
|
Then Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||||
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||||
|
| 01 March 2024 | Accrual | 1.07 | 0.0 | 1.07 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 01 March 2024 | Contract Termination | 84.06 | 83.57 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
When Admin successfully undoes loan contract termination
|
||||||
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||||
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 17.01 | 0.0 | 0.0 | 85.04 |
|
||||||
|
Then Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||||
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||||
|
| 01 March 2024 | Accrual | 1.07 | 0.0 | 1.07 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 01 March 2024 | Contract Termination | 84.06 | 83.57 | 0.49 | 0.0 | 0.0 | 0.0 | true | false |
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.event.business.domain.loan.transaction;
|
||||||
|
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
|
||||||
|
public class LoanUndoContractTerminationBusinessEvent extends LoanTransactionBusinessEvent {
|
||||||
|
|
||||||
|
private static final String TYPE = "LoanUndoContractTerminationBusinessEvent";
|
||||||
|
|
||||||
|
public LoanUndoContractTerminationBusinessEvent(LoanTransaction value) {
|
||||||
|
super(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -180,6 +180,7 @@ public interface LoanApiConstants {
|
|||||||
String CAPITALIZED_INCOME_TRANSACTION_COMMAND = "capitalizedIncome";
|
String CAPITALIZED_INCOME_TRANSACTION_COMMAND = "capitalizedIncome";
|
||||||
String CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND = "capitalizedIncomeAdjustment";
|
String CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND = "capitalizedIncomeAdjustment";
|
||||||
String CONTRACT_TERMINATION_COMMAND = "contractTermination";
|
String CONTRACT_TERMINATION_COMMAND = "contractTermination";
|
||||||
|
String UNDO_CONTRACT_TERMINATION_COMMAND = "undoContractTermination";
|
||||||
|
|
||||||
// Data Validator names
|
// Data Validator names
|
||||||
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";
|
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";
|
||||||
|
|||||||
+2
@@ -71,6 +71,7 @@ public class LoanTransactionEnumData implements Serializable {
|
|||||||
private final boolean capitalizedIncomeAmortization;
|
private final boolean capitalizedIncomeAmortization;
|
||||||
private final boolean capitalizedIncomeAdjustment;
|
private final boolean capitalizedIncomeAdjustment;
|
||||||
private final boolean capitalizedIncomeAmortizationAdjustment;
|
private final boolean capitalizedIncomeAmortizationAdjustment;
|
||||||
|
private final boolean contractTermination;
|
||||||
|
|
||||||
public LoanTransactionEnumData(final Long id, final String code, final String value) {
|
public LoanTransactionEnumData(final Long id, final String code, final String value) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -112,6 +113,7 @@ public class LoanTransactionEnumData implements Serializable {
|
|||||||
this.capitalizedIncomeAdjustment = Long.valueOf(LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.getValue()).equals(this.id);
|
this.capitalizedIncomeAdjustment = Long.valueOf(LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.getValue()).equals(this.id);
|
||||||
this.capitalizedIncomeAmortizationAdjustment = Long
|
this.capitalizedIncomeAmortizationAdjustment = Long
|
||||||
.valueOf(LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT.getValue()).equals(this.id);
|
.valueOf(LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT.getValue()).equals(this.id);
|
||||||
|
this.contractTermination = Long.valueOf(LoanTransactionType.CONTRACT_TERMINATION.getValue()).equals(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRepaymentType() {
|
public boolean isRepaymentType() {
|
||||||
|
|||||||
@@ -1789,6 +1789,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return getLoanTransaction(e -> e.isNotReversed() && e.isChargeOff());
|
return getLoanTransaction(e -> e.isNotReversed() && e.isChargeOff());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanTransaction findContractTerminationTransaction() {
|
||||||
|
return getLoanTransaction(e -> e.isNotReversed() && e.isContractTermination());
|
||||||
|
}
|
||||||
|
|
||||||
public void handleMaturityDateActivate() {
|
public void handleMaturityDateActivate() {
|
||||||
if (this.expectedMaturityDate != null && !this.expectedMaturityDate.equals(this.actualMaturityDate)) {
|
if (this.expectedMaturityDate != null && !this.expectedMaturityDate.equals(this.actualMaturityDate)) {
|
||||||
this.actualMaturityDate = this.expectedMaturityDate;
|
this.actualMaturityDate = this.expectedMaturityDate;
|
||||||
|
|||||||
+2
@@ -92,4 +92,6 @@ public interface LoanTransactionValidator {
|
|||||||
void validateNote(DataValidatorBuilder baseDataValidator, JsonElement element);
|
void validateNote(DataValidatorBuilder baseDataValidator, JsonElement element);
|
||||||
|
|
||||||
void validateExternalId(DataValidatorBuilder baseDataValidator, JsonElement element);
|
void validateExternalId(DataValidatorBuilder baseDataValidator, JsonElement element);
|
||||||
|
|
||||||
|
void validateReversalExternalId(DataValidatorBuilder baseDataValidator, JsonElement element);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -52,4 +52,5 @@
|
|||||||
<include relativeToChangelogFile="true" file="parts/1027_add_capitalized_income_transaction_type.xml"/>
|
<include relativeToChangelogFile="true" file="parts/1027_add_capitalized_income_transaction_type.xml"/>
|
||||||
<include relativeToChangelogFile="true" file="parts/1028_add_missing_indexes.xml"/>
|
<include relativeToChangelogFile="true" file="parts/1028_add_missing_indexes.xml"/>
|
||||||
<include relativeToChangelogFile="true" file="parts/1029_add_installment_amount_in_multiples_of_to_loan.xml"/>
|
<include relativeToChangelogFile="true" file="parts/1029_add_installment_amount_in_multiples_of_to_loan.xml"/>
|
||||||
|
<include relativeToChangelogFile="true" file="parts/1030_add_loan_undo_contract_termination_event.xml"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
<?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.1.xsd">
|
||||||
|
<changeSet id="1" author="fineract">
|
||||||
|
<insert tableName="m_external_event_configuration">
|
||||||
|
<column name="type" value="LoanUndoContractTerminationBusinessEvent"/>
|
||||||
|
<column name="enabled" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+2
@@ -26,4 +26,6 @@ public interface ProgressiveLoanTransactionValidator extends LoanTransactionVali
|
|||||||
void validateCapitalizedIncome(JsonCommand command, Long loanId);
|
void validateCapitalizedIncome(JsonCommand command, Long loanId);
|
||||||
|
|
||||||
void validateCapitalizedIncomeAdjustment(JsonCommand command, Long loanId, Long capitalizedIncomeTransactionId);
|
void validateCapitalizedIncomeAdjustment(JsonCommand command, Long loanId, Long capitalizedIncomeTransactionId);
|
||||||
|
|
||||||
|
void validateContractTerminationUndo(JsonCommand command, Long loanId);
|
||||||
}
|
}
|
||||||
|
|||||||
+51
@@ -34,6 +34,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
@@ -219,6 +220,47 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateContractTerminationUndo(final JsonCommand command, final Long loanId) {
|
||||||
|
final String json = command.json();
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, getContractTerminationUndoParameters());
|
||||||
|
|
||||||
|
Validator.validateOrThrow("loan.contract.termination.undo", baseDataValidator -> {
|
||||||
|
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||||
|
validateLoanClientIsActive(loan);
|
||||||
|
validateLoanGroupIsActive(loan);
|
||||||
|
|
||||||
|
if (!loan.isOpen()) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.loan.is.not.active",
|
||||||
|
"Loan: " + loanId + " Undo Contract Termination is not allowed. Loan Account is not Active", loanId);
|
||||||
|
}
|
||||||
|
if (!loan.isContractTermination()) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.loan.is.not.contract.terminated",
|
||||||
|
"Loan: " + loanId + " is not contract terminated", loanId);
|
||||||
|
}
|
||||||
|
final LoanTransaction contractTerminationTransaction = loan.findContractTerminationTransaction();
|
||||||
|
if (contractTerminationTransaction == null) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.loan.contract.termination.transaction.not.found",
|
||||||
|
"Loan: " + loanId + " contract termination transaction was not found", loanId);
|
||||||
|
}
|
||||||
|
if (!contractTerminationTransaction.equals(loan.getLastUserTransaction())) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.loan.contract.termination.is.not.the.last.user.transaction",
|
||||||
|
"Loan: " + loanId
|
||||||
|
+ " contract termination cannot be undone. User transaction was found after contract termination!",
|
||||||
|
loanId);
|
||||||
|
}
|
||||||
|
|
||||||
|
validateNote(baseDataValidator, element);
|
||||||
|
validateReversalExternalId(baseDataValidator, element);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Delegates
|
// Delegates
|
||||||
@Override
|
@Override
|
||||||
public void validateDisbursement(JsonCommand command, boolean isAccountTransfer, Long loanId) {
|
public void validateDisbursement(JsonCommand command, boolean isAccountTransfer, Long loanId) {
|
||||||
@@ -368,6 +410,11 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
|
|||||||
loanTransactionValidator.validateExternalId(baseDataValidator, element);
|
loanTransactionValidator.validateExternalId(baseDataValidator, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateReversalExternalId(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
||||||
|
loanTransactionValidator.validateReversalExternalId(baseDataValidator, element);
|
||||||
|
}
|
||||||
|
|
||||||
private Set<String> getCapitalizedIncomeParameters() {
|
private Set<String> getCapitalizedIncomeParameters() {
|
||||||
return new HashSet<>(
|
return new HashSet<>(
|
||||||
Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId"));
|
Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId"));
|
||||||
@@ -377,4 +424,8 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
|
|||||||
return new HashSet<>(
|
return new HashSet<>(
|
||||||
Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId"));
|
Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<String> getContractTerminationUndoParameters() {
|
||||||
|
return new HashSet<>(Arrays.asList("note", "reversalExternalId"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -1234,6 +1234,8 @@ public class LoansApiResource {
|
|||||||
commandRequest = builder.assignDelinquency(resolvedLoanId).build();
|
commandRequest = builder.assignDelinquency(resolvedLoanId).build();
|
||||||
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.CONTRACT_TERMINATION_COMMAND)) {
|
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.CONTRACT_TERMINATION_COMMAND)) {
|
||||||
commandRequest = builder.applyContractTermination(resolvedLoanId).build();
|
commandRequest = builder.applyContractTermination(resolvedLoanId).build();
|
||||||
|
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.UNDO_CONTRACT_TERMINATION_COMMAND)) {
|
||||||
|
commandRequest = builder.undoContractTermination(resolvedLoanId).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commandRequest == null) {
|
if (commandRequest == null) {
|
||||||
|
|||||||
+2
@@ -733,6 +733,8 @@ final class LoansApiResourceSwagger {
|
|||||||
public boolean capitalizedIncomeAmortization;
|
public boolean capitalizedIncomeAmortization;
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public boolean capitalizedIncomeAdjustment;
|
public boolean capitalizedIncomeAdjustment;
|
||||||
|
@Schema(example = "false")
|
||||||
|
public boolean contractTermination;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class GetLoansLoanIdPaymentDetailData {
|
static final class GetLoansLoanIdPaymentDetailData {
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.handler.loan.contracttermination;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.annotation.CommandType;
|
||||||
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.contracttermination.LoanContractTerminationServiceImpl;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@CommandType(entity = "LOAN", action = "CONTRACT_TERMINATION_UNDO")
|
||||||
|
public class UndoLoanContractTerminationCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final LoanContractTerminationServiceImpl loanContractTerminationService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return loanContractTerminationService.undoContractTermination(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -1081,4 +1081,12 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
|
|||||||
baseDataValidator.reset().parameter("externalId").value(externalId).notExceedingLengthOf(100);
|
baseDataValidator.reset().parameter("externalId").value(externalId).notExceedingLengthOf(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateReversalExternalId(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
||||||
|
final String reversalExternalId = this.fromApiJsonHelper.extractStringNamed("reversalExternalId", element);
|
||||||
|
if (StringUtils.isNotBlank(reversalExternalId)) {
|
||||||
|
baseDataValidator.reset().parameter("reversalExternalId").value(reversalExternalId).notExceedingLengthOf(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+70
@@ -32,19 +32,25 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
|||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionContractTerminationPostBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionContractTerminationPostBusinessEvent;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanUndoContractTerminationBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
import org.apache.fineract.portfolio.note.domain.Note;
|
import org.apache.fineract.portfolio.note.domain.Note;
|
||||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||||
@@ -65,6 +71,9 @@ public class LoanContractTerminationServiceImpl {
|
|||||||
private final ExternalIdFactory externalIdFactory;
|
private final ExternalIdFactory externalIdFactory;
|
||||||
private final BusinessEventNotifierService businessEventNotifierService;
|
private final BusinessEventNotifierService businessEventNotifierService;
|
||||||
private final LoanTransactionService loanTransactionService;
|
private final LoanTransactionService loanTransactionService;
|
||||||
|
private final LoanScheduleService loanScheduleService;
|
||||||
|
private final LoanChargeValidator loanChargeValidator;
|
||||||
|
private final ProgressiveLoanTransactionValidator loanTransactionValidator;
|
||||||
|
|
||||||
public CommandProcessingResult applyContractTermination(final JsonCommand command) {
|
public CommandProcessingResult applyContractTermination(final JsonCommand command) {
|
||||||
final Loan loan = loanAssembler.assembleFrom(command.getLoanId());
|
final Loan loan = loanAssembler.assembleFrom(command.getLoanId());
|
||||||
@@ -116,6 +125,67 @@ public class LoanContractTerminationServiceImpl {
|
|||||||
.with(changes).build();
|
.with(changes).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandProcessingResult undoContractTermination(final JsonCommand command) {
|
||||||
|
final Long loanId = command.getLoanId();
|
||||||
|
|
||||||
|
loanTransactionValidator.validateContractTerminationUndo(command, loanId);
|
||||||
|
|
||||||
|
final Loan loan = loanAssembler.assembleFrom(loanId);
|
||||||
|
final LoanTransaction contractTerminationTransaction = loan.findContractTerminationTransaction();
|
||||||
|
|
||||||
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoContractTerminationBusinessEvent(contractTerminationTransaction));
|
||||||
|
businessEventNotifierService.notifyPreBusinessEvent(
|
||||||
|
new LoanAdjustTransactionBusinessEvent(new LoanAdjustTransactionBusinessEvent.Data(contractTerminationTransaction)));
|
||||||
|
|
||||||
|
// check if reversalExternalId is provided
|
||||||
|
final String reversalExternalId = command.stringValueOfParameterNamedAllowingNull(LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME);
|
||||||
|
final ExternalId reversalTxnExternalId = ExternalIdFactory.produce(reversalExternalId);
|
||||||
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Add note if provided
|
||||||
|
final String noteText = command.stringValueOfParameterNamed("note");
|
||||||
|
if (StringUtils.isNotBlank(noteText)) {
|
||||||
|
changes.put("note", noteText);
|
||||||
|
final Note note = Note.loanTransactionNote(loan, contractTerminationTransaction, noteText);
|
||||||
|
noteRepository.save(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(contractTerminationTransaction.getLoan(),
|
||||||
|
contractTerminationTransaction, "reversed");
|
||||||
|
contractTerminationTransaction.reverse(reversalTxnExternalId);
|
||||||
|
contractTerminationTransaction.manuallyAdjustedOrReversed();
|
||||||
|
|
||||||
|
loan.liftContractTerminationSubStatus();
|
||||||
|
changes.put(LoanApiConstants.subStatusAttributeName, loan.getLoanSubStatus());
|
||||||
|
loanTransactionRepository.saveAndFlush(contractTerminationTransaction);
|
||||||
|
|
||||||
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||||
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
|
|
||||||
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||||
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoContractTerminationBusinessEvent(contractTerminationTransaction));
|
||||||
|
|
||||||
|
final LoanAdjustTransactionBusinessEvent.Data eventData = new LoanAdjustTransactionBusinessEvent.Data(
|
||||||
|
contractTerminationTransaction);
|
||||||
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(eventData));
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withOfficeId(loan.getOfficeId()) //
|
||||||
|
.withClientId(loan.getClientId()) //
|
||||||
|
.withGroupId(loan.getGroupId()) //
|
||||||
|
.withLoanId(loanId) //
|
||||||
|
.withEntityId(contractTerminationTransaction.getId()) //
|
||||||
|
.withEntityExternalId(contractTerminationTransaction.getExternalId()) //
|
||||||
|
.with(changes) //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
public void validateContractTermination(final Loan loan) {
|
public void validateContractTermination(final Loan loan) {
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -109,7 +109,7 @@ public class ExternalEventConfigurationValidationServiceTest {
|
|||||||
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
||||||
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
||||||
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent");
|
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "LoanUndoContractTerminationBusinessEvent");
|
||||||
|
|
||||||
List<FineractPlatformTenant> tenants = Arrays
|
List<FineractPlatformTenant> tenants = Arrays
|
||||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||||
@@ -199,7 +199,7 @@ public class ExternalEventConfigurationValidationServiceTest {
|
|||||||
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
||||||
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
||||||
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent");
|
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "LoanUndoContractTerminationBusinessEvent");
|
||||||
|
|
||||||
List<FineractPlatformTenant> tenants = Arrays
|
List<FineractPlatformTenant> tenants = Arrays
|
||||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||||
|
|||||||
+5
@@ -621,6 +621,11 @@ public class ExternalEventConfigurationHelper {
|
|||||||
loanCapitalizedIncomeTransactionCreatedBusinessEvent.put("enabled", false);
|
loanCapitalizedIncomeTransactionCreatedBusinessEvent.put("enabled", false);
|
||||||
defaults.add(loanCapitalizedIncomeTransactionCreatedBusinessEvent);
|
defaults.add(loanCapitalizedIncomeTransactionCreatedBusinessEvent);
|
||||||
|
|
||||||
|
Map<String, Object> loanTransactionUndoContractTerminationBusinessEvent = new HashMap<>();
|
||||||
|
loanTransactionUndoContractTerminationBusinessEvent.put("type", "LoanUndoContractTerminationBusinessEvent");
|
||||||
|
loanTransactionUndoContractTerminationBusinessEvent.put("enabled", false);
|
||||||
|
defaults.add(loanTransactionUndoContractTerminationBusinessEvent);
|
||||||
|
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user