Skip to content

Commit c2be3d9

Browse files
johnw65vidakovic
authored andcommitted
FINERACT-1596 New Merchant Issued and Payout Refunds and Goodwill Credit Transactions
1 parent 40eb547 commit c2be3d9

39 files changed

Lines changed: 557 additions & 171 deletions

File tree

fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ public static CashAccountsForLoan fromInt(final int i) {
7373
***/
7474
public enum AccrualAccountsForLoan {
7575

76-
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(
77-
6), INTEREST_RECEIVABLE(
78-
7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12);
76+
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), //
77+
LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE(7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), //
78+
TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12);
7979

8080
private final Integer value;
8181

fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ public void createJournalEntriesForLoan(final LoanDTO loanDTO) {
6161
}
6262

6363
/***
64-
* Handle repayments, repayments at disbursement and reversal of Repayments and Repayments at disbursement
64+
* Handle repayments, loan refunds, repayments at disbursement and reversal of Repayments and Repayments at
65+
* disbursement
6566
***/
66-
else if (loanTransactionDTO.getTransactionType().isRepayment()
67+
else if (loanTransactionDTO.getTransactionType().isRepaymentType()
6768
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()
6869
|| loanTransactionDTO.getTransactionType().isChargePayment()) {
6970
createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, false,
@@ -293,9 +294,16 @@ private void createJournalEntriesForRepaymentsAndWriteOffs(final LoanDTO loanDTO
293294
FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
294295
transactionDate, totalDebitAmount, isReversal);
295296
} else {
296-
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
297-
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
298-
transactionDate, totalDebitAmount, isReversal);
297+
if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
298+
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
299+
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
300+
transactionDate, totalDebitAmount, isReversal);
301+
302+
} else {
303+
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
304+
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
305+
transactionDate, totalDebitAmount, isReversal);
306+
}
299307
}
300308
}
301309
}

fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,33 @@ public CommandWrapperBuilder loanRepaymentTransaction(final Long loanId) {
797797
return this;
798798
}
799799

800+
public CommandWrapperBuilder loanMerchantIssuedRefundTransaction(final Long loanId) {
801+
this.actionName = "MERCHANTISSUEDREFUND";
802+
this.entityName = "LOAN";
803+
this.entityId = null;
804+
this.loanId = loanId;
805+
this.href = "/loans/" + loanId + "/transactions/template?command=merchantissuedrefund";
806+
return this;
807+
}
808+
809+
public CommandWrapperBuilder loanPayoutRefundTransaction(final Long loanId) {
810+
this.actionName = "PAYOUTREFUND";
811+
this.entityName = "LOAN";
812+
this.entityId = null;
813+
this.loanId = loanId;
814+
this.href = "/loans/" + loanId + "/transactions/template?command=payoutrefund";
815+
return this;
816+
}
817+
818+
public CommandWrapperBuilder loanGoodwillCreditTransaction(final Long loanId) {
819+
this.actionName = "GOODWILLCREDIT";
820+
this.entityName = "LOAN";
821+
this.entityId = null;
822+
this.loanId = loanId;
823+
this.href = "/loans/" + loanId + "/transactions/template?command=goodwillcredit";
824+
return this;
825+
}
826+
800827
public CommandWrapperBuilder loanRecoveryPaymentTransaction(final Long loanId) {
801828
this.actionName = "RECOVERYPAYMENT";
802829
this.entityName = "LOAN";

fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ public CommandProcessingResult create(final JsonCommand command) {
177177
final Boolean isHolidayValidationDone = false;
178178
final HolidayDetailDTO holidayDetailDto = null;
179179
final boolean isRecoveryRepayment = false;
180-
final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount,
181-
new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null,
180+
final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT,
181+
toLoanAccount, new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null,
182182
isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone);
183183

184184
final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command,
@@ -340,10 +340,10 @@ public Long transferFunds(final AccountTransferDTO accountTransferDTO) {
340340
final boolean isRecoveryRepayment = false;
341341
final Boolean isHolidayValidationDone = false;
342342
final HolidayDetailDTO holidayDetailDto = null;
343-
loanTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(),
344-
accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
345-
accountTransferDTO.getPaymentDetail(), null, null, isRecoveryRepayment, isAccountTransfer, holidayDetailDto,
346-
isHolidayValidationDone);
343+
loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount,
344+
new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(),
345+
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), null, null, isRecoveryRepayment,
346+
isAccountTransfer, holidayDetailDto, isHolidayValidationDone);
347347
}
348348

349349
accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO, fromSavingsAccount,
@@ -476,9 +476,9 @@ public AccountTransferDetails repayLoanWithTopup(AccountTransferDTO accountTrans
476476
accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
477477
accountTransferDTO.getNoteText(), accountTransferDTO.getTxnExternalId(), true);
478478

