This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 80401586e Loan transaction reversal data extension
80401586e is described below
commit 80401586e683cdc86105edeb76334b4f9c029430
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Mon Aug 8 20:31:36 2022 -0500
Loan transaction reversal data extension
---
.../core/service/CommandParameterUtil.java | 31 +++++
.../loanaccount/api/LoanApiConstants.java | 4 +
.../api/LoanTransactionsApiResource.java | 137 ++++++++-------------
.../api/LoanTransactionsApiResourceSwagger.java | 4 +
.../loanaccount/data/LoanTransactionData.java | 18 ++-
.../portfolio/loanaccount/domain/Loan.java | 4 +-
.../loanaccount/domain/LoanTransaction.java | 12 ++
...tLoanRepaymentScheduleTransactionProcessor.java | 2 +-
.../serialization/LoanEventApiJsonValidator.java | 19 ++-
.../service/LoanReadPlatformServiceImpl.java | 7 +-
.../LoanWritePlatformServiceJpaRepositoryImpl.java | 6 +-
.../db/changelog/tenant/changelog-tenant.xml | 1 +
.../0038_add_reversal_data_to_loan_transaction.xml | 31 +++++
13 files changed, 167 insertions(+), 109 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java
new file mode 100644
index 000000000..69da158c9
--- /dev/null
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java
@@ -0,0 +1,31 @@
+/**
+ * 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.core.service;
+
+import org.apache.commons.lang3.StringUtils;
+
+public final class CommandParameterUtil {
+
+ private CommandParameterUtil() {}
+
+ public static boolean is(final String commandParam, final String
commandValue) {
+ return StringUtils.isNotBlank(commandParam) &&
commandParam.trim().equalsIgnoreCase(commandValue);
+ }
+
+}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
index 60c4b81cb..ca0907f3a 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
@@ -142,4 +142,8 @@ public interface LoanApiConstants {
String fixedPrincipalPercentagePerInstallmentParamName =
"fixedPrincipalPercentagePerInstallment";
String LOAN_ASSOCIATIONS_ALL = "all";
+
+ // Reversal Transation Data
+ String REVERSAL_EXTERNAL_ID_PARAMNAME = "reversalExternalId";
+ String REVERSED_ON_DATE_PARAMNAME = "reversedOnDate";
}
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 bea194e6c..62bc82943 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
@@ -44,7 +44,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
-import org.apache.commons.lang3.StringUtils;
+import lombok.AllArgsConstructor;
import org.apache.fineract.accounting.journalentry.api.DateParam;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
@@ -54,6 +54,7 @@ import
org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import
org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
@@ -63,18 +64,18 @@ import
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPla
import
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import
org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/loans/{loanId}/transactions")
@Component
+@AllArgsConstructor
@Scope("singleton")
@Tag(name = "Loan Transactions", description = "Capabilities include loan
repayment's, interest waivers and the ability to 'adjust' an existing
transaction. An 'adjustment' of a transaction is really a 'reversal' of
existing transaction followed by creation of a new transaction with the
provided details.")
public class LoanTransactionsApiResource {
- private final Set<String> responseDataParameters = new HashSet<>(
- Arrays.asList("id", "type", "date", "currency", "amount",
"externalId"));
+ private final Set<String> responseDataParameters = new
HashSet<>(Arrays.asList("id", "type", "date", "currency", "amount",
"externalId",
+ LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME,
LoanApiConstants.REVERSED_ON_DATE_PARAMNAME));
private final String resourceNameForPermissions = "LOAN";
@@ -86,26 +87,6 @@ public class LoanTransactionsApiResource {
private final PaymentTypeReadPlatformService
paymentTypeReadPlatformService;
private final LoanChargePaidByReadPlatformService
loanChargePaidByReadPlatformService;
- @Autowired
- public LoanTransactionsApiResource(final PlatformSecurityContext context,
final LoanReadPlatformService loanReadPlatformService,
- final ApiRequestParameterHelper apiRequestParameterHelper,
- final DefaultToApiJsonSerializer<LoanTransactionData>
toApiJsonSerializer,
- final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService,
- PaymentTypeReadPlatformService paymentTypeReadPlatformService,
- LoanChargePaidByReadPlatformService
loanChargePaidByReadPlatformService) {
- this.context = context;
- this.loanReadPlatformService = loanReadPlatformService;
- this.apiRequestParameterHelper = apiRequestParameterHelper;
- this.toApiJsonSerializer = toApiJsonSerializer;
- this.commandsSourceWritePlatformService =
commandsSourceWritePlatformService;
- this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
- this.loanChargePaidByReadPlatformService =
loanChargePaidByReadPlatformService;
- }
-
- private boolean is(final String commandParam, final String commandValue) {
- return StringUtils.isNotBlank(commandParam) &&
commandParam.trim().equalsIgnoreCase(commandValue);
- }
-
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@@ -132,39 +113,39 @@ public class LoanTransactionsApiResource {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
LoanTransactionData transactionData = null;
- if (is(commandParam, "repayment")) {
+ if (CommandParameterUtil.is(commandParam, "repayment")) {
transactionData =
this.loanReadPlatformService.retrieveLoanTransactionTemplate(loanId);
- } else if (is(commandParam, "merchantIssuedRefund")) {
+ } else if (CommandParameterUtil.is(commandParam,
"merchantIssuedRefund")) {
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
transactionData =
this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.MERCHANT_ISSUED_REFUND,
loanId, transactionDate);
- } else if (is(commandParam, "payoutRefund")) {
+ } else if (CommandParameterUtil.is(commandParam, "payoutRefund")) {
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
transactionData =
this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.PAYOUT_REFUND,
loanId,
transactionDate);
- } else if (is(commandParam, "goodwillCredit")) {
+ } else if (CommandParameterUtil.is(commandParam, "goodwillCredit")) {
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
transactionData =
this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.GOODWILL_CREDIT,
loanId,
transactionDate);
- } else if (is(commandParam, "waiveinterest")) {
+ } else if (CommandParameterUtil.is(commandParam, "waiveinterest")) {
transactionData =
this.loanReadPlatformService.retrieveWaiveInterestDetails(loanId);
- } else if (is(commandParam, "writeoff")) {
+ } else if (CommandParameterUtil.is(commandParam, "writeoff")) {
transactionData =
this.loanReadPlatformService.retrieveLoanWriteoffTemplate(loanId);
- } else if (is(commandParam, "close-rescheduled")) {
+ } else if (CommandParameterUtil.is(commandParam, "close-rescheduled"))
{
transactionData =
this.loanReadPlatformService.retrieveNewClosureDetails();
- } else if (is(commandParam, "close")) {
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
transactionData =
this.loanReadPlatformService.retrieveNewClosureDetails();
- } else if (is(commandParam, "disburse")) {
+ } else if (CommandParameterUtil.is(commandParam, "disburse")) {
transactionData =
this.loanReadPlatformService.retrieveDisbursalTemplate(loanId, true);
transactionData.setNumberOfRepayments(this.loanReadPlatformService.retrieveNumberOfRepayments(loanId));
final List<LoanRepaymentScheduleInstallmentData>
loanRepaymentScheduleInstallmentData = this.loanReadPlatformService
.getRepaymentDataResponse(loanId);
transactionData.setLoanRepaymentScheduleInstallments(loanRepaymentScheduleInstallmentData);
- } else if (is(commandParam, "disburseToSavings")) {
+ } else if (CommandParameterUtil.is(commandParam, "disburseToSavings"))
{
transactionData =
this.loanReadPlatformService.retrieveDisbursalTemplate(loanId, false);
- } else if (is(commandParam, "recoverypayment")) {
+ } else if (CommandParameterUtil.is(commandParam, "recoverypayment")) {
transactionData =
this.loanReadPlatformService.retrieveRecoveryPaymentTemplate(loanId);
- } else if (is(commandParam, "prepayLoan")) {
+ } else if (CommandParameterUtil.is(commandParam, "prepayLoan")) {
LocalDate transactionDate = null;
if (transactionDateParam == null) {
transactionDate = DateUtils.getBusinessLocalDate();
@@ -173,11 +154,11 @@ public class LoanTransactionsApiResource {
}
transactionData =
this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT,
loanId,
transactionDate);
- } else if (is(commandParam, "refundbycash")) {
+ } else if (CommandParameterUtil.is(commandParam, "refundbycash")) {
transactionData =
this.loanReadPlatformService.retrieveRefundByCashTemplate(loanId);
- } else if (is(commandParam, "refundbytransfer")) {
+ } else if (CommandParameterUtil.is(commandParam, "refundbytransfer")) {
transactionData =
this.loanReadPlatformService.retrieveDisbursalTemplate(loanId, true);
- } else if (is(commandParam, "foreclosure")) {
+ } else if (CommandParameterUtil.is(commandParam, "foreclosure")) {
LocalDate transactionDate = null;
if (transactionDateParam == null) {
transactionDate = DateUtils.getBusinessLocalDate();
@@ -185,7 +166,7 @@ public class LoanTransactionsApiResource {
transactionDate =
transactionDateParam.getDate("transactionDate", dateFormat, locale);
}
transactionData =
this.loanReadPlatformService.retrieveLoanForeclosureTemplate(loanId,
transactionDate);
- } else if (is(commandParam, "creditBalanceRefund")) {
+ } else if (CommandParameterUtil.is(commandParam,
"creditBalanceRefund")) {
transactionData =
this.loanReadPlatformService.retrieveCreditBalanceRefundTemplate(loanId);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
@@ -245,55 +226,41 @@ public class LoanTransactionsApiResource {
final CommandWrapperBuilder builder = new
CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
- CommandProcessingResult result = null;
- if (is(commandParam, "repayment")) {
- final CommandWrapper commandRequest =
builder.loanRepaymentTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "merchantIssuedRefund")) {
- final CommandWrapper commandRequest =
builder.loanMerchantIssuedRefundTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "payoutRefund")) {
- final CommandWrapper commandRequest =
builder.loanPayoutRefundTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "goodwillCredit")) {
- final CommandWrapper commandRequest =
builder.loanGoodwillCreditTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "chargeRefund")) {
- final CommandWrapper commandRequest =
builder.refundLoanCharge(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "waiveinterest")) {
- final CommandWrapper commandRequest =
builder.waiveInterestPortionTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "writeoff")) {
- final CommandWrapper commandRequest =
builder.writeOffLoanTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "close-rescheduled")) {
- final CommandWrapper commandRequest =
builder.closeLoanAsRescheduledTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "close")) {
- final CommandWrapper commandRequest =
builder.closeLoanTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "undowriteoff")) {
- final CommandWrapper commandRequest =
builder.undoWriteOffLoanTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "recoverypayment")) {
- final CommandWrapper commandRequest =
builder.loanRecoveryPaymentTransaction(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "refundByCash")) {
- final CommandWrapper commandRequest =
builder.refundLoanTransactionByCash(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "foreclosure")) {
- final CommandWrapper commandRequest =
builder.loanForeclosure(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else if (is(commandParam, "creditBalanceRefund")) {
- final CommandWrapper commandRequest =
builder.creditBalanceRefund(loanId).build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "repayment")) {
+ commandRequest = builder.loanRepaymentTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam,
"merchantIssuedRefund")) {
+ commandRequest =
builder.loanMerchantIssuedRefundTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "payoutRefund")) {
+ commandRequest =
builder.loanPayoutRefundTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "goodwillCredit")) {
+ commandRequest =
builder.loanGoodwillCreditTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "chargeRefund")) {
+ commandRequest = builder.refundLoanCharge(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "waiveinterest")) {
+ commandRequest =
builder.waiveInterestPortionTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "writeoff")) {
+ commandRequest = builder.writeOffLoanTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close-rescheduled"))
{
+ commandRequest =
builder.closeLoanAsRescheduledTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeLoanTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undowriteoff")) {
+ commandRequest =
builder.undoWriteOffLoanTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "recoverypayment")) {
+ commandRequest =
builder.loanRecoveryPaymentTransaction(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "refundByCash")) {
+ commandRequest =
builder.refundLoanTransactionByCash(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "foreclosure")) {
+ commandRequest = builder.loanForeclosure(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam,
"creditBalanceRefund")) {
+ commandRequest = builder.creditBalanceRefund(loanId).build();
}
- if (result == null) {
+ if (commandRequest == null) {
throw new UnrecognizedQueryParamException("command", commandParam);
}
-
+ final CommandProcessingResult result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
index cb7327dc6..39ea011ee 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
@@ -142,6 +142,10 @@ final class LoanTransactionsApiResourceSwagger {
public Double amount;
@Schema(example = "559.88")
public Double interestPortion;
+ @Schema(example = "20120514")
+ public String reversalExternalId;
+ @Schema(example = "[2012, 5, 18]")
+ public LocalDate reversedOnDate;
}
@Schema(description = "PostLoansLoanIdTransactionsRequest")
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
index ce805898d..4c2ec3483 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
@@ -86,6 +86,10 @@ public class LoanTransactionData {
private transient String transactionType;
private List<LoanRepaymentScheduleInstallmentData>
loanRepaymentScheduleInstallments;
+ // Reverse Data
+ private String reversalExternalId;
+ private LocalDate reversedOnDate;
+
public static LoanTransactionData importInstance(BigDecimal
repaymentAmount, LocalDate lastRepaymentDate, Long repaymentTypeId,
Integer rowIndex, String locale, String dateFormat) {
return new LoanTransactionData(repaymentAmount, lastRepaymentDate,
repaymentTypeId, rowIndex, locale, dateFormat);
@@ -222,7 +226,7 @@ public class LoanTransactionData {
boolean manuallyReversed) {
this(id, officeId, officeName, transactionType, paymentDetailData,
currency, date, amount, netDisbursalAmount, principalPortion,
interestPortion, feeChargesPortion, penaltyChargesPortion,
overpaymentPortion, unrecognizedIncomePortion,
- paymentTypeOptions, externalId, transfer, fixedEmiAmount,
outstandingLoanBalance, null, manuallyReversed);
+ paymentTypeOptions, externalId, transfer, fixedEmiAmount,
outstandingLoanBalance, null, manuallyReversed, null, null);
}
public LoanTransactionData(final Long id, final Long officeId, final
String officeName, final LoanTransactionEnumData transactionType,
@@ -230,10 +234,11 @@ public class LoanTransactionData {
final BigDecimal netDisbursalAmount, final BigDecimal
principalPortion, final BigDecimal interestPortion,
final BigDecimal feeChargesPortion, final BigDecimal
penaltyChargesPortion, final BigDecimal overpaymentPortion,
final BigDecimal unrecognizedIncomePortion, final String
externalId, final AccountTransferData transfer,
- BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance,
LocalDate submittedOnDate, final boolean manuallyReversed) {
+ BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance,
LocalDate submittedOnDate, final boolean manuallyReversed,
+ final String reversalExternalId, final LocalDate reversedOnDate) {
this(id, officeId, officeName, transactionType, paymentDetailData,
currency, date, amount, netDisbursalAmount, principalPortion,
interestPortion, feeChargesPortion, penaltyChargesPortion,
overpaymentPortion, unrecognizedIncomePortion, null, externalId,
- transfer, fixedEmiAmount, outstandingLoanBalance,
submittedOnDate, manuallyReversed);
+ transfer, fixedEmiAmount, outstandingLoanBalance,
submittedOnDate, manuallyReversed, reversalExternalId, reversedOnDate);
}
public LoanTransactionData(final Long id, final Long officeId, final
String officeName, final LoanTransactionEnumData transactionType,
@@ -242,7 +247,8 @@ public class LoanTransactionData {
final BigDecimal feeChargesPortion, final BigDecimal
penaltyChargesPortion, final BigDecimal overpaymentPortion,
final BigDecimal unrecognizedIncomePortion, final
Collection<PaymentTypeData> paymentTypeOptions, final String externalId,
final AccountTransferData transfer, final BigDecimal
fixedEmiAmount, BigDecimal outstandingLoanBalance,
- final LocalDate submittedOnDate, final boolean manuallyReversed) {
+ final LocalDate submittedOnDate, final boolean manuallyReversed,
final String reversalExternalId,
+ final LocalDate reversedOnDate) {
this.id = id;
this.officeId = officeId;
this.officeName = officeName;
@@ -266,6 +272,8 @@ public class LoanTransactionData {
this.submittedOnDate = submittedOnDate;
this.manuallyReversed = manuallyReversed;
this.possibleNextRepaymentDate = null;
+ this.reversalExternalId = reversalExternalId;
+ this.reversedOnDate = reversedOnDate;
}
public LoanTransactionData(Long id, LoanTransactionEnumData
transactionType, LocalDate date, BigDecimal totalAmount,
@@ -274,7 +282,7 @@ public class LoanTransactionData {
BigDecimal outstandingLoanBalance, final boolean manuallyReversed)
{
this(id, null, null, transactionType, null, null, date, totalAmount,
netDisbursalAmount, principalPortion, interestPortion,
feeChargesPortion, penaltyChargesPortion, overpaymentPortion,
unrecognizedIncomePortion, null, null, null, null,
- outstandingLoanBalance, null, manuallyReversed);
+ outstandingLoanBalance, null, manuallyReversed, null, null);
}
public static LoanTransactionData
loanTransactionDataForDisbursalTemplate(final LoanTransactionEnumData
transactionType,
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 8909db4ae..e651117c6 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -3606,7 +3606,7 @@ public class Loan extends
AbstractAuditableWithUTCDateTimeCustom {
public ChangedTransactionDetail adjustExistingTransaction(final
LoanTransaction newTransactionDetail,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final
LoanTransaction transactionForAdjustment,
final List<Long> existingTransactionIds, final List<Long>
existingReversedTransactionIds,
- final ScheduleGeneratorDTO scheduleGeneratorDTO) {
+ final ScheduleGeneratorDTO scheduleGeneratorDTO, final String
reversalExternalId) {
HolidayDetailDTO holidayDetailDTO =
scheduleGeneratorDTO.getHolidayDetailDTO();
validateActivityNotBeforeLastTransactionDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
transactionForAdjustment.getTransactionDate());
@@ -3630,7 +3630,7 @@ public class Loan extends
AbstractAuditableWithUTCDateTimeCustom {
"adjustment.is.only.allowed.to.repayment.or.waiver.or.creditbalancerefund.transactions",
errorMessage);
}
- transactionForAdjustment.reverse();
+ transactionForAdjustment.reverse(reversalExternalId);
transactionForAdjustment.manuallyAdjustedOrReversed();
if (isClosedWrittenOff()) {
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index b1c0f3d8b..60e83ac2a 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -106,6 +106,12 @@ public class LoanTransaction extends
AbstractAuditableWithUTCDateTimeCustom {
@Column(name = "external_id", length = 100, nullable = true, unique = true)
private String externalId;
+ @Column(name = "reversal_external_id", length = 100, nullable = true,
unique = true)
+ private String reversalExternalId;
+
+ @Column(name = "reversed_on_date", nullable = true)
+ private LocalDate reversedOnDate;
+
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction",
orphanRemoval = true, fetch = FetchType.EAGER)
private Set<LoanChargePaidBy> loanChargesPaid = new HashSet<>();
@@ -364,9 +370,15 @@ public class LoanTransaction extends
AbstractAuditableWithUTCDateTimeCustom {
public void reverse() {
this.loan.validateRepaymentTypeTransactionNotBeforeAChargeRefund(this,
"reversed");
this.reversed = true;
+ this.reversedOnDate = DateUtils.getBusinessLocalDate();
this.loanTransactionToRepaymentScheduleMappings.clear();
}
+ public void reverse(final String reversalExternalId) {
+ this.reverse();
+ this.reversalExternalId = reversalExternalId;
+ }
+
public void resetDerivedComponents() {
this.principalPortion = null;
this.interestPortion = null;
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
index 224cc7ca9..5a10c2298 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -176,7 +176,7 @@ public abstract class
AbstractLoanRepaymentScheduleTransactionProcessor implemen
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(
newLoanTransaction.getLoanTransactionToRepaymentScheduleMappings());
} else {
- loanTransaction.reverse();
+
loanTransaction.reverse(loanTransaction.getExternalId());
loanTransaction.updateExternalId(null);
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(),
newLoanTransaction);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java
index 487952f26..1e0ac6c90 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java
@@ -33,6 +33,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
@@ -51,24 +52,16 @@ import
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleIns
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import
org.apache.fineract.portfolio.loanaccount.exception.LoanRepaymentScheduleNotFoundException;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
+@AllArgsConstructor
public final class LoanEventApiJsonValidator {
private final FromJsonHelper fromApiJsonHelper;
private final LoanApplicationCommandFromApiJsonHelper
fromApiJsonDeserializer;
private final LoanRepository loanRepository;
- @Autowired
- public LoanEventApiJsonValidator(final FromJsonHelper fromApiJsonHelper,
- final LoanApplicationCommandFromApiJsonHelper
fromApiJsonDeserializer, final LoanRepository loanRepository) {
- this.fromApiJsonHelper = fromApiJsonHelper;
- this.fromApiJsonDeserializer = fromApiJsonDeserializer;
- this.loanRepository = loanRepository;
- }
-
private void throwExceptionIfValidationWarningsExist(final
List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new
PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.",
@@ -195,7 +188,8 @@ public final class LoanEventApiJsonValidator {
}
final Set<String> transactionParameters = new
HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId",
"note",
- "locale", "dateFormat", "paymentTypeId", "accountNumber",
"checkNumber", "routingCode", "receiptNumber", "bankNumber"));
+ "locale", "dateFormat", "paymentTypeId", "accountNumber",
"checkNumber", "routingCode", "receiptNumber", "bankNumber",
+ LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME));
final Type typeOfMap = new TypeToken<Map<String, Object>>()
{}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
transactionParameters);
@@ -213,6 +207,11 @@ public final class LoanEventApiJsonValidator {
final String note = this.fromApiJsonHelper.extractStringNamed("note",
element);
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
+ final String reversalExternalId =
this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME,
+ element);
+
baseDataValidator.reset().parameter(LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME).ignoreIfNull().value(reversalExternalId)
+ .notExceedingLengthOf(100);
+
validatePaymentDetails(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 1c84f5d8b..2b38734a2 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -1300,7 +1300,7 @@ public class LoanReadPlatformServiceImpl implements
LoanReadPlatformService {
+ " tr.fee_charges_portion_derived as fees,
tr.penalty_charges_portion_derived as penalties, "
+ " tr.overpayment_portion_derived as overpayment,
tr.outstanding_loan_balance_derived as outstandingLoanBalance, "
+ " tr.unrecognized_income_portion as unrecognizedIncome,"
+ " tr.submitted_on_date as submittedOnDate, "
- + " tr.manually_adjusted_or_reversed as manuallyReversed, "
+ + " tr.manually_adjusted_or_reversed as manuallyReversed,
tr.reversal_external_id as reversalExternalId, tr.reversed_on_date as
reversedOnDate, "
+ " pd.payment_type_id as paymentType,pd.account_number as
accountNumber,pd.check_number as checkNumber, "
+ " pd.receipt_number as receiptNumber, pd.bank_number as
bankNumber,pd.routing_code as routingCode, l.net_disbursal_amount as
netDisbursalAmount,"
+ " l.currency_code as currencyCode, l.currency_digits as
currencyDigits, l.currency_multiplesof as inMultiplesOf, rc."
@@ -1366,6 +1366,8 @@ public class LoanReadPlatformServiceImpl implements
LoanReadPlatformService {
final BigDecimal unrecognizedIncomePortion =
JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "unrecognizedIncome");
final BigDecimal outstandingLoanBalance =
JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "outstandingLoanBalance");
final String externalId = rs.getString("externalId");
+ final String reversalExternalId =
rs.getString("reversalExternalId");
+ final LocalDate reversedOnDate = JdbcSupport.getLocalDate(rs,
"reversedOnDate");
final BigDecimal netDisbursalAmount =
JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "netDisbursalAmount");
@@ -1391,7 +1393,8 @@ public class LoanReadPlatformServiceImpl implements
LoanReadPlatformService {
}
return new LoanTransactionData(id, officeId, officeName,
transactionType, paymentDetailData, currencyData, date, totalAmount,
netDisbursalAmount, principalPortion, interestPortion,
feeChargesPortion, penaltyChargesPortion, overPaymentPortion,
- unrecognizedIncomePortion, externalId, transfer, null,
outstandingLoanBalance, submittedOnDate, manuallyReversed);
+ unrecognizedIncomePortion, externalId, transfer, null,
outstandingLoanBalance, submittedOnDate, manuallyReversed,
+ reversalExternalId, reversedOnDate);
}
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index c92ff7b9a..833c4c3e4 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -800,7 +800,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
@Override
public CommandProcessingResult undoLoanDisbursal(final Long loanId, final
JsonCommand command) {
- final AppUser currentUser = getAppUserIfPresent();
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
businessEventNotifierService.notifyPreBusinessEvent(new
LoanUndoDisbursalBusinessEvent(loan));
@@ -1009,8 +1008,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
@Override
public CommandProcessingResult adjustLoanTransaction(final Long loanId,
final Long transactionId, final JsonCommand command) {
- AppUser currentUser = getAppUserIfPresent();
-
this.loanEventApiJsonValidator.validateTransaction(command.json());
final Loan loan = this.loanAssembler.assembleFrom(loanId);
@@ -1036,6 +1033,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
final LocalDate transactionDate =
command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount =
command.bigDecimalValueOfParameterNamed("transactionAmount");
final String txnExternalId =
command.stringValueOfParameterNamedAllowingNull("externalId");
+ final String reversalExternalId =
command.stringValueOfParameterNamedAllowingNull(LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME);
final Map<String, Object> changes = new LinkedHashMap<>();
changes.put("transactionDate",
command.stringValueOfParameterNamed("transactionDate"));
@@ -1076,7 +1074,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
final ChangedTransactionDetail changedTransactionDetail =
loan.adjustExistingTransaction(newTransactionDetail,
defaultLoanLifecycleStateMachine(), transactionToAdjust,
existingTransactionIds, existingReversedTransactionIds,
- scheduleGeneratorDTO);
+ scheduleGeneratorDTO, reversalExternalId);
if
(newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
if (paymentDetail != null) {
diff --git
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index 501a7aafc..362169f97 100644
---
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -57,4 +57,5 @@
<include file="parts/0035_add_audit_entries_to_calendar.xml"
relativeToChangelogFile="true"/>
<include
file="parts/0036_add_audit_entries_and_rework_command_source_datetime_fields.xml"
relativeToChangelogFile="true"/>
<include file="parts/0037_add_loan_cob_job_data.xml"
relativeToChangelogFile="true"/>
+ <include file="parts/0038_add_reversal_data_to_loan_transaction.xml"
relativeToChangelogFile="true"/>
</databaseChangeLog>
diff --git
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0038_add_reversal_data_to_loan_transaction.xml
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0038_add_reversal_data_to_loan_transaction.xml
new file mode 100644
index 000000000..3b30d4bf5
--- /dev/null
+++
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0038_add_reversal_data_to_loan_transaction.xml
@@ -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 author="fineract" id="1">
+ <addColumn tableName="m_loan_transaction">
+ <column defaultValueComputed="NULL" name="reversal_external_id"
type="VARCHAR(100)" />
+ </addColumn>
+ <addColumn tableName="m_loan_transaction">
+ <column defaultValueComputed="NULL" name="reversed_on_date"
type="date"/>
+ </addColumn>
+ </changeSet>
+</databaseChangeLog>