diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java index 9de490cc5..320ff34fc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java @@ -73,9 +73,9 @@ public final class AccountingConstants { ***/ public enum AccrualAccountsForLoan { - FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF( - 6), INTEREST_RECEIVABLE( - 7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12); + FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), // + LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE(7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), // + TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12); private final Integer value; diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java index 330651ac8..537d906ea 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java @@ -61,9 +61,10 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } /*** - * Handle repayments, repayments at disbursement and reversal of Repayments and Repayments at disbursement + * Handle repayments, loan refunds, repayments at disbursement and reversal of Repayments and Repayments at + * disbursement ***/ - else if (loanTransactionDTO.getTransactionType().isRepayment() + else if (loanTransactionDTO.getTransactionType().isRepaymentType() || loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement() || loanTransactionDTO.getTransactionType().isChargePayment()) { createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, false, @@ -293,9 +294,16 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); } else { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, - AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, - transactionDate, totalDebitAmount, isReversal); + if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { + this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, + AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, + transactionDate, totalDebitAmount, isReversal); + + } else { + this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, + AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, + transactionDate, totalDebitAmount, isReversal); + } } } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index 775cd84e1..5a2b9239a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -797,6 +797,33 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder loanMerchantIssuedRefundTransaction(final Long loanId) { + this.actionName = "MERCHANTISSUEDREFUND"; + this.entityName = "LOAN"; + this.entityId = null; + this.loanId = loanId; + this.href = "/loans/" + loanId + "/transactions/template?command=merchantissuedrefund"; + return this; + } + + public CommandWrapperBuilder loanPayoutRefundTransaction(final Long loanId) { + this.actionName = "PAYOUTREFUND"; + this.entityName = "LOAN"; + this.entityId = null; + this.loanId = loanId; + this.href = "/loans/" + loanId + "/transactions/template?command=payoutrefund"; + return this; + } + + public CommandWrapperBuilder loanGoodwillCreditTransaction(final Long loanId) { + this.actionName = "GOODWILLCREDIT"; + this.entityName = "LOAN"; + this.entityId = null; + this.loanId = loanId; + this.href = "/loans/" + loanId + "/transactions/template?command=goodwillcredit"; + return this; + } + public CommandWrapperBuilder loanRecoveryPaymentTransaction(final Long loanId) { this.actionName = "RECOVERYPAYMENT"; this.entityName = "LOAN"; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java index 98b2162dd..4bfc81c18 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java @@ -177,8 +177,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer final Boolean isHolidayValidationDone = false; final HolidayDetailDTO holidayDetailDto = null; final boolean isRecoveryRepayment = false; - final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, - new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null, + final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, + toLoanAccount, new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null, isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone); final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command, @@ -340,10 +340,10 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer final boolean isRecoveryRepayment = false; final Boolean isHolidayValidationDone = false; final HolidayDetailDTO holidayDetailDto = null; - loanTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(), - accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), null, null, isRecoveryRepayment, isAccountTransfer, holidayDetailDto, - isHolidayValidationDone); + loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount, + new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(), + accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), null, null, isRecoveryRepayment, + isAccountTransfer, holidayDetailDto, isHolidayValidationDone); } accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO, fromSavingsAccount, @@ -476,9 +476,9 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), accountTransferDTO.getNoteText(), accountTransferDTO.getTxnExternalId(), true); - LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(), - accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), - null, null, false, isAccountTransfer, null, false, true); + LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount, + new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), null, null, false, isAccountTransfer, null, false, true); AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToLoanTransfer(accountTransferDTO, fromLoanAccount, toLoanAccount, disburseTransaction, repayTransaction); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java index 4c4af1cf1..e7799abbd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java @@ -33,6 +33,10 @@ public class BusinessEventNotificationConstants { LOAN_UNDO_TRANSACTION("loan_undo_transaction"), // LOAN_ADJUST_TRANSACTION("loan_adjust_transaction"), // LOAN_MAKE_REPAYMENT("loan_repayment_transaction"), // + LOAN_MERCHANT_ISSUED_REFUND("loan_merchant_issued_refund"), // + LOAN_PAYOUT_REFUND("loan_payout_refund"), // + LOAN_GOODWILL_CREDIT("loan_goodwill_credit"), // + LOAN_RECOVERY_PAYMENT("loan_recovery_payment"), // LOAN_WRITTEN_OFF("loan_writtenoff"), // LOAN_UNDO_WRITTEN_OFF("loan_undo_writtenoff"), // LOAN_DISBURSAL("loan_disbursal"), // 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 646ff12de..985566905 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 @@ -57,6 +57,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData; import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService; import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; @@ -110,12 +111,14 @@ public class LoanTransactionsApiResource { @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Retrieve Loan Transaction Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of:\n" + "\n" + "Field Defaults\n" + "Allowed Value Lists\n\n" + "Example Requests:\n" + "\n" - + "loans/1/transactions/template?command=repayment" + "\n" + "loans/1/transactions/template?command=waiveinterest" + "\n" - + "loans/1/transactions/template?command=writeoff" + "\n" + "loans/1/transactions/template?command=close-rescheduled" + "\n" - + "loans/1/transactions/template?command=close" + "\n" + "loans/1/transactions/template?command=disburse" + "\n" - + "loans/1/transactions/template?command=disburseToSavings" + "\n" + "loans/1/transactions/template?command=recoverypayment" - + "\n" + "loans/1/transactions/template?command=prepayLoan" + "\n" + "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=repayment" + "loans/1/transactions/template?command=merchantIssuedRefund" + + "loans/1/transactions/template?command=payoutRefund" + "loans/1/transactions/template?command=goodwillCredit" + "\n" + + "loans/1/transactions/template?command=waiveinterest" + "\n" + "loans/1/transactions/template?command=writeoff" + "\n" + + "loans/1/transactions/template?command=close-rescheduled" + "\n" + "loans/1/transactions/template?command=close" + "\n" + + "loans/1/transactions/template?command=disburse" + "\n" + "loans/1/transactions/template?command=disburseToSavings" + "\n" + + "loans/1/transactions/template?command=recoverypayment" + "\n" + "loans/1/transactions/template?command=prepayLoan" + "\n" + + "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") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) }) @@ -130,6 +133,18 @@ public class LoanTransactionsApiResource { LoanTransactionData transactionData = null; if (is(commandParam, "repayment")) { transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(loanId); + } else if (is(commandParam, "merchantIssuedRefund")) { + LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); + transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.MERCHANT_ISSUED_REFUND, + loanId, transactionDate); + } else if (is(commandParam, "payoutRefund")) { + LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); + transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.PAYOUT_REFUND, loanId, + transactionDate); + } else if (is(commandParam, "goodwillCredit")) { + LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); + transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.GOODWILL_CREDIT, loanId, + transactionDate); } else if (is(commandParam, "waiveinterest")) { transactionData = this.loanReadPlatformService.retrieveWaiveInterestDetails(loanId); } else if (is(commandParam, "writeoff")) { @@ -156,7 +171,8 @@ public class LoanTransactionsApiResource { transactionDate = LocalDate.ofInstant(transactionDateParam.getDate("transactionDate", dateFormat, locale).toInstant(), DateUtils.getDateTimeZoneOfTenant()); } - transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanId, transactionDate); + transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanId, + transactionDate); } else if (is(commandParam, "refundbycash")) { transactionData = this.loanReadPlatformService.retrieveRefundByCashTemplate(loanId); } else if (is(commandParam, "refundbytransfer")) { @@ -209,15 +225,17 @@ public class LoanTransactionsApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Significant Loan Transactions", description = "This API covers the major loan transaction functionality\n\n" - + "Example Requests:\n" + "\n" + "loans/1/transactions/template?command=repayment" + " | Make a Repayment | \n" - + "loans/1/transactions/template?command=waiveinterest" + " | Waive Interest | \n" - + "loans/1/transactions/template?command=writeoff" + " | Write-off Loan | \n" - + "loans/1/transactions/template?command=close-rescheduled" + " | Close Rescheduled Loan | \n" - + "loans/1/transactions/template?command=close" + " | Close Loan | \n" + "loans/1/transactions/template?command=undowriteoff" - + " | Undo Loan Write-off | \n" + "loans/1/transactions/template?command=recoverypayment" + " | Make Recovery Payment | \n" - + "loans/1/transactions/template?command=refundByCash" + " | Make a Refund of an Active Loan by Cash | \n" - + "loans/1/transactions/template?command=foreclosure" + " | Foreclosure of an Active Loan | \n" - + "loans/1/transactions/template?command=creditBalanceRefund" + " | Credit Balance Refund" + " | \n") + + "Example Requests:\n" + "\n" + "loans/1/transactions?command=repayment" + " | Make a Repayment | \n" + + "loans/1/transactions?command=merchantIssuedRefund" + " | Merchant Issued Refund | \n" + + "loans/1/transactions?command=payoutRefund" + " | Payout Refund | \n" + "loans/1/transactions?command=goodwillCredit" + + " | Goodwil Credit | \n" + "loans/1/transactions?command=waiveinterest" + " | Waive Interest | \n" + + "loans/1/transactions?command=writeoff" + " | Write-off Loan | \n" + "loans/1/transactions?command=close-rescheduled" + + " | Close Rescheduled Loan | \n" + "loans/1/transactions?command=close" + " | Close Loan | \n" + + "loans/1/transactions?command=undowriteoff" + " | Undo Loan Write-off | \n" + "loans/1/transactions?command=recoverypayment" + + " | Make Recovery Payment | \n" + "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") @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))) }) @@ -231,6 +249,15 @@ public class LoanTransactionsApiResource { 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, "waiveinterest")) { final CommandWrapper commandRequest = builder.waiveInterestPortionTransaction(loanId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java index 1cf00e9e3..5d8928a75 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java @@ -31,6 +31,9 @@ public class LoanTransactionEnumData { private final boolean disbursement; private final boolean repaymentAtDisbursement; private final boolean repayment; + private final boolean merchantIssuedRefund; + private final boolean payoutRefund; + private final boolean goodwillCredit; private final boolean contra; private final boolean waiveInterest; private final boolean waiveCharges; @@ -53,6 +56,9 @@ public class LoanTransactionEnumData { this.disbursement = Long.valueOf(1).equals(this.id); this.repaymentAtDisbursement = Long.valueOf(5).equals(this.id); this.repayment = Long.valueOf(2).equals(this.id); + this.merchantIssuedRefund = Long.valueOf(21).equals(this.id); + this.payoutRefund = Long.valueOf(22).equals(this.id); + this.goodwillCredit = Long.valueOf(23).equals(this.id); this.contra = Long.valueOf(3).equals(this.id); this.waiveInterest = Long.valueOf(4).equals(this.id); this.waiveCharges = Long.valueOf(9).equals(this.id); @@ -88,7 +94,14 @@ public class LoanTransactionEnumData { * @return */ public boolean isPaymentOrReceipt() { - if (isDisbursement() || isRepayment() || isRepaymentAtDisbursement() || isRecoveryRepayment()) { + if (isDisbursement() || isRepaymentType() || isRepaymentAtDisbursement() || isRecoveryRepayment()) { + return true; + } + return false; + } + + public boolean isRepaymentType() { + if (isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit()) { return true; } return false; @@ -106,6 +119,18 @@ public class LoanTransactionEnumData { return this.repayment; } + public boolean isMerchantIssuedRefund() { + return this.merchantIssuedRefund; + } + + public boolean isPayoutRefund() { + return this.payoutRefund; + } + + public boolean isGoodwillCredit() { + return this.goodwillCredit; + } + public boolean isWaiveInterest() { return this.waiveInterest; } 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 dbf7680f2..12a804bd8 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 @@ -3179,7 +3179,7 @@ public class Loan extends AbstractPersistableCustom { addLoanTransaction(loanTransaction); } - if (loanTransaction.isNotRepayment() && loanTransaction.isNotWaiver() && loanTransaction.isNotRecoveryRepayment()) { + if (loanTransaction.isNotRepaymentType() && loanTransaction.isNotWaiver() && loanTransaction.isNotRecoveryRepayment()) { final String errorMessage = "A transaction of type repayment or recovery repayment or waiver was expected but not received."; throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.repayment.or.waiver.or.recovery.transaction", errorMessage); @@ -3502,7 +3502,7 @@ public class Loan extends AbstractPersistableCustom { final LocalDate currentTransactionDate = loanTransaction.getTransactionDate(); for (final LoanTransaction previousTransaction : loanTransactions) { - if (previousTransaction.isRepayment() && previousTransaction.isNotReversed()) { + if (previousTransaction.isRepaymentType() && previousTransaction.isNotReversed()) { if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())) { isAfterLatRepayment = false; break; @@ -3543,7 +3543,7 @@ public class Loan extends AbstractPersistableCustom { LocalDate lastTransactionDate = null; for (final LoanTransaction transaction : this.loanTransactions) { - if (transaction.isRepayment() && transaction.isNonZero()) { + if (transaction.isRepaymentType() && transaction.isNonZero()) { lastTransactionDate = transaction.getTransactionDate(); } } @@ -3615,7 +3615,7 @@ public class Loan extends AbstractPersistableCustom { validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, transactionForAdjustment.getTransactionDate()); - if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver() + if (transactionForAdjustment.isNotRepaymentType() && transactionForAdjustment.isNotWaiver() && transactionForAdjustment.isNotCreditBalanceRefund()) { final String errorMessage = "Only transactions of type repayment, waiver or credit balance refund can be adjusted."; throw new InvalidLoanTransactionTypeException("transaction", @@ -3635,7 +3635,7 @@ public class Loan extends AbstractPersistableCustom { this.loanStatus = LoanStatus.ACTIVE.getValue(); } - if (newTransactionDetail.isRepayment() || newTransactionDetail.isInterestWaiver()) { + if (newTransactionDetail.isRepaymentType() || newTransactionDetail.isInterestWaiver()) { changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment, scheduleGeneratorDTO, currentUser); } @@ -4139,7 +4139,7 @@ public class Loan extends AbstractPersistableCustom { Money cumulativePaid = Money.zero(loanCurrency()); for (final LoanTransaction repayment : this.loanTransactions) { - if (repayment.isRepayment() && !repayment.isReversed()) { + if (repayment.isRepaymentType() && !repayment.isReversed()) { cumulativePaid = cumulativePaid.plus(repayment.getAmount(loanCurrency())); } } @@ -4464,7 +4464,7 @@ public class Loan extends AbstractPersistableCustom { && !transaction.getTransactionDate().isAfter(tillDate)) { if (transaction.isAccrual()) { receivableInterest = receivableInterest.plus(transaction.getInterestPortion(getCurrency())); - } else if (transaction.isRepayment() || transaction.isInterestWaiver()) { + } else if (transaction.isRepaymentType() || transaction.isInterestWaiver()) { receivableInterest = receivableInterest.minus(transaction.getInterestPortion(getCurrency())); } } @@ -4953,7 +4953,7 @@ public class Loan extends AbstractPersistableCustom { public LocalDate getLastRepaymentDate() { LocalDate currentTransactionDate = getDisbursementDate(); for (final LoanTransaction previousTransaction : this.loanTransactions) { - if (previousTransaction.isRepayment()) { + if (previousTransaction.isRepaymentType()) { if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())) { currentTransactionDate = previousTransaction.getTransactionDate(); } @@ -5063,7 +5063,7 @@ public class Loan extends AbstractPersistableCustom { break; case LOAN_REPAYMENT_OR_WAIVER: if (!isOpen()) { - final String defaultUserMessage = "Loan Repayment or Waiver is not allowed. Loan Account is not active."; + final String defaultUserMessage = "Loan Repayment (or its types) or Waiver is not allowed. Loan Account is not active."; final ApiParameterError error = ApiParameterError .generalError("error.msg.loan.repayment.or.waiver.account.is.not.active", defaultUserMessage); dataValidationErrors.add(error); @@ -6223,7 +6223,7 @@ public class Loan extends AbstractPersistableCustom { LocalDate lastTransactionDate = null; for (final LoanTransaction transaction : this.loanTransactions) { - if ((transaction.isRepayment() || transaction.isRefundForActiveLoan() || transaction.isCreditBalanceRefund()) + if ((transaction.isRepaymentType() || transaction.isRefundForActiveLoan() || transaction.isCreditBalanceRefund()) && transaction.isNonZero() && transaction.isNotReversed()) { lastTransactionDate = transaction.getTransactionDate(); } @@ -6268,7 +6268,7 @@ public class Loan extends AbstractPersistableCustom { Collections.reverse(loanTransactions); for (final LoanTransaction previousTransaction : loanTransactions) { if (lastTransactionDate.isBefore(previousTransaction.getTransactionDate())) { - if (previousTransaction.isRepayment() || previousTransaction.isWaiver() || previousTransaction.isChargePayment()) { + if (previousTransaction.isRepaymentType() || previousTransaction.isWaiver() || previousTransaction.isChargePayment()) { throw new UndoLastTrancheDisbursementException(previousTransaction.getId()); } } @@ -6602,7 +6602,7 @@ public class Loan extends AbstractPersistableCustom { receivableInterest = receivableInterest.plus(transaction.getInterestPortion(currency)); receivableFee = receivableFee.plus(transaction.getFeeChargesPortion(currency)); receivablePenalty = receivablePenalty.plus(transaction.getPenaltyChargesPortion(currency)); - } else if (transaction.isRepayment() || transaction.isChargePayment()) { + } else if (transaction.isRepaymentType() || transaction.isChargePayment()) { receivableInterest = receivableInterest.minus(transaction.getInterestPortion(currency)); receivableFee = receivableFee.minus(transaction.getFeeChargesPortion(currency)); receivablePenalty = receivablePenalty.minus(transaction.getPenaltyChargesPortion(currency)); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java index 29819a3e2..35cef98b4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java @@ -28,9 +28,9 @@ import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; public interface LoanAccountDomainService { - LoanTransaction makeRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate, - BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId, boolean isRecoveryRepayment, - boolean isAccountTransfer, HolidayDetailDTO holidatDetailDto, Boolean isHolidayValidationDone); + LoanTransaction makeRepayment(LoanTransactionType repaymentTransactionType, Loan loan, CommandProcessingResultBuilder builderResult, + LocalDate transactionDate, BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId, + boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidatDetailDto, Boolean isHolidayValidationDone); LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate, BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId); @@ -61,9 +61,10 @@ public interface LoanAccountDomainService { */ void recalculateAccruals(Loan loan); - LoanTransaction makeRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate, - BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId, boolean isRecoveryRepayment, - boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone, boolean isLoanToLoanTransfer); + LoanTransaction makeRepayment(LoanTransactionType repaymentTransactionType, Loan loan, CommandProcessingResultBuilder builderResult, + LocalDate transactionDate, BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId, + boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone, + boolean isLoanToLoanTransfer); void saveLoanWithDataIntegrityViolationChecks(Loan loan); 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 cf6c62775..285b6f8d8 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 @@ -146,12 +146,12 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { @Transactional @Override - public LoanTransaction makeRepayment(final Loan loan, final CommandProcessingResultBuilder builderResult, - final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, - final String txnExternalId, final boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, - Boolean isHolidayValidationDone) { - return makeRepayment(loan, builderResult, transactionDate, transactionAmount, paymentDetail, noteText, txnExternalId, - isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone, false); + public LoanTransaction makeRepayment(final LoanTransactionType repaymentTransactionType, final Loan loan, + final CommandProcessingResultBuilder builderResult, final LocalDate transactionDate, final BigDecimal transactionAmount, + final PaymentDetail paymentDetail, final String noteText, final String txnExternalId, final boolean isRecoveryRepayment, + boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone) { + return makeRepayment(repaymentTransactionType, loan, builderResult, transactionDate, transactionAmount, paymentDetail, noteText, + txnExternalId, isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone, false); } @Transactional @@ -171,13 +171,16 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { @Transactional @Override - public LoanTransaction makeRepayment(final Loan loan, final CommandProcessingResultBuilder builderResult, - final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, - final String txnExternalId, final boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, - Boolean isHolidayValidationDone, final boolean isLoanToLoanTransfer) { + public LoanTransaction makeRepayment(final LoanTransactionType repaymentTransactionType, final Loan loan, + final CommandProcessingResultBuilder builderResult, final LocalDate transactionDate, final BigDecimal transactionAmount, + final PaymentDetail paymentDetail, final String noteText, final String txnExternalId, final boolean isRecoveryRepayment, + boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone, + final boolean isLoanToLoanTransfer) { AppUser currentUser = getAppUserIfPresent(); checkClientOrGroupActive(loan); - this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BusinessEvents.LOAN_MAKE_REPAYMENT, + + BusinessEvents repaymentTypeEvent = getRepaymentTypeBusinessEvent(repaymentTransactionType, isRecoveryRepayment); + this.businessEventNotifierService.notifyBusinessEventToBeExecuted(repaymentTypeEvent, constructEntityMap(BusinessEntity.LOAN, loan)); // TODO: Is it required to validate transaction date with meeting dates @@ -199,8 +202,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { newRepaymentTransaction = LoanTransaction.recoveryRepayment(loan.getOffice(), repaymentAmount, paymentDetail, transactionDate, txnExternalId, currentDateTime, currentUser); } else { - newRepaymentTransaction = LoanTransaction.repayment(loan.getOffice(), repaymentAmount, paymentDetail, transactionDate, - txnExternalId, currentDateTime, currentUser); + newRepaymentTransaction = LoanTransaction.repaymentType(repaymentTransactionType, loan.getOffice(), repaymentAmount, + paymentDetail, transactionDate, txnExternalId, currentDateTime, currentUser); } LocalDate recalculateFrom = null; @@ -242,7 +245,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { recalculateAccruals(loan); - this.businessEventNotifierService.notifyBusinessEventWasExecuted(BusinessEvents.LOAN_MAKE_REPAYMENT, + this.businessEventNotifierService.notifyBusinessEventWasExecuted(repaymentTypeEvent, constructEntityMap(BusinessEntity.LOAN_TRANSACTION, newRepaymentTransaction)); // disable all active standing orders linked to this loan if status @@ -284,6 +287,22 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { return newRepaymentTransaction; } + private BusinessEvents getRepaymentTypeBusinessEvent(LoanTransactionType repaymentTransactionType, boolean isRecoveryRepayment) { + BusinessEvents repaymentTypeEvent = null; + if (repaymentTransactionType.isRepayment()) { + repaymentTypeEvent = BusinessEvents.LOAN_MAKE_REPAYMENT; + } else if (repaymentTransactionType.isMerchantIssuedRefund()) { + repaymentTypeEvent = BusinessEvents.LOAN_MERCHANT_ISSUED_REFUND; + } else if (repaymentTransactionType.isPayoutRefund()) { + repaymentTypeEvent = BusinessEvents.LOAN_PAYOUT_REFUND; + } else if (repaymentTransactionType.isGoodwillCredit()) { + repaymentTypeEvent = BusinessEvents.LOAN_GOODWILL_CREDIT; + } else if (isRecoveryRepayment) { + repaymentTypeEvent = BusinessEvents.LOAN_RECOVERY_PAYMENT; + } + return repaymentTypeEvent; + } + private void saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) { try { this.loanTransactionRepository.saveAndFlush(newRepaymentTransaction); 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 351566a2f..dd52ed73d 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 @@ -169,6 +169,13 @@ public class LoanTransaction extends AbstractPersistableCustom { createdDate, appUser); } + public static LoanTransaction repaymentType(final LoanTransactionType repaymentType, 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, repaymentType, paymentDetail, amount.getAmount(), paymentDate, externalId, createdDate, + appUser); + } + public void setLoanTransactionToRepaymentScheduleMappings(final Integer installmentId, final BigDecimal chargePerInstallment) { for (LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping : this.loanTransactionToRepaymentScheduleMappings) { final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loanTransactionToRepaymentScheduleMapping @@ -524,16 +531,28 @@ public class LoanTransaction extends AbstractPersistableCustom { this.manuallyAdjustedOrReversed = true; } - public boolean isAnyTypeOfRepayment() { - return isRepayment() || isRepaymentAtDisbursement() || isRecoveryRepayment(); + public boolean isRepaymentType() { + return isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit(); } public boolean isRepayment() { return LoanTransactionType.REPAYMENT.equals(getTypeOf()) && isNotReversed(); } - public boolean isNotRepayment() { - return !isRepayment(); + public boolean isMerchantIssuedRefund() { + return LoanTransactionType.MERCHANT_ISSUED_REFUND.equals(getTypeOf()) && isNotReversed(); + } + + public boolean isPayoutRefund() { + return LoanTransactionType.PAYOUT_REFUND.equals(getTypeOf()) && isNotReversed(); + } + + public boolean isGoodwillCredit() { + return LoanTransactionType.GOODWILL_CREDIT.equals(getTypeOf()) && isNotReversed(); + } + + public boolean isNotRepaymentType() { + return !isRepaymentType(); } public boolean isIncomePosting() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java index c38bda6be..e05d97262 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java @@ -52,7 +52,10 @@ public enum LoanTransactionType { CHARGE_PAYMENT(17, "loanTransactionType.chargePayment"), // REFUND_FOR_ACTIVE_LOAN(18, "loanTransactionType.refund"), // INCOME_POSTING(19, "loanTransactionType.incomePosting"), // - CREDIT_BALANCE_REFUND(20, "loanTransactionType.creditBalanceRefund"); + CREDIT_BALANCE_REFUND(20, "loanTransactionType.creditBalanceRefund"), // + MERCHANT_ISSUED_REFUND(21, "loanTransactionType.merchantIssuedRefund"), // + PAYOUT_REFUND(22, "loanTransactionType.payoutRefund"), // + GOODWILL_CREDIT(23, "loanTransactionType.goodwillCredit"); private final Integer value; private final String code; @@ -135,6 +138,15 @@ public enum LoanTransactionType { case 20: loanTransactionType = LoanTransactionType.CREDIT_BALANCE_REFUND; break; + case 21: + loanTransactionType = LoanTransactionType.MERCHANT_ISSUED_REFUND; + break; + case 22: + loanTransactionType = LoanTransactionType.PAYOUT_REFUND; + break; + case 23: + loanTransactionType = LoanTransactionType.GOODWILL_CREDIT; + break; default: loanTransactionType = LoanTransactionType.INVALID; break; @@ -154,6 +166,22 @@ public enum LoanTransactionType { return this.value.equals(LoanTransactionType.REPAYMENT.getValue()); } + public boolean isMerchantIssuedRefund() { + return this.value.equals(LoanTransactionType.MERCHANT_ISSUED_REFUND.getValue()); + } + + public boolean isPayoutRefund() { + return this.value.equals(LoanTransactionType.PAYOUT_REFUND.getValue()); + } + + public boolean isGoodwillCredit() { + return this.value.equals(LoanTransactionType.GOODWILL_CREDIT.getValue()); + } + + public boolean isRepaymentType() { + return (isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit()); + } + public boolean isRecoveryRepayment() { return this.value.equals(LoanTransactionType.RECOVERY_REPAYMENT.getValue()); } 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 8c95c2b2a..224cc7ca9 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 @@ -152,7 +152,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen Collections.sort(installments, byDate); } - if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { + if (loanTransaction.isRepaymentType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { // pass through for new transactions if (loanTransaction.getId() == null) { handleTransaction(loanTransaction, currency, installments, charges); @@ -230,7 +230,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen final List installments, final Set charges, final Money chargeAmountToProcess, final boolean isFeeCharge) { // to. - if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { + if (loanTransaction.isRepaymentType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { loanTransaction.resetDerivedComponents(); } Money transactionAmountUnprocessed = processTransaction(loanTransaction, currency, installments, chargeAmountToProcess); @@ -488,7 +488,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen Money unProcessed = Money.zero(currency); for (final LoanTransaction loanTransaction : transactionsPostDisbursement) { Money amountToProcess = null; - if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { + if (loanTransaction.isRepaymentType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { loanTransaction.resetDerivedComponents(); } if (loanTransaction.isInterestWaiver()) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java index 214827055..07c161bdb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java @@ -18,24 +18,20 @@ */ package org.apache.fineract.portfolio.loanaccount.handler; +import lombok.RequiredArgsConstructor; import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor public class AddAndDeleteLoanDisburseDetailsCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public AddAndDeleteLoanDisburseDetailsCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddLoanChargeCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddLoanChargeCommandHandler.java index b93d90c76..4c4987b4c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddLoanChargeCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddLoanChargeCommandHandler.java @@ -18,33 +18,27 @@ */ 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.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.orm.jpa.JpaSystemException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor +@Slf4j @CommandType(entity = "LOANCHARGE", action = "CREATE") public class AddLoanChargeCommandHandler implements NewCommandSourceHandler { - private static final Logger LOG = LoggerFactory.getLogger(AddLoanChargeCommandHandler.class); - private final LoanWritePlatformService writePlatformService; - @Autowired - public AddLoanChargeCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { @@ -71,7 +65,7 @@ public class AddLoanChargeCommandHandler implements NewCommandSourceHandler { } private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) { - LOG.error("Error occured.", dve); + log.error("Error occured.", dve); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/BulkUpdateLoanOfficerCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/BulkUpdateLoanOfficerCommandHandler.java index 017b354d5..aac2d9dbc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/BulkUpdateLoanOfficerCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/BulkUpdateLoanOfficerCommandHandler.java @@ -18,26 +18,22 @@ */ 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.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor @CommandType(entity = "LOAN", action = "BULKREASSIGN") public class BulkUpdateLoanOfficerCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public BulkUpdateLoanOfficerCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java index af1ed6c15..d45b62f25 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java @@ -18,26 +18,22 @@ */ 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.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor @CommandType(entity = "LOAN", action = "CLOSEASRESCHEDULED") public class CloseLoanAsRescheduledCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public CloseLoanAsRescheduledCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanCommandHandler.java index 2460bde85..c95416c65 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanCommandHandler.java @@ -18,26 +18,22 @@ */ 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.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor @CommandType(entity = "LOAN", action = "CLOSE") public class CloseLoanCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public CloseLoanCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CreditBalanceRefundCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CreditBalanceRefundCommandHandler.java index 798290c51..4253ee231 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CreditBalanceRefundCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CreditBalanceRefundCommandHandler.java @@ -31,9 +31,9 @@ import org.springframework.orm.jpa.JpaSystemException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -@Slf4j -@RequiredArgsConstructor @Service +@RequiredArgsConstructor +@Slf4j @CommandType(entity = "LOAN", action = "CREDITBALANCEREFUND") public class CreditBalanceRefundCommandHandler implements NewCommandSourceHandler { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java index 98066bea5..b9b61e805 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java @@ -18,26 +18,22 @@ */ 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.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor @CommandType(entity = "LOANCHARGE", action = "DELETE") public class DeleteLoanChargeCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public DeleteLoanChargeCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java index e6acaf692..a90183f79 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java @@ -18,26 +18,22 @@ */ 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.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor @CommandType(entity = "LOAN", action = "DISBURSE") public class DisburseLoanCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public DisburseLoanCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanToSavingsCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanToSavingsCommandHandler.java index 04fff7c9b..762b7c389 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanToSavingsCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanToSavingsCommandHandler.java @@ -18,26 +18,22 @@ */ 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.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor @CommandType(entity = "LOAN", action = "DISBURSETOSAVINGS") public class DisburseLoanToSavingsCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public DisburseLoanToSavingsCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/ForeClosureCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/ForeClosureCommandHandler.java index e249dc978..caf039903 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/ForeClosureCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/ForeClosureCommandHandler.java @@ -18,25 +18,21 @@ */ 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.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service +@RequiredArgsConstructor @CommandType(entity = "LOAN", action = "FORECLOSURE") public class ForeClosureCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public ForeClosureCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Override public CommandProcessingResult processCommand(final JsonCommand command) { return writePlatformService.forecloseLoan(command.getLoanId(), command); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanGoodwilCreditCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanGoodwilCreditCommandHandler.java new file mode 100644 index 000000000..81a696edf --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanGoodwilCreditCommandHandler.java @@ -0,0 +1,45 @@ +/** + * 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.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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@CommandType(entity = "LOAN", action = "GOODWILLCREDIT") +public class LoanGoodwilCreditCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + boolean isRecoveryRepayment = false; + return this.writePlatformService.makeLoanRepayment(LoanTransactionType.GOODWILL_CREDIT, command.getLoanId(), command, + isRecoveryRepayment); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanMerchantIssuedRefundCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanMerchantIssuedRefundCommandHandler.java new file mode 100644 index 000000000..433c479a7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanMerchantIssuedRefundCommandHandler.java @@ -0,0 +1,45 @@ +/** + * 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.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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@CommandType(entity = "LOAN", action = "MERCHANTISSUEDREFUND") +public class LoanMerchantIssuedRefundCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + boolean isRecoveryRepayment = false; + return this.writePlatformService.makeLoanRepayment(LoanTransactionType.MERCHANT_ISSUED_REFUND, command.getLoanId(), command, + isRecoveryRepayment); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanPayoutRefundCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanPayoutRefundCommandHandler.java new file mode 100644 index 000000000..0f48ad6d9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanPayoutRefundCommandHandler.java @@ -0,0 +1,45 @@ +/** + * 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.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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@CommandType(entity = "LOAN", action = "PAYOUTREFUND") +public class LoanPayoutRefundCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + boolean isRecoveryRepayment = false; + return this.writePlatformService.makeLoanRepayment(LoanTransactionType.PAYOUT_REFUND, command.getLoanId(), command, + isRecoveryRepayment); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRecoveryPaymentCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRecoveryPaymentCommandHandler.java index 87aa86d73..18ada7cef 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRecoveryPaymentCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRecoveryPaymentCommandHandler.java @@ -22,6 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType; import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -40,6 +41,6 @@ public class LoanRecoveryPaymentCommandHandler implements NewCommandSourceHandle @Override public CommandProcessingResult processCommand(JsonCommand command) { final boolean isRecoveryRepayment = true; - return writePlatformService.makeLoanRepayment(command.getLoanId(), command, isRecoveryRepayment); + return writePlatformService.makeLoanRepayment(LoanTransactionType.REPAYMENT, command.getLoanId(), command, isRecoveryRepayment); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java index 4061bf6e2..3bbd62bf7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java @@ -18,30 +18,28 @@ */ 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.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.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor @CommandType(entity = "LOAN", action = "REPAYMENT") public class LoanRepaymentCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService; - @Autowired - public LoanRepaymentCommandHandler(final LoanWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Transactional @Override public CommandProcessingResult processCommand(final JsonCommand command) { boolean isRecoveryRepayment = false; - return this.writePlatformService.makeLoanRepayment(command.getLoanId(), command, isRecoveryRepayment); + return this.writePlatformService.makeLoanRepayment(LoanTransactionType.REPAYMENT, command.getLoanId(), command, + isRecoveryRepayment); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index fe9776087..45abfa493 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -112,6 +112,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTopupDetails; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException; import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted; import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified; @@ -378,8 +379,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa + " should be after last transaction date of loan to be closed " + lastUserTransactionOnLoanToClose); } - BigDecimal loanOutstanding = this.loanReadPlatformService - .retrieveLoanPrePaymentTemplate(loanIdToClose, newLoanApplication.getDisbursementDate()).getAmount(); + BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, + loanIdToClose, newLoanApplication.getDisbursementDate()).getAmount(); final BigDecimal firstDisbursalAmount = newLoanApplication.getFirstDisbursalAmount(); if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) { throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed", @@ -956,8 +957,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa + " should be after last transaction date of loan to be closed " + lastUserTransactionOnLoanToClose); } - BigDecimal loanOutstanding = this.loanReadPlatformService - .retrieveLoanPrePaymentTemplate(loanIdToClose, existingLoanApplication.getDisbursementDate()).getAmount(); + BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate( + LoanTransactionType.REPAYMENT, loanIdToClose, existingLoanApplication.getDisbursementDate()).getAmount(); final BigDecimal firstDisbursalAmount = existingLoanApplication.getFirstDisbursalAmount(); if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) { throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed", @@ -1456,7 +1457,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa + " should be after last transaction date of loan to be closed " + lastUserTransactionOnLoanToClose); } BigDecimal loanOutstanding = this.loanReadPlatformService - .retrieveLoanPrePaymentTemplate(loanIdToClose, expectedDisbursementDate).getAmount(); + .retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, expectedDisbursementDate).getAmount(); final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount(); if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) { throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed", diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java index 8618c33f0..3d81a76a1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java @@ -38,6 +38,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData; import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData; import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData; @@ -114,7 +115,7 @@ public interface LoanReadPlatformService { List fetchLoansForInterestRecalculation(Integer pageSize, Long maxLoanIdInList, String officeHierarchy); - LoanTransactionData retrieveLoanPrePaymentTemplate(Long loanId, LocalDate onDate); + LoanTransactionData retrieveLoanPrePaymentTemplate(LoanTransactionType repaymentTransactionType, Long loanId, LocalDate onDate); Collection retrieveWaiverLoanTransactions(Long loanId); 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 cb3e177ca..bf126f58d 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 @@ -445,9 +445,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } @Override - public LoanTransactionData retrieveLoanPrePaymentTemplate(final Long loanId, LocalDate onDate) { + public LoanTransactionData retrieveLoanPrePaymentTemplate(final LoanTransactionType repaymentTransactionType, final Long loanId, + LocalDate onDate) { this.context.authenticatedUser(); + this.loanUtilService.validateRepaymentTransactionType(repaymentTransactionType); final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true); loan.setHelpers(null, null, loanRepaymentScheduleTransactionProcessorFactory); @@ -461,7 +463,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final LocalDate recalculateFrom = null; final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loan.fetchPrepaymentDetail(scheduleGeneratorDTO, onDate); - final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT); + final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(repaymentTransactionType); final Collection paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes(); final BigDecimal outstandingLoanBalance = loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount(); final BigDecimal unrecognizedIncomePortion = null; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java index 0653a7218..8c810548f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java @@ -30,6 +30,7 @@ import java.util.Locale; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; +import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.organisation.holiday.domain.Holiday; import org.apache.fineract.organisation.holiday.domain.HolidayRepository; @@ -57,6 +58,7 @@ import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.springframework.beans.factory.annotation.Autowired; @@ -351,4 +353,12 @@ public class LoanUtilService { return disbursementDatas; } + public void validateRepaymentTransactionType(LoanTransactionType repaymentTransactionType) { + if (!repaymentTransactionType.isRepaymentType()) { + throw new PlatformServiceUnavailableException("error.msg.repaymentTransactionType.provided.not.a.repayment.type", + "Loan :" + repaymentTransactionType.getCode() + " Repayment Transaction Type provided is not a Repayment Type", + repaymentTransactionType.getCode()); + } + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java index 9c49ade84..83555bb24 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java @@ -32,6 +32,7 @@ import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulk import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData; public interface LoanWritePlatformService { @@ -43,7 +44,8 @@ public interface LoanWritePlatformService { CommandProcessingResult undoLoanDisbursal(Long loanId, JsonCommand command); - CommandProcessingResult makeLoanRepayment(Long loanId, JsonCommand command, boolean isRecoveryRepayment); + CommandProcessingResult makeLoanRepayment(LoanTransactionType repaymentTransactionType, Long loanId, JsonCommand command, + boolean isRecoveryRepayment); Map makeLoanBulkRepayment(CollectionSheetBulkRepaymentCommand bulkRepaymentCommand); 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 512242dee..ee5772b7a 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 @@ -488,7 +488,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } BigDecimal loanOutstanding = this.loanReadPlatformService - .retrieveLoanPrePaymentTemplate(loanIdToClose, actualDisbursementDate).getAmount(); + .retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, actualDisbursementDate).getAmount(); final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount(); if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) { throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed", @@ -886,7 +886,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate expectedDisbursementDate = command .localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName); BigDecimal loanOutstanding = this.loanReadPlatformService - .retrieveLoanPrePaymentTemplate(loanIdToClose, expectedDisbursementDate).getAmount(); + .retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, expectedDisbursementDate).getAmount(); BigDecimal netDisbursalAmount = loan.getApprovedPrincipal().subtract(loanOutstanding); loan.adjustNetDisbursalAmount(netDisbursalAmount); } @@ -941,15 +941,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf int j = 0; for (JsonElement element : repayments) { childCommand = JsonCommand.fromExistingCommand(command, element); - result = makeLoanRepayment(childLoanId[j++], childCommand, false); + result = makeLoanRepayment(LoanTransactionType.REPAYMENT, childLoanId[j++], childCommand, false); } return result; } @Transactional @Override - public CommandProcessingResult makeLoanRepayment(final Long loanId, final JsonCommand command, final boolean isRecoveryRepayment) { + public CommandProcessingResult makeLoanRepayment(final LoanTransactionType repaymentTransactionType, final Long loanId, + final JsonCommand command, final boolean isRecoveryRepayment) { + this.loanUtilService.validateRepaymentTransactionType(repaymentTransactionType); this.loanEventApiJsonValidator.validateNewRepaymentTransaction(command.json()); final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); @@ -973,9 +975,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final HolidayDetailDTO holidayDetailDto = null; boolean isAccountTransfer = false; final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder(); - LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(loan, commandProcessingResultBuilder, transactionDate, - transactionAmount, paymentDetail, noteText, txnExternalId, isRecoveryRepayment, isAccountTransfer, holidayDetailDto, - isHolidayValidationDone); + LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(repaymentTransactionType, loan, + commandProcessingResultBuilder, transactionDate, transactionAmount, paymentDetail, noteText, txnExternalId, + isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone); // Update loan transaction on repayment. if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) { @@ -1044,10 +1046,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail); } final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder(); - LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(loan, commandProcessingResultBuilder, - bulkRepaymentCommand.getTransactionDate(), singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail, - bulkRepaymentCommand.getNote(), null, isRecoveryRepayment, isAccountTransfer, holidayDetailDTO, - isHolidayValidationDone); + LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, loan, + commandProcessingResultBuilder, bulkRepaymentCommand.getTransactionDate(), + singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail, bulkRepaymentCommand.getNote(), null, + isRecoveryRepayment, isAccountTransfer, holidayDetailDTO, isHolidayValidationDone); transactionIds.add(loanTransaction.getId()); } } @@ -1183,7 +1185,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanAccountDomainService.recalculateAccruals(loan); Map entityMap = constructEntityMap(BusinessEntity.LOAN_ADJUSTED_TRANSACTION, transactionToAdjust); - if (newTransactionDetail.isRepayment() && newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { + if (newTransactionDetail.isRepaymentType() && newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { entityMap.put(BusinessEntity.LOAN_TRANSACTION, newTransactionDetail); } this.businessEventNotifierService.notifyBusinessEventWasExecuted(BusinessEvents.LOAN_ADJUST_TRANSACTION, entityMap); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java index 56faedcba..0407304a2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java @@ -415,6 +415,18 @@ public final class LoanEnumerations { optionData = new LoanTransactionEnumData(LoanTransactionType.CREDIT_BALANCE_REFUND.getValue().longValue(), LoanTransactionType.CREDIT_BALANCE_REFUND.getCode(), "Credit Balance Refund"); break; + case MERCHANT_ISSUED_REFUND: + optionData = new LoanTransactionEnumData(LoanTransactionType.MERCHANT_ISSUED_REFUND.getValue().longValue(), + LoanTransactionType.MERCHANT_ISSUED_REFUND.getCode(), "Merchant Issued Refund"); + break; + case PAYOUT_REFUND: + optionData = new LoanTransactionEnumData(LoanTransactionType.PAYOUT_REFUND.getValue().longValue(), + LoanTransactionType.PAYOUT_REFUND.getCode(), "Payout Refund"); + break; + case GOODWILL_CREDIT: + optionData = new LoanTransactionEnumData(LoanTransactionType.GOODWILL_CREDIT.getValue().longValue(), + LoanTransactionType.GOODWILL_CREDIT.getCode(), "Goodwill Credit"); + break; } return optionData; } 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 e871a7f94..0eed83638 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 @@ -31,4 +31,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0012_add_merchantissuedrefund_payoutrefund_goodwillcredit_permissions.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0012_add_merchantissuedrefund_payoutrefund_goodwillcredit_permissions.xml new file mode 100644 index 000000000..f8706a844 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0012_add_merchantissuedrefund_payoutrefund_goodwillcredit_permissions.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java similarity index 84% rename from integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundIntegrationTest.java rename to integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java index 91959773e..61508badb 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java @@ -45,9 +45,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SuppressWarnings({ "rawtypes", "unchecked" }) -public class ClientLoanCreditBalanceRefundIntegrationTest { +public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest { - private static final Logger LOG = LoggerFactory.getLogger(ClientLoanCreditBalanceRefundIntegrationTest.class); + private static final Logger LOG = LoggerFactory.getLogger(ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.class); private ResponseSpecification responseSpec; private RequestSpecification requestSpec; @@ -56,11 +56,15 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { private JournalEntryHelper journalEntryHelper; private AccountHelper accountHelper; private Integer disbursedLoanID; + private static final String CASH_BASED = "2"; private static final String ACCRUAL_PERIODIC = "3"; private Account assetAccount; private Account incomeAccount; private Account expenseAccount; private Account overpaymentAccount; + private static final String MERCHANT_ISSUED_REFUND = "merchantIssuedRefund"; + private static final String PAYOUT_REFUND = "payoutRefund"; + private static final String GOODWILL_CREDIT = "goodwillCredit"; @BeforeEach public void setup() { @@ -76,12 +80,13 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { this.expenseAccount = this.accountHelper.createExpenseAccount(); this.overpaymentAccount = this.accountHelper.createLiabilityAccount(); this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, this.responseSpec); + } + private void disburseLoanOfAccountingRule(final String accountingType) { final String principal = "12000.00"; final String submitApproveDisburseDate = "01 January 2022"; this.disbursedLoanID = fromStartToDisburseLoan(submitApproveDisburseDate, principal, ACCRUAL_PERIODIC, assetAccount, incomeAccount, expenseAccount, overpaymentAccount); - } private Integer createLoanProduct(final String principal, final boolean multiDisburseLoan, final String accountingRule, @@ -162,6 +167,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { @Test public void creditBalanceRefundCanOnlyBeAppliedWhereLoanStatusIsOverpaidTest() { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); HashMap loanStatusHashMap = makeRepayment("06 January 2022", 2000.00f); // not full payment LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap); @@ -174,14 +180,11 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { assertEquals("error.msg.loan.credit.balance.refund.account.is.not.overpaid", cbrErrors.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); - // ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, - // this.responseSpec, loanID); - // final int loanScheduleLineCount = loanSchedule.size(); - } @Test public void cantRefundMoreThanOverpaidTest() { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); @@ -204,6 +207,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { @Test public void fullRefundChangesStatusToClosedObligationMetTest() { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); @@ -229,6 +233,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { @Test public void partialRefundKeepsOverpaidStatusTest() { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); @@ -246,6 +251,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { @Test public void newCreditBalanceRefundSavesExternalIdTest() { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); @@ -265,6 +271,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { @Test public void newCreditBalanceRefundFindsDuplicateExternalIdTest() { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); @@ -287,6 +294,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { @Test public void newCreditBalanceRefundCreatesCorrectJournalEntriesForPeriodicAccrualsTest() { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); @@ -304,4 +312,45 @@ public class ClientLoanCreditBalanceRefundIntegrationTest { } + @Test + public void newCreditBalanceRefundCreatesCorrectJournalEntriesForCashAccrualsTest() { + + disburseLoanOfAccountingRule(CASH_BASED); + HashMap loanStatusHashMap = makeRepayment("08 January 2022", 20000.00f); // overpayment + LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); + + final Float refund = 1000.00f; // partial refund + final String creditBalanceRefundDate = "09 January 2022"; + final String externalId = null; + final Integer resourceId = (Integer) loanTransactionHelper.creditBalanceRefund(creditBalanceRefundDate, refund, externalId, + disbursedLoanID, "resourceId"); + Assertions.assertNotNull(resourceId); + + this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, creditBalanceRefundDate, + new JournalEntry(refund, JournalEntry.TransactionType.DEBIT)); + this.journalEntryHelper.checkJournalEntryForLiabilityAccount(overpaymentAccount, creditBalanceRefundDate, + new JournalEntry(refund, JournalEntry.TransactionType.CREDIT)); + + } + + @Test + public void repaymentTransactionTypeMatchesTest() { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); + verifyRepaymentTransactionTypeMatches(MERCHANT_ISSUED_REFUND); + verifyRepaymentTransactionTypeMatches(PAYOUT_REFUND); + verifyRepaymentTransactionTypeMatches(GOODWILL_CREDIT); + + } + + private void verifyRepaymentTransactionTypeMatches(final String repaymentTransactionType) { + HashMap loanStatusHashMap = this.loanTransactionHelper.makeRepaymentTypePayment(repaymentTransactionType, "06 January 2022", + 200.00f, this.disbursedLoanID); + Integer newTransactionId = (Integer) loanStatusHashMap.get("resourceId"); + loanStatusHashMap = this.loanTransactionHelper.getLoanTransactionDetails(this.disbursedLoanID, newTransactionId); + + HashMap typeMap = (HashMap) loanStatusHashMap.get("type"); + Boolean isTypeCorrect = (Boolean) typeMap.get(repaymentTransactionType); + Assertions.assertTrue(Boolean.TRUE.equals(isTypeCorrect), "Not " + repaymentTransactionType); + } + } 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 50942ac27..85527944d 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 @@ -339,6 +339,12 @@ public class LoanTransactionHelper { getCreditBalanceRefundBodyAsJSON(date, amountToBePaid, externalId), jsonAttributeToGetback); } + public HashMap makeRepaymentTypePayment(final String repaymentTypeCommand, final String date, final Float amountToBePaid, + final Integer loanID) { + return (HashMap) performLoanTransaction(createLoanTransactionURL(repaymentTypeCommand, loanID), + getRepaymentBodyAsJSON(date, amountToBePaid), ""); + } + public HashMap makeRepayment(final String date, final Float amountToBePaid, final Integer loanID) { return (HashMap) performLoanTransaction(createLoanTransactionURL(MAKE_REPAYMENT_COMMAND, loanID), getRepaymentBodyAsJSON(date, amountToBePaid), "");