479-
LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(),
480-
accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
481-
null, null, false, isAccountTransfer, null, false, true);
479+
LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount,
480+
new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
481+
accountTransferDTO.getPaymentDetail(), null, null, false, isAccountTransfer, null, false, true);
482482

483483
AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToLoanTransfer(accountTransferDTO,
484484
fromLoanAccount, toLoanAccount, disburseTransaction, repayTransaction);

fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public enum BusinessEvents {
3333
LOAN_UNDO_TRANSACTION("loan_undo_transaction"), //
3434
LOAN_ADJUST_TRANSACTION("loan_adjust_transaction"), //
3535
LOAN_MAKE_REPAYMENT("loan_repayment_transaction"), //
36+
LOAN_MERCHANT_ISSUED_REFUND("loan_merchant_issued_refund"), //
37+
LOAN_PAYOUT_REFUND("loan_payout_refund"), //
38+
LOAN_GOODWILL_CREDIT("loan_goodwill_credit"), //
39+
LOAN_RECOVERY_PAYMENT("loan_recovery_payment"), //
3640
LOAN_WRITTEN_OFF("loan_writtenoff"), //
3741
LOAN_UNDO_WRITTEN_OFF("loan_undo_writtenoff"), //
3842
LOAN_DISBURSAL("loan_disbursal"), //

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
5858
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
5959
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
60+
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
6061
import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
6162
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
6263
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
@@ -110,12 +111,14 @@ private boolean is(final String commandParam, final String commandValue) {
110111
@Produces({ MediaType.APPLICATION_JSON })
111112
@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"
112113
+ "\n" + "Field Defaults\n" + "Allowed Value Lists\n\n" + "Example Requests:\n" + "\n"
113-
+ "loans/1/transactions/template?command=repayment" + "\n" + "loans/1/transactions/template?command=waiveinterest" + "\n"
114-
+ "loans/1/transactions/template?command=writeoff" + "\n" + "loans/1/transactions/template?command=close-rescheduled" + "\n"
115-
+ "loans/1/transactions/template?command=close" + "\n" + "loans/1/transactions/template?command=disburse" + "\n"
116-
+ "loans/1/transactions/template?command=disburseToSavings" + "\n" + "loans/1/transactions/template?command=recoverypayment"
117-
+ "\n" + "loans/1/transactions/template?command=prepayLoan" + "\n" + "loans/1/transactions/template?command=refundbycash" + "\n"
118-
+ "loans/1/transactions/template?command=refundbytransfer" + "\n" + "loans/1/transactions/template?command=foreclosure" + "\n"
114+
+ "loans/1/transactions/template?command=repayment" + "loans/1/transactions/template?command=merchantIssuedRefund"
115+
+ "loans/1/transactions/template?command=payoutRefund" + "loans/1/transactions/template?command=goodwillCredit" + "\n"
116+
+ "loans/1/transactions/template?command=waiveinterest" + "\n" + "loans/1/transactions/template?command=writeoff" + "\n"
117+
+ "loans/1/transactions/template?command=close-rescheduled" + "\n" + "loans/1/transactions/template?command=close" + "\n"
118+
+ "loans/1/transactions/template?command=disburse" + "\n" + "loans/1/transactions/template?command=disburseToSavings" + "\n"
119+
+ "loans/1/transactions/template?command=recoverypayment" + "\n" + "loans/1/transactions/template?command=prepayLoan" + "\n"
120+
+ "loans/1/transactions/template?command=refundbycash" + "\n" + "loans/1/transactions/template?command=refundbytransfer" + "\n"
121+
+ "loans/1/transactions/template?command=foreclosure" + "\n"
119122
+ "loans/1/transactions/template?command=creditBalanceRefund (returned 'amount' field will have the overpaid value")
120123
@ApiResponses({
121124
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) })
@@ -130,6 +133,18 @@ public String retrieveTransactionTemplate(@PathParam("loanId") @Parameter(descri
130133
LoanTransactionData transactionData = null;
131134
if (is(commandParam, "repayment")) {
132135
transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(loanId);
136+
} else if (is(commandParam, "merchantIssuedRefund")) {
137+
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
138+
transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.MERCHANT_ISSUED_REFUND,
139+
loanId, transactionDate);
140+
} else if (is(commandParam, "payoutRefund")) {
141+
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
142+
transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.PAYOUT_REFUND, loanId,
143+
transactionDate);
144+
} else if (is(commandParam, "goodwillCredit")) {
145+
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
146+
transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.GOODWILL_CREDIT, loanId,
147+
transactionDate);
133148
} else if (is(commandParam, "waiveinterest")) {
134149
transactionData = this.loanReadPlatformService.retrieveWaiveInterestDetails(loanId);
135150
} else if (is(commandParam, "writeoff")) {
@@ -156,7 +171,8 @@ public String retrieveTransactionTemplate(@PathParam("loanId") @Parameter(descri
156171
transactionDate = LocalDate.ofInstant(transactionDateParam.getDate("transactionDate", dateFormat, locale).toInstant(),
157172
DateUtils.getDateTimeZoneOfTenant());
158173
}
159-
transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanId, transactionDate);
174+
transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanId,
175+
transactionDate);
160176
} else if (is(commandParam, "refundbycash")) {
161177
transactionData = this.loanReadPlatformService.retrieveRefundByCashTemplate(loanId);
162178
} else if (is(commandParam, "refundbytransfer")) {
@@ -209,15 +225,17 @@ public String retrieveTransaction(@PathParam("loanId") @Parameter(description =
209225
@Consumes({ MediaType.APPLICATION_JSON })
210226
@Produces({ MediaType.APPLICATION_JSON })
211227
@Operation(summary = "Significant Loan Transactions", description = "This API covers the major loan transaction functionality\n\n"
212-
+ "Example Requests:\n" + "\n" + "loans/1/transactions/template?command=repayment" + " | Make a Repayment | \n"
213-
+ "loans/1/transactions/template?command=waiveinterest" + " | Waive Interest | \n"
214-
+ "loans/1/transactions/template?command=writeoff" + " | Write-off Loan | \n"
215-
+ "loans/1/transactions/template?command=close-rescheduled" + " | Close Rescheduled Loan | \n"
216-
+ "loans/1/transactions/template?command=close" + " | Close Loan | \n" + "loans/1/transactions/template?command=undowriteoff"
217-
+ " | Undo Loan Write-off | \n" + "loans/1/transactions/template?command=recoverypayment" + " | Make Recovery Payment | \n"
218-
+ "loans/1/transactions/template?command=refundByCash" + " | Make a Refund of an Active Loan by Cash | \n"
219-
+ "loans/1/transactions/template?command=foreclosure" + " | Foreclosure of an Active Loan | \n"
220-
+ "loans/1/transactions/template?command=creditBalanceRefund" + " | Credit Balance Refund" + " | \n")
228+
+ "Example Requests:\n" + "\n" + "loans/1/transactions?command=repayment" + " | Make a Repayment | \n"
229+
+ "loans/1/transactions?command=merchantIssuedRefund" + " | Merchant Issued Refund | \n"
230+
+ "loans/1/transactions?command=payoutRefund" + " | Payout Refund | \n" + "loans/1/transactions?command=goodwillCredit"
231+
+ " | Goodwil Credit | \n" + "loans/1/transactions?command=waiveinterest" + " | Waive Interest | \n"
232+
+ "loans/1/transactions?command=writeoff" + " | Write-off Loan | \n" + "loans/1/transactions?command=close-rescheduled"
233+
+ " | Close Rescheduled Loan | \n" + "loans/1/transactions?command=close" + " | Close Loan | \n"
234+
+ "loans/1/transactions?command=undowriteoff" + " | Undo Loan Write-off | \n" + "loans/1/transactions?command=recoverypayment"
235+
+ " | Make Recovery Payment | \n" + "loans/1/transactions?command=refundByCash"
236+
+ " | Make a Refund of an Active Loan by Cash | \n" + "loans/1/transactions?command=foreclosure"
237+
+ " | Foreclosure of an Active Loan | \n" + "loans/1/transactions?command=creditBalanceRefund" + " | Credit Balance Refund"
238+
+ " | \n")
221239
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsRequest.class)))
222240
@ApiResponses({
223241
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsResponse.class))) })
@@ -231,6 +249,15 @@ public String executeLoanTransaction(@PathParam("loanId") @Parameter(description
231249
if (is(commandParam, "repayment")) {
232250
final CommandWrapper commandRequest = builder.loanRepaymentTransaction(loanId).build();
233251
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
252+
} else if (is(commandParam, "merchantIssuedRefund")) {
253+
final CommandWrapper commandRequest = builder.loanMerchantIssuedRefundTransaction(loanId).build();
254+
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
255+
} else if (is(commandParam, "payoutRefund")) {
256+
final CommandWrapper commandRequest = builder.loanPayoutRefundTransaction(loanId).build();
257+
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
258+
} else if (is(commandParam, "goodwillCredit")) {
259+
final CommandWrapper commandRequest = builder.loanGoodwillCreditTransaction(loanId).build();
260+
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
234261
} else if (is(commandParam, "waiveinterest")) {
235262
final CommandWrapper commandRequest = builder.waiveInterestPortionTransaction(loanId).build();
236263
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);

0 commit comments

Comments
 (0)