From a4f9b167f2a34848aaabe3ff8720b81e02120e84 Mon Sep 17 00:00:00 2001 From: Ruchi Dhamankar Date: Wed, 16 Aug 2023 10:54:22 +0530 Subject: [PATCH] FINERACT-1958-Down-Payment-transaction-type --- .../service/CommandWrapperBuilder.java | 8 + .../LoanTransactionBusinessEvent.java | 0 ...ansactionDownPaymentPostBusinessEvent.java | 35 ++++ ...ransactionDownPaymentPreBusinessEvent.java | 37 ++++ .../module/loan/module-changelog-master.xml | 1 + ...ion_for_down_payment_transaction_event.xml | 35 ++++ .../api/LoanTransactionsApiResource.java | 13 +- .../domain/LoanAccountDomainServiceJpa.java | 6 + .../LoanDownPaymentCommandHandler.java | 53 ++++++ ...entConfigurationValidationServiceTest.java | 6 +- .../LoanDownPaymentTransactionTypeTest.java | 171 ++++++++++++++++++ .../ExternalEventConfigurationHelper.java | 10 + .../common/loans/LoanTransactionHelper.java | 8 + 13 files changed, 378 insertions(+), 5 deletions(-) rename {fineract-provider => fineract-loan}/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionBusinessEvent.java (100%) create mode 100644 fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionDownPaymentPostBusinessEvent.java create mode 100644 fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionDownPaymentPreBusinessEvent.java create mode 100644 fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1004_add_external_event_configuration_for_down_payment_transaction_event.xml create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanDownPaymentCommandHandler.java create mode 100644 integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDownPaymentTransactionTypeTest.java diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index 77e4b893d..9c14256ff 100644 --- a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -3646,4 +3646,12 @@ public class CommandWrapperBuilder { this.href = "/external-asset-owners/transfers/" + id; return this; } + + public CommandWrapperBuilder downPayment(final Long loanId) { + this.actionName = "DOWNPAYMENT"; + this.entityName = "LOAN"; + this.loanId = loanId; + this.href = "/loans/" + loanId + "/transactions?command=downPayment"; + return this; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionBusinessEvent.java b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionBusinessEvent.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionBusinessEvent.java rename to fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionBusinessEvent.java diff --git a/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionDownPaymentPostBusinessEvent.java b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionDownPaymentPostBusinessEvent.java new file mode 100644 index 000000000..184d72d5c --- /dev/null +++ b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionDownPaymentPostBusinessEvent.java @@ -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 LoanTransactionDownPaymentPostBusinessEvent extends LoanTransactionBusinessEvent { + + private static final String TYPE = "LoanTransactionDownPaymentPostBusinessEvent"; + + public LoanTransactionDownPaymentPostBusinessEvent(LoanTransaction value) { + super(value); + } + + @Override + public String getType() { + return TYPE; + } +} diff --git a/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionDownPaymentPreBusinessEvent.java b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionDownPaymentPreBusinessEvent.java new file mode 100644 index 000000000..0e9e37ca5 --- /dev/null +++ b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanTransactionDownPaymentPreBusinessEvent.java @@ -0,0 +1,37 @@ +/** + * 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.infrastructure.event.business.domain.loan.LoanBusinessEvent; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; + +public class LoanTransactionDownPaymentPreBusinessEvent extends LoanBusinessEvent { + + private static final String TYPE = "LoanTransactionDownPaymentPreBusinessEvent"; + + public LoanTransactionDownPaymentPreBusinessEvent(Loan value) { + super(value); + } + + @Override + public String getType() { + return TYPE; + } + +} diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml index abab920a6..f9e4a3859 100644 --- a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml @@ -26,4 +26,5 @@ + diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1004_add_external_event_configuration_for_down_payment_transaction_event.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1004_add_external_event_configuration_for_down_payment_transaction_event.xml new file mode 100644 index 000000000..2c716c9e0 --- /dev/null +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1004_add_external_event_configuration_for_down_payment_transaction_event.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java index c1425e938..1e84c8026 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java @@ -80,6 +80,7 @@ public class LoanTransactionsApiResource { public static final String CHARGE_OFF_COMMAND_VALUE = "charge-off"; public static final String UNDO_CHARGE_OFF_COMMAND_VALUE = "undo-charge-off"; + public static final String DOWN_PAYMENT = "downPayment"; private final Set responseDataParameters = new HashSet<>(Arrays.asList("id", "type", "date", "currency", "amount", "externalId", LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME, LoanApiConstants.REVERSED_ON_DATE_PARAMNAME)); @@ -108,7 +109,7 @@ public class LoanTransactionsApiResource { + "loans/1/transactions/template?command=refundbycash" + "\n" + "loans/1/transactions/template?command=refundbytransfer" + "\n" + "loans/1/transactions/template?command=foreclosure" + "\n" + "loans/1/transactions/template?command=creditBalanceRefund (returned 'amount' field will have the overpaid value)" + "\n" - + "loans/1/transactions/template?command=charge-off" + "\n") + + "loans/1/transactions/template?command=charge-off" + "\n" + "loans/1/transactions/template?command=downPayment" + "\n") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) }) public String retrieveTransactionTemplate(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, @@ -137,7 +138,7 @@ public class LoanTransactionsApiResource { + "loans/1/transactions/template?command=refundbycash" + "\n" + "loans/1/transactions/template?command=refundbytransfer" + "\n" + "loans/1/transactions/template?command=foreclosure" + "\n" + "loans/1/transactions/template?command=creditBalanceRefund (returned 'amount' field will have the overpaid value)" + "\n" - + "loans/1/transactions/template?command=charge-off" + "\n") + + "loans/1/transactions/template?command=charge-off" + "\n" + "loans/1/transactions/template?command=downPayment" + "\n") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) }) public String retrieveTransactionTemplate( @@ -235,7 +236,8 @@ public class LoanTransactionsApiResource { + "loans/1/transactions?command=refundByCash" + " | Make a Refund of an Active Loan by Cash | \n" + "loans/1/transactions?command=foreclosure" + " | Foreclosure of an Active Loan | \n" + "loans/1/transactions?command=creditBalanceRefund" + " | Credit Balance Refund" + " | \n" - + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=charge-off" + " | Charge-off Loan" + " | \n") + + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=charge-off" + " | Charge-off Loan" + " | \n" + + "loans/1/transactions?command=downPayment" + " | Down Payment" + " | \n") @RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsResponse.class))) }) @@ -269,6 +271,7 @@ public class LoanTransactionsApiResource { + " | Foreclosure of an Active Loan | \n" + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=creditBalanceRefund" + " | Credit Balance Refund" + " | \n" + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=charge-off" + " | Charge-off Loan" + + " | \n" + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=downPayment" + " | Down Payment" + " | \n") @RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsRequest.class))) @ApiResponses({ @@ -472,6 +475,8 @@ public class LoanTransactionsApiResource { commandRequest = builder.chargeOff(resolvedLoanId).build(); } else if (CommandParameterUtil.is(commandParam, UNDO_CHARGE_OFF_COMMAND_VALUE)) { commandRequest = builder.undoChargeOff(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, DOWN_PAYMENT)) { + commandRequest = builder.downPayment(resolvedLoanId).build(); } if (commandRequest == null) { @@ -547,6 +552,8 @@ public class LoanTransactionsApiResource { transactionData = this.loanReadPlatformService.retrieveCreditBalanceRefundTemplate(resolvedLoanId); } else if (CommandParameterUtil.is(commandParam, CHARGE_OFF_COMMAND_VALUE)) { transactionData = this.loanReadPlatformService.retrieveLoanChargeOffTemplate(resolvedLoanId); + } else if (CommandParameterUtil.is(commandParam, DOWN_PAYMENT)) { + transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId); } else { throw new UnrecognizedQueryParamException("command", commandParam); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index 5816beea1..604e5804f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -53,6 +53,8 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanRefundPostBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanRefundPreBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionDownPaymentPostBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionDownPaymentPreBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionGoodwillCreditPostBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionGoodwillCreditPreBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionMakeRepaymentPostBusinessEvent; @@ -289,6 +291,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { repaymentEvent = new LoanChargePaymentPreBusinessEvent(loan); } else if (isRecoveryRepayment) { repaymentEvent = new LoanTransactionRecoveryPaymentPreBusinessEvent(loan); + } else if (repaymentTransactionType.isDownPayment()) { + repaymentEvent = new LoanTransactionDownPaymentPreBusinessEvent(loan); } return repaymentEvent; } @@ -308,6 +312,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { repaymentEvent = new LoanChargePaymentPostBusinessEvent(transaction); } else if (isRecoveryRepayment) { repaymentEvent = new LoanTransactionRecoveryPaymentPostBusinessEvent(transaction); + } else if (repaymentTransactionType.isDownPayment()) { + repaymentEvent = new LoanTransactionDownPaymentPostBusinessEvent(transaction); } return repaymentEvent; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanDownPaymentCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanDownPaymentCommandHandler.java new file mode 100644 index 000000000..ab2ce495d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanDownPaymentCommandHandler.java @@ -0,0 +1,53 @@ +/** + * 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; + +import lombok.RequiredArgsConstructor; +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.DataIntegrityErrorHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; +import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.orm.jpa.JpaSystemException; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@CommandType(entity = "LOAN", action = "DOWNPAYMENT") +public class LoanDownPaymentCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + private final DataIntegrityErrorHandler dataIntegrityErrorHandler; + + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + try { + boolean isRecoveryRepayment = false; + return this.writePlatformService.makeLoanRepayment(LoanTransactionType.DOWN_PAYMENT, command.getLoanId(), command, + isRecoveryRepayment); + } catch (final JpaSystemException | DataIntegrityViolationException dve) { + dataIntegrityErrorHandler.handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve, "loan.downPayment", + "Down Payment"); + return CommandProcessingResult.empty(); + } + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java index 4cceda7c6..6c094b12e 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java @@ -96,7 +96,8 @@ public class ExternalEventConfigurationValidationServiceTest { "LoanChargeAdjustmentPostBusinessEvent", "LoanChargeAdjustmentPreBusinessEvent", "LoanDelinquencyRangeChangeBusinessEvent", "LoanAccountsStayedLockedBusinessEvent", "MockBusinessEvent", "LoanChargeOffPreBusinessEvent", "LoanChargeOffPostBusinessEvent", "LoanUndoChargeOffBusinessEvent", "LoanAccrualTransactionCreatedBusinessEvent", - "LoanRescheduledDueAdjustScheduleBusinessEvent", "LoanOwnershipTransferBusinessEvent", "LoanAccountSnapshotBusinessEvent"); + "LoanRescheduledDueAdjustScheduleBusinessEvent", "LoanOwnershipTransferBusinessEvent", "LoanAccountSnapshotBusinessEvent", + "LoanTransactionDownPaymentPostBusinessEvent", "LoanTransactionDownPaymentPreBusinessEvent"); List tenants = Arrays .asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null)); @@ -174,7 +175,8 @@ public class ExternalEventConfigurationValidationServiceTest { "LoanChargeAdjustmentPostBusinessEvent", "LoanChargeAdjustmentPreBusinessEvent", "LoanDelinquencyRangeChangeBusinessEvent", "LoanAccountsStayedLockedBusinessEvent", "LoanChargeOffPreBusinessEvent", "LoanChargeOffPostBusinessEvent", "LoanUndoChargeOffBusinessEvent", "LoanAccrualTransactionCreatedBusinessEvent", - "LoanRescheduledDueAdjustScheduleBusinessEvent", "LoanOwnershipTransferBusinessEvent", "LoanAccountSnapshotBusinessEvent"); + "LoanRescheduledDueAdjustScheduleBusinessEvent", "LoanOwnershipTransferBusinessEvent", "LoanAccountSnapshotBusinessEvent", + "LoanTransactionDownPaymentPostBusinessEvent", "LoanTransactionDownPaymentPreBusinessEvent"); List tenants = Arrays .asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null)); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDownPaymentTransactionTypeTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDownPaymentTransactionTypeTest.java new file mode 100644 index 000000000..61c9ff3af --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDownPaymentTransactionTypeTest.java @@ -0,0 +1,171 @@ +/** + * 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.assertNotNull; + +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 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; +import org.apache.fineract.client.models.GetLoanProductsProductIdResponse; +import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest; +import org.apache.fineract.integrationtests.common.ClientHelper; +import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension; +import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(LoanTestLifecycleExtension.class) +public class LoanDownPaymentTransactionTypeTest { + + private ResponseSpecification responseSpec; + private RequestSpecification requestSpec; + private LoanTransactionHelper loanTransactionHelper; + private ClientHelper clientHelper; + + @BeforeEach + public void setup() { + Utils.initializeRESTAssured(); + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + this.clientHelper = new ClientHelper(this.requestSpec, this.responseSpec); + } + + @Test + public void loanDownPaymentTransactionTypeTest() { + + DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter(); + + String loanExternalIdStr = UUID.randomUUID().toString(); + + // Delinquency Bucket + final Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec); + final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec, + delinquencyBucketId); + + // client creation + final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue(); + + // Loan Product + GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, delinquencyBucketId); + assertNotNull(getLoanProductsProductResponse); + + final Integer loanId = createLoanAccount(clientId, getLoanProductsProductResponse.getId(), loanExternalIdStr); + + // make down payment for loan + final PostLoansLoanIdTransactionsResponse downPaymentTransaction_1 = loanTransactionHelper.makeLoanDownPayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("5 September 2022").locale("en") + .transactionAmount(100.0)); + assertNotNull(downPaymentTransaction_1); + + // verify details for down payment transaction + + GetLoansLoanIdTransactionsTransactionIdResponse loanDownPaymentTransaction = loanTransactionHelper.getLoanTransaction(loanId, + downPaymentTransaction_1.getResourceId().intValue()); + + assertNotNull(loanDownPaymentTransaction); + assertEquals(loanDownPaymentTransaction.getAmount(), 100.0); + assertEquals(loanDownPaymentTransaction.getPrincipalPortion(), 100.0); + assertEquals("loanTransactionType.downPayment", loanDownPaymentTransaction.getType().getCode()); + + // undo/adjust down payment + LocalDate adjustmentDate = LocalDate.of(2022, 9, 7); + String formattedDate = dateFormatter.format(adjustmentDate); + PostLoansLoanIdTransactionsResponse adjustmentResult = loanTransactionHelper.reverseLoanTransaction(loanExternalIdStr, + loanDownPaymentTransaction.getId(), new PostLoansLoanIdTransactionsTransactionIdRequest().transactionDate(formattedDate) + .locale("en").dateFormat("dd MMMM yyyy").transactionAmount(0.0)); + + assertNotNull(adjustmentResult); + assertEquals(loanDownPaymentTransaction.getId(), adjustmentResult.getResourceId()); + + // Down Payment Transaction with External Id Test + + // make downpayment with transaction external Id + String downPaymentExternalIdStr = UUID.randomUUID().toString(); + + final PostLoansLoanIdTransactionsResponse downPaymentTransaction_2 = loanTransactionHelper.makeLoanDownPayment(loanId.longValue(), + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("9 September 2022").locale("en") + .transactionAmount(200.0).externalId(downPaymentExternalIdStr)); + assertNotNull(downPaymentTransaction_2); + assertEquals(downPaymentExternalIdStr, downPaymentTransaction_2.getResourceExternalId()); + + // verify details for down payment transaction + + GetLoansLoanIdTransactionsTransactionIdResponse loanDownPaymentTransaction_1 = loanTransactionHelper.getLoanTransaction(loanId, + downPaymentTransaction_2.getResourceId().intValue()); + + assertNotNull(loanDownPaymentTransaction_1); + assertEquals(loanDownPaymentTransaction_1.getAmount(), 200.0); + assertEquals(loanDownPaymentTransaction_1.getPrincipalPortion(), 200.0); + assertEquals("loanTransactionType.downPayment", loanDownPaymentTransaction_1.getType().getCode()); + + // undo/adjust down payment + adjustmentDate = LocalDate.of(2022, 9, 12); + formattedDate = dateFormatter.format(adjustmentDate); + PostLoansLoanIdTransactionsResponse adjustmentResult_1 = loanTransactionHelper.reverseLoanTransaction(loanExternalIdStr, + downPaymentExternalIdStr, new PostLoansLoanIdTransactionsTransactionIdRequest().transactionDate(formattedDate).locale("en") + .dateFormat("dd MMMM yyyy").transactionAmount(0.0)); + + assertNotNull(adjustmentResult_1); + assertEquals(loanDownPaymentTransaction_1.getId(), adjustmentResult_1.getResourceId()); + assertEquals(downPaymentExternalIdStr, adjustmentResult_1.getResourceExternalId()); + } + + private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper, + final Integer delinquencyBucketId) { + final HashMap loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId); + final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap)); + return loanTransactionHelper.getLoanProduct(loanProductId); + } + + private Integer createLoanAccount(final Integer clientID, final Long loanProductID, final String externalId) { + + String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1") + .withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1") + .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance() + .withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod() + .withExpectedDisbursementDate("03 September 2022").withSubmittedOnDate("01 September 2022").withLoanType("individual") + .withExternalId(externalId).build(clientID.toString(), loanProductID.toString(), null); + + final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON); + loanTransactionHelper.approveLoan("02 September 2022", "1000", loanId, null); + loanTransactionHelper.disburseLoanWithNetDisbursalAmount("03 September 2022", loanId, "1000"); + return loanId; + } + +} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java index b52743696..b3c7ad60b 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java @@ -480,6 +480,16 @@ public class ExternalEventConfigurationHelper { loanAccountSnapshotBusinessEvent.put("enabled", false); defaults.add(loanAccountSnapshotBusinessEvent); + Map loanTransactionDownPaymentPreBusinessEvent = new HashMap<>(); + loanTransactionDownPaymentPreBusinessEvent.put("type", "LoanTransactionDownPaymentPreBusinessEvent"); + loanTransactionDownPaymentPreBusinessEvent.put("enabled", false); + defaults.add(loanTransactionDownPaymentPreBusinessEvent); + + Map loanTransactionDownPaymentPostBusinessEvent = new HashMap<>(); + loanTransactionDownPaymentPostBusinessEvent.put("type", "LoanTransactionDownPaymentPostBusinessEvent"); + loanTransactionDownPaymentPostBusinessEvent.put("enabled", false); + defaults.add(loanTransactionDownPaymentPostBusinessEvent); + return defaults; } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java index f7dc3cf7e..7523afe42 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java @@ -1897,4 +1897,12 @@ public class LoanTransactionHelper extends IntegrationTest { public PutLoanProductsProductIdResponse updateLoanProduct(Long id, PutLoanProductsProductIdRequest requestModifyLoan) { return ok(fineract().loanProducts.updateLoanProduct(id, requestModifyLoan)); } + + public PostLoansLoanIdTransactionsResponse makeLoanDownPayment(String loanExternalId, PostLoansLoanIdTransactionsRequest request) { + return ok(fineract().loanTransactions.executeLoanTransaction1(loanExternalId, request, "downPayment")); + } + + public PostLoansLoanIdTransactionsResponse makeLoanDownPayment(Long loanId, PostLoansLoanIdTransactionsRequest request) { + return ok(fineract().loanTransactions.executeLoanTransaction(loanId, request, "downPayment")); + } }