Credit Balance Refund loan transaction

This commit is contained in:
John Woodlock
2022-04-19 10:04:16 +02:00
committed by Arnold Gálovics
parent 3d3e8c4083
commit d1168833cc
24 changed files with 671 additions and 21 deletions
@@ -80,6 +80,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
createJournalEntriesForRefund(loanDTO, loanTransactionDTO, office);
}
/** Logic for Credit Balance Refunds **/
else if (loanTransactionDTO.getTransactionType().isCreditBalanceRefund()) {
createJournalEntriesForCreditBalanceRefund(loanDTO, loanTransactionDTO, office);
}
/** Handle Write Offs, waivers and their reversals **/
else if ((loanTransactionDTO.getTransactionType().isWriteOff() || loanTransactionDTO.getTransactionType().isWaiveInterest()
|| loanTransactionDTO.getTransactionType().isWaiveCharges())) {
@@ -397,6 +402,25 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
}
private void createJournalEntriesForCreditBalanceRefund(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
final Office office) {
// loan properties
final Long loanProductId = loanDTO.getLoanProductId();
final Long loanId = loanDTO.getLoanId();
final String currencyCode = loanDTO.getCurrencyCode();
// transaction properties
final String transactionId = loanTransactionDTO.getTransactionId();
final Date transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal refundAmount = loanTransactionDTO.getAmount();
final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), AccrualAccountsForLoan.OVERPAYMENT.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, refundAmount, isReversal);
}
private void createJournalEntriesForRefundForActiveLoan(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
// TODO Auto-generated method stub
// loan properties
@@ -878,6 +878,15 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder creditBalanceRefund(final Long loanId) {
this.actionName = "CREDITBALANCEREFUND";
this.entityName = "LOAN";
this.entityId = null;
this.loanId = loanId;
this.href = "/loans/" + loanId + "/transactions?command=creditBalanceRefund";
return this;
}
public CommandWrapperBuilder undoWaiveChargeTransaction(final Long loanId, final Long transactionId) {
this.actionName = "UNDO";
this.entityName = "WAIVECHARGE";
@@ -55,6 +55,7 @@ public class BusinessEventNotificationConstants {
LOAN_INTEREST_RECALCULATION("loan_interest_recalculation"), //
LOAN_REFUND("loan_refund"), //
LOAN_FORECLOSURE("loan_foreclosure"), //
LOAN_CREDIT_BALANCE_REFUND("loan_credit_balance_refund"), //
LOAN_CREATE("loan_create"), //
LOAN_PRODUCT_CREATE("loan_product_create"), //
SAVINGS_ACTIVATE("savings_activated"), //
@@ -164,6 +164,8 @@ public class LoanTransactionsApiResource {
DateUtils.getDateTimeZoneOfTenant());
}
transactionData = this.loanReadPlatformService.retrieveLoanForeclosureTemplate(loanId, transactionDate);
} else if (is(commandParam, "creditBalanceRefund")) {
transactionData = this.loanReadPlatformService.retrieveCreditBalanceRefundTemplate(loanId);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
@@ -239,6 +241,9 @@ public class LoanTransactionsApiResource {
} 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);
}
if (result == null) {
@@ -44,6 +44,7 @@ public class LoanTransactionEnumData {
private final boolean chargePayment;
private final boolean refund;
private final boolean refundForActiveLoans;
private final boolean creditBalanceRefund;
public LoanTransactionEnumData(final Long id, final String code, final String value) {
this.id = id;
@@ -65,6 +66,7 @@ public class LoanTransactionEnumData {
this.refund = Long.valueOf(16).equals(this.id);
this.chargePayment = Long.valueOf(17).equals(this.id);
this.refundForActiveLoans = Long.valueOf(18).equals(this.id);
this.creditBalanceRefund = Long.valueOf(20).equals(this.id);
}
public Long id() {
@@ -152,4 +154,8 @@ public class LoanTransactionEnumData {
return this.refundForActiveLoans;
}
public boolean isCreditBalanceRefund() {
return this.creditBalanceRefund;
}
}
@@ -106,6 +106,8 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
break;
case WRITE_OFF_OUTSTANDING_UNDO:
break;
case LOAN_CREDIT_BALANCE_REFUND:
break;
default:
break;
}
@@ -3615,10 +3615,11 @@ public class Loan extends AbstractPersistableCustom {
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
transactionForAdjustment.getTransactionDate());
if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()) {
final String errorMessage = "Only transactions of type repayment or waiver can be adjusted.";
throw new InvalidLoanTransactionTypeException("transaction", "adjustment.is.only.allowed.to.repayment.or.waiver.transaction",
errorMessage);
if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()
&& transactionForAdjustment.isNotCreditBalanceRefund()) {
final String errorMessage = "Only transactions of type repayment, waiver or credit balance refund can be adjusted.";
throw new InvalidLoanTransactionTypeException("transaction",
"adjustment.is.only.allowed.to.repayment.or.waiver.or.creditbalancerefund.transactions", errorMessage);
}
transactionForAdjustment.reverse();
@@ -3698,7 +3699,8 @@ public class Loan extends AbstractPersistableCustom {
}
for (final LoanTransaction loanTransaction : this.loanTransactions) {
if ((loanTransaction.isRefund() || loanTransaction.isRefundForActiveLoan()) && !loanTransaction.isReversed()) {
if ((loanTransaction.isRefund() || loanTransaction.isRefundForActiveLoan() || loanTransaction.isCreditBalanceRefund())
&& !loanTransaction.isReversed()) {
totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getAmount(currency));
}
}
@@ -5157,6 +5159,14 @@ public class Loan extends AbstractPersistableCustom {
dataValidationErrors.add(error);
}
break;
case LOAN_CREDIT_BALANCE_REFUND:
if (!status().isOverpaid()) {
final String defaultUserMessage = "Loan Credit Balance Refund is not allowed. Loan Account is not Overpaid.";
final ApiParameterError error = ApiParameterError
.generalError("error.msg.loan.credit.balance.refund.account.is.not.overpaid", defaultUserMessage);
dataValidationErrors.add(error);
}
break;
default:
break;
}
@@ -6062,6 +6072,40 @@ public class Loan extends AbstractPersistableCustom {
return this.guaranteeAmountDerived == null ? BigDecimal.ZERO : this.guaranteeAmountDerived;
}
public void creditBalanceRefund(LoanTransaction newCreditBalanceRefundTransaction,
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, List<Long> existingTransactionIds,
List<Long> existingReversedTransactionIds) {
validateAccountStatus(LoanEvent.LOAN_CREDIT_BALANCE_REFUND);
validateRefundDateIsAfterLastRepayment(newCreditBalanceRefundTransaction.getTransactionDate());
if (!newCreditBalanceRefundTransaction.isGreaterThanZeroAndLessThanOrEqualTo(this.totalOverpaid)) {
final String errorMessage = "Transaction Amount ("
+ newCreditBalanceRefundTransaction.getAmount(getCurrency()).getAmount().toString()
+ ") must be > zero and <= Overpaid amount (" + this.totalOverpaid.toString() + ").";
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final ApiParameterError error = ApiParameterError.parameterError(
"error.msg.transactionAmount.invalid.must.be.>zero.and<=overpaidamount", errorMessage, "transactionAmount",
newCreditBalanceRefundTransaction.getAmount(getCurrency()));
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
this.loanTransactions.add(newCreditBalanceRefundTransaction);
updateLoanSummaryDerivedFields();
if (this.totalOverpaid == null || BigDecimal.ZERO.compareTo(this.totalOverpaid) == 0) {
this.loanStatus = LoanStatus.CLOSED_OBLIGATIONS_MET.getValue();
}
}
public ChangedTransactionDetail makeRefundForActiveLoan(final LoanTransaction loanTransaction,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds, final boolean allowTransactionsOnHoliday, final List<Holiday> holidays,
@@ -6179,7 +6223,8 @@ public class Loan extends AbstractPersistableCustom {
LocalDate lastTransactionDate = null;
for (final LoanTransaction transaction : this.loanTransactions) {
if ((transaction.isRepayment() || transaction.isRefundForActiveLoan()) && transaction.isNonZero()) {
if ((transaction.isRepayment() || transaction.isRefundForActiveLoan() || transaction.isCreditBalanceRefund())
&& transaction.isNonZero() && transaction.isNotReversed()) {
lastTransactionDate = transaction.getTransactionDate();
}
}
@@ -6841,4 +6886,5 @@ public class Loan extends AbstractPersistableCustom {
public void adjustNetDisbursalAmount(BigDecimal adjustedAmount) {
this.netDisbursalAmount = adjustedAmount.subtract(this.deriveSumTotalOfChargesDueAtDisbursement());
}
}
@@ -78,4 +78,7 @@ public interface LoanAccountDomainService {
void disableStandingInstructionsLinkedToClosedLoan(Loan loan);
void recalculateAccruals(Loan loan, boolean isInterestCalcualtionHappened);
CommandProcessingResultBuilder creditBalanceRefund(Long loanId, LocalDate transactionDate, BigDecimal transactionAmount,
String noteText, String externalId);
}
@@ -623,6 +623,41 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
return user;
}
@Override
public CommandProcessingResultBuilder creditBalanceRefund(Long loanId, LocalDate transactionDate, BigDecimal transactionAmount,
String noteText, String externalId) {
final Loan loan = this.loanAccountAssembler.assembleFrom(loanId);
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BusinessEvents.LOAN_CREDIT_BALANCE_REFUND,
constructEntityMap(BusinessEntity.LOAN, loan));
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
AppUser currentUser = getAppUserIfPresent();
final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount);
final LoanTransaction newCreditBalanceRefundTransaction = LoanTransaction.creditBalanceRefund(loan, loan.getOffice(), refundAmount,
transactionDate, externalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
loan.creditBalanceRefund(newCreditBalanceRefundTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds,
existingReversedTransactionIds);
this.loanTransactionRepository.saveAndFlush(newCreditBalanceRefundTransaction);
if (StringUtils.isNotBlank(noteText)) {
final Note note = Note.loanTransactionNote(loan, newCreditBalanceRefundTransaction, noteText);
this.noteRepository.save(note);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
recalculateAccruals(loan);
this.businessEventNotifierService.notifyBusinessEventWasExecuted(BusinessEvents.LOAN_CREDIT_BALANCE_REFUND,
constructEntityMap(BusinessEntity.LOAN_TRANSACTION, newCreditBalanceRefundTransaction));
return new CommandProcessingResultBuilder().withEntityId(newCreditBalanceRefundTransaction.getId()) //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId());
}
@Override
public LoanTransaction makeRefundForActiveLoan(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId) {
@@ -794,4 +829,5 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
}
}
}
}
@@ -43,5 +43,6 @@ public enum LoanEvent {
LOAN_CLOSED, //
LOAN_EDIT_MULTI_DISBURSE_DATE, //
LOAN_REFUND, //
LOAN_FORECLOSURE;
LOAN_FORECLOSURE, //
LOAN_CREDIT_BALANCE_REFUND;
}
@@ -297,6 +297,13 @@ public class LoanTransaction extends AbstractPersistableCustom {
return applyCharge;
}
public static LoanTransaction creditBalanceRefund(final Loan loan, final Office office, final Money amount, final LocalDate paymentDate,
final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
final PaymentDetail paymentDetail = null;
return new LoanTransaction(loan, office, LoanTransactionType.CREDIT_BALANCE_REFUND, paymentDetail, amount.getAmount(), paymentDate,
externalId, createdDate, appUser);
}
public static LoanTransaction refundForActiveLoan(final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
return new LoanTransaction(null, office, LoanTransactionType.REFUND_FOR_ACTIVE_LOAN, paymentDetail, amount.getAmount(), paymentDate,
@@ -573,6 +580,10 @@ public class LoanTransaction extends AbstractPersistableCustom {
return !isInterestWaiver() && !isChargesWaiver();
}
public boolean isNotCreditBalanceRefund() {
return !isCreditBalanceRefund();
}
public boolean isChargePayment() {
return getTypeOf().isChargePayment() && isNotReversed();
}
@@ -616,6 +627,10 @@ public class LoanTransaction extends AbstractPersistableCustom {
return getAmount(currency).isGreaterThanZero();
}
public boolean isGreaterThanZeroAndLessThanOrEqualTo(BigDecimal totalOverpaid) {
return isNonZero() && this.amount.compareTo(totalOverpaid) <= 0;
}
public boolean isNotZero(final MonetaryCurrency currency) {
return !getAmount(currency).isZero();
}
@@ -700,6 +715,10 @@ public class LoanTransaction extends AbstractPersistableCustom {
return LoanTransactionType.REFUND.equals(getTypeOf()) && isNotReversed();
}
public boolean isCreditBalanceRefund() {
return LoanTransactionType.CREDIT_BALANCE_REFUND.equals(getTypeOf()) && isNotReversed();
}
public void updateExternalId(final String externalId) {
this.externalId = externalId;
}
@@ -51,7 +51,8 @@ public enum LoanTransactionType {
REFUND(16, "loanTransactionType.refund"), //
CHARGE_PAYMENT(17, "loanTransactionType.chargePayment"), //
REFUND_FOR_ACTIVE_LOAN(18, "loanTransactionType.refund"), //
INCOME_POSTING(19, "loanTransactionType.incomePosting");
INCOME_POSTING(19, "loanTransactionType.incomePosting"), //
CREDIT_BALANCE_REFUND(20, "loanTransactionType.creditBalanceRefund");
private final Integer value;
private final String code;
@@ -131,6 +132,9 @@ public enum LoanTransactionType {
case 19:
loanTransactionType = LoanTransactionType.INCOME_POSTING;
break;
case 20:
loanTransactionType = LoanTransactionType.CREDIT_BALANCE_REFUND;
break;
default:
loanTransactionType = LoanTransactionType.INVALID;
break;
@@ -0,0 +1,72 @@
/**
* 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 lombok.extern.slf4j.Slf4j;
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.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@RequiredArgsConstructor
@Service
@CommandType(entity = "LOAN", action = "CREDITBALANCEREFUND")
public class CreditBalanceRefundCommandHandler implements NewCommandSourceHandler {
private final LoanWritePlatformService writePlatformService;
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
try {
return this.writePlatformService.creditBalanceRefund(command.getLoanId(), command);
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve, "loan.creditBalanceRefund", "Credit Balance Refund");
return CommandProcessingResult.empty();
}
}
private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dve, final String msgType,
final String msgDescription) {
if (realCause.getMessage().contains("external_id")) {
final String externalId = command.stringValueOfParameterNamed("externalId");
throw new PlatformDataIntegrityException("error.msg." + msgType + ".duplicate.externalId",
msgDescription + " with externalId `" + externalId + "` already exists", "externalId", externalId);
}
logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.loan.charge.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) {
log.error("Error occured.", dve);
}
}
@@ -529,6 +529,9 @@ public final class LoanEventApiJsonValidator {
final String note = this.fromApiJsonHelper.extractStringNamed("note", element);
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
final String externalId = this.fromApiJsonHelper.extractStringNamed("externalId", element);
baseDataValidator.reset().parameter("externalId").value(externalId).notExceedingLengthOf(100);
validatePaymentDetails(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -128,6 +128,8 @@ public interface LoanReadPlatformService {
LoanTransactionData retrieveRefundByCashTemplate(Long loanId);
LoanTransactionData retrieveCreditBalanceRefundTemplate(Long loanId);
Collection<InterestRatePeriodData> retrieveLoanInterestRatePeriodData(LoanAccountData loan);
Collection<Long> retrieveLoanIdsWithPendingIncomePostingTransactions();
@@ -2118,24 +2118,38 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public LoanTransactionData retrieveRefundByCashTemplate(Long loanId) {
// TODO Auto-generated method stub
this.context.authenticatedUser();
// TODO - KW - OPTIMIZE - write simple sql query to fetch back date of
// possible next transaction date.
final Collection<PaymentTypeData> paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
final MonetaryCurrency currency = loan.getCurrency();
return retrieveRefundTemplate(loanId, LoanTransactionType.REFUND_FOR_ACTIVE_LOAN, paymentOptions, loan.getCurrency(),
retrieveTotalPaidInAdvance(loan.getId()).getPaidInAdvance(), loan.getNetDisbursalAmount());
}
@Override
public LoanTransactionData retrieveCreditBalanceRefundTemplate(Long loanId) {
this.context.authenticatedUser();
final Collection<PaymentTypeData> paymentOptions = null;
final BigDecimal netDisbursal = null;
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
return retrieveRefundTemplate(loanId, LoanTransactionType.CREDIT_BALANCE_REFUND, paymentOptions, loan.getCurrency(),
loan.getTotalOverpaid(), netDisbursal);
}
private LoanTransactionData retrieveRefundTemplate(Long loanId, LoanTransactionType loanTransactionType,
Collection<PaymentTypeData> paymentOptions, MonetaryCurrency currency, BigDecimal transactionAmount, BigDecimal netDisbursal) {
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
final CurrencyData currencyData = applicationCurrency.toData();
final LocalDate earliestUnpaidInstallmentDate = LocalDate.now(DateUtils.getDateTimeZoneOfTenant());
final LocalDate currentDate = LocalDate.now(DateUtils.getDateTimeZoneOfTenant());
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN);
final Collection<PaymentTypeData> paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
retrieveTotalPaidInAdvance(loan.getId()).getPaidInAdvance(), null, loan.getNetDisbursalAmount(), null, null, null, null,
null, paymentOptions, null, null, null, null, false);
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(loanTransactionType);
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, currentDate, transactionAmount, null,
netDisbursal, null, null, null, null, null, paymentOptions, null, null, null, null, false);
}
@Override
@@ -116,4 +116,6 @@ public interface LoanWritePlatformService {
CommandProcessingResult makeGLIMLoanRepayment(Long loanId, JsonCommand command);
CommandProcessingResult creditBalanceRefund(Long loanId, JsonCommand command);
}
@@ -3213,10 +3213,40 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return installments;
}
@Override
public CommandProcessingResult creditBalanceRefund(Long loanId, JsonCommand command) {
this.loanEventApiJsonValidator.validateNewRefundTransaction(command.json());
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
final String noteText = command.stringValueOfParameterNamedAllowingNull("note");
final String externalId = command.stringValueOfParameterNamedAllowingNull("externalId");
final Map<String, Object> changes = new LinkedHashMap<>();
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount"));
changes.put("locale", command.locale());
changes.put("dateFormat", command.dateFormat());
if (StringUtils.isNotBlank(noteText)) {
changes.put("note", noteText);
}
if (StringUtils.isNotBlank(externalId)) {
changes.put("externalId", externalId);
}
final CommandProcessingResultBuilder commandProcessingResultBuilder = this.loanAccountDomainService.creditBalanceRefund(loanId,
transactionDate, transactionAmount, noteText, externalId);
return commandProcessingResultBuilder //
.withCommandId(command.commandId()).with(changes) //
.build();
}
@Override
@Transactional
public CommandProcessingResult makeLoanRefund(Long loanId, JsonCommand command) {
// TODO Auto-generated method stub
this.loanEventApiJsonValidator.validateNewRefundTransaction(command.json());
@@ -3400,4 +3430,5 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
}
@@ -411,6 +411,10 @@ public final class LoanEnumerations {
optionData = new LoanTransactionEnumData(LoanTransactionType.INCOME_POSTING.getValue().longValue(),
LoanTransactionType.INCOME_POSTING.getCode(), "Income Posting");
break;
case CREDIT_BALANCE_REFUND:
optionData = new LoanTransactionEnumData(LoanTransactionType.CREDIT_BALANCE_REFUND.getValue().longValue(),
LoanTransactionType.CREDIT_BALANCE_REFUND.getCode(), "Credit Balance Refund");
break;
}
return optionData;
}