FINERACT-1806: Charge-off API
This commit is contained in:
committed by
Arnold Gálovics
parent
bd052acab0
commit
c7504680b7
@@ -724,6 +724,30 @@
|
|||||||
"null",
|
"null",
|
||||||
"org.apache.fineract.avro.loan.v1.DelinquencyRangeDataV1"
|
"org.apache.fineract.avro.loan.v1.DelinquencyRangeDataV1"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "overpaidOnDate",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "lastClosedBusinessDate",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "chargedOff",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"boolean"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,6 +242,38 @@
|
|||||||
"null",
|
"null",
|
||||||
"string"
|
"string"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "chargedOffOnDate",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "chargedOffByUsername",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "chargedOffByFirstname",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "chargedOffByLastname",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -410,6 +410,22 @@
|
|||||||
"null",
|
"null",
|
||||||
"bigdecimal"
|
"bigdecimal"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "chargeOffReasonId",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"long"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": null,
|
||||||
|
"name": "chargeOffReason",
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -3606,4 +3606,12 @@ public class CommandWrapperBuilder {
|
|||||||
this.href = "/externaleventconfiguration";
|
this.href = "/externaleventconfiguration";
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder chargeOff(final Long loanId) {
|
||||||
|
this.actionName = "CHARGEOFF";
|
||||||
|
this.entityName = "LOAN";
|
||||||
|
this.loanId = loanId;
|
||||||
|
this.href = "/loans/" + loanId + "/transactions?command=charge-off";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-4
@@ -470,15 +470,17 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
|||||||
.append(" l.closedon_date as closedOnDate,")
|
.append(" l.closedon_date as closedOnDate,")
|
||||||
.append(" cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,")
|
.append(" cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,")
|
||||||
.append(" la.overdue_since_date_derived as overdueSinceDate, ")
|
.append(" la.overdue_since_date_derived as overdueSinceDate, ")
|
||||||
.append(" l.writtenoffon_date as writtenOffOnDate, l.expected_maturedon_date as expectedMaturityDate")
|
.append(" l.writtenoffon_date as writtenOffOnDate, l.expected_maturedon_date as expectedMaturityDate, ")
|
||||||
|
.append(" l.charged_off_on_date as chargedOffOnDate, cobu.username as chargedOffByUsername, ")
|
||||||
.append(" from m_loan l ").append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id")
|
.append(" cobu.firstname as chargedOffByFirstname, cobu.lastname as chargedOffByLastname ").append(" from m_loan l ")
|
||||||
|
.append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id")
|
||||||
.append(" left join m_appuser sbu on sbu.id = l.created_by")
|
.append(" left join m_appuser sbu on sbu.id = l.created_by")
|
||||||
.append(" left join m_appuser rbu on rbu.id = l.rejectedon_userid")
|
.append(" left join m_appuser rbu on rbu.id = l.rejectedon_userid")
|
||||||
.append(" left join m_appuser wbu on wbu.id = l.withdrawnon_userid")
|
.append(" left join m_appuser wbu on wbu.id = l.withdrawnon_userid")
|
||||||
.append(" left join m_appuser abu on abu.id = l.approvedon_userid")
|
.append(" left join m_appuser abu on abu.id = l.approvedon_userid")
|
||||||
.append(" left join m_appuser dbu on dbu.id = l.disbursedon_userid")
|
.append(" left join m_appuser dbu on dbu.id = l.disbursedon_userid")
|
||||||
.append(" left join m_appuser cbu on cbu.id = l.closedon_userid")
|
.append(" left join m_appuser cbu on cbu.id = l.closedon_userid")
|
||||||
|
.append(" left join m_appuser cobu on cobu.id = l.charged_off_by_userid")
|
||||||
.append(" left join m_loan_arrears_aging la on la.loan_id = l.id")
|
.append(" left join m_loan_arrears_aging la on la.loan_id = l.id")
|
||||||
.append(" left join glim_accounts glim on glim.id=l.glim_id");
|
.append(" left join glim_accounts glim on glim.id=l.glim_id");
|
||||||
|
|
||||||
@@ -541,6 +543,12 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
|||||||
final LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, "expectedMaturityDate");
|
final LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, "expectedMaturityDate");
|
||||||
|
|
||||||
final LocalDate overdueSinceDate = JdbcSupport.getLocalDate(rs, "overdueSinceDate");
|
final LocalDate overdueSinceDate = JdbcSupport.getLocalDate(rs, "overdueSinceDate");
|
||||||
|
|
||||||
|
final LocalDate chargedOffOnDate = JdbcSupport.getLocalDate(rs, "chargedOffOnDate");
|
||||||
|
final String chargedOffByUsername = rs.getString("chargedOffByUsername");
|
||||||
|
final String chargedOffByFirstname = rs.getString("chargedOffByFirstname");
|
||||||
|
final String chargedOffByLastname = rs.getString("chargedOffByLastname");
|
||||||
|
|
||||||
Boolean inArrears = (overdueSinceDate != null);
|
Boolean inArrears = (overdueSinceDate != null);
|
||||||
|
|
||||||
final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername,
|
final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername,
|
||||||
@@ -548,7 +556,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
|||||||
withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername,
|
withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername,
|
||||||
approvedByFirstname, approvedByLastname, expectedDisbursementDate, actualDisbursementDate, disbursedByUsername,
|
approvedByFirstname, approvedByLastname, expectedDisbursementDate, actualDisbursementDate, disbursedByUsername,
|
||||||
disbursedByFirstname, disbursedByLastname, closedOnDate, closedByUsername, closedByFirstname, closedByLastname,
|
disbursedByFirstname, disbursedByLastname, closedOnDate, closedByUsername, closedByFirstname, closedByLastname,
|
||||||
expectedMaturityDate, writtenOffOnDate, closedByUsername, closedByFirstname, closedByLastname);
|
expectedMaturityDate, writtenOffOnDate, closedByUsername, closedByFirstname, closedByLastname, chargedOffOnDate,
|
||||||
|
chargedOffByUsername, chargedOffByFirstname, chargedOffByLastname);
|
||||||
|
|
||||||
return new LoanAccountSummaryData(id, accountNo, parentAccountNumber, externalId, productId, loanProductName,
|
return new LoanAccountSummaryData(id, accountNo, parentAccountNumber, externalId, productId, loanProductName,
|
||||||
shortLoanProductName, loanStatus, loanType, loanCycle, timeline, inArrears, originalLoan, loanBalance, amountPaid);
|
shortLoanProductName, loanStatus, loanType, loanCycle, timeline, inArrears, originalLoan, loanBalance, amountPaid);
|
||||||
|
|||||||
+3
-1
@@ -133,7 +133,8 @@ public interface LoanApiConstants {
|
|||||||
String installmentAmountParamName = "installmentAmount";
|
String installmentAmountParamName = "installmentAmount";
|
||||||
// loan write off
|
// loan write off
|
||||||
String WRITEOFFREASONS = "WriteOffReasons";
|
String WRITEOFFREASONS = "WriteOffReasons";
|
||||||
|
// loan charge-off
|
||||||
|
String CHARGE_OFF_REASONS = "ChargeOffReasons";
|
||||||
// fore closure constants
|
// fore closure constants
|
||||||
String transactionDateParamName = "transactionDate";
|
String transactionDateParamName = "transactionDate";
|
||||||
String noteParamName = "note";
|
String noteParamName = "note";
|
||||||
@@ -153,6 +154,7 @@ public interface LoanApiConstants {
|
|||||||
String fixedPrincipalPercentagePerInstallmentParamName = "fixedPrincipalPercentagePerInstallment";
|
String fixedPrincipalPercentagePerInstallmentParamName = "fixedPrincipalPercentagePerInstallment";
|
||||||
|
|
||||||
String LOAN_ASSOCIATIONS_ALL = "all";
|
String LOAN_ASSOCIATIONS_ALL = "all";
|
||||||
|
String chargeOffReasonIdParamName = "chargeOffReasonId";
|
||||||
|
|
||||||
// Reversal Transation Data
|
// Reversal Transation Data
|
||||||
String REVERSAL_EXTERNAL_ID_PARAMNAME = "reversalExternalId";
|
String REVERSAL_EXTERNAL_ID_PARAMNAME = "reversalExternalId";
|
||||||
|
|||||||
+12
-3
@@ -76,6 +76,7 @@ import org.springframework.stereotype.Component;
|
|||||||
@Tag(name = "Loan Transactions", description = "Capabilities include loan repayment's, interest waivers and the ability to 'adjust' an existing transaction. An 'adjustment' of a transaction is really a 'reversal' of existing transaction followed by creation of a new transaction with the provided details.")
|
@Tag(name = "Loan Transactions", description = "Capabilities include loan repayment's, interest waivers and the ability to 'adjust' an existing transaction. An 'adjustment' of a transaction is really a 'reversal' of existing transaction followed by creation of a new transaction with the provided details.")
|
||||||
public class LoanTransactionsApiResource {
|
public class LoanTransactionsApiResource {
|
||||||
|
|
||||||
|
public static final String CHARGE_OFF_COMMAND_VALUE = "charge-off";
|
||||||
private final Set<String> responseDataParameters = new HashSet<>(Arrays.asList("id", "type", "date", "currency", "amount", "externalId",
|
private final Set<String> responseDataParameters = new HashSet<>(Arrays.asList("id", "type", "date", "currency", "amount", "externalId",
|
||||||
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME, LoanApiConstants.REVERSED_ON_DATE_PARAMNAME));
|
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME, LoanApiConstants.REVERSED_ON_DATE_PARAMNAME));
|
||||||
|
|
||||||
@@ -103,7 +104,8 @@ public class LoanTransactionsApiResource {
|
|||||||
+ "loans/1/transactions/template?command=recoverypayment" + "\n" + "loans/1/transactions/template?command=prepayLoan" + "\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=refundbycash" + "\n" + "loans/1/transactions/template?command=refundbytransfer" + "\n"
|
||||||
+ "loans/1/transactions/template?command=foreclosure" + "\n"
|
+ "loans/1/transactions/template?command=foreclosure" + "\n"
|
||||||
+ "loans/1/transactions/template?command=creditBalanceRefund (returned 'amount' field will have the overpaid value)")
|
+ "loans/1/transactions/template?command=creditBalanceRefund (returned 'amount' field will have the overpaid value)" + "\n"
|
||||||
|
+ "loans/1/transactions/template?command=charge-off" + "\n")
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) })
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) })
|
||||||
public String retrieveTransactionTemplate(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId,
|
public String retrieveTransactionTemplate(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId,
|
||||||
@@ -129,7 +131,8 @@ public class LoanTransactionsApiResource {
|
|||||||
+ "loans/1/transactions/template?command=recoverypayment" + "\n" + "loans/1/transactions/template?command=prepayLoan" + "\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=refundbycash" + "\n" + "loans/1/transactions/template?command=refundbytransfer" + "\n"
|
||||||
+ "loans/1/transactions/template?command=foreclosure" + "\n"
|
+ "loans/1/transactions/template?command=foreclosure" + "\n"
|
||||||
+ "loans/1/transactions/template?command=creditBalanceRefund (returned 'amount' field will have the overpaid value)")
|
+ "loans/1/transactions/template?command=creditBalanceRefund (returned 'amount' field will have the overpaid value)" + "\n"
|
||||||
|
+ "loans/1/transactions/template?command=charge-off" + "\n")
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) })
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) })
|
||||||
public String retrieveTransactionTemplate(
|
public String retrieveTransactionTemplate(
|
||||||
@@ -224,7 +227,8 @@ public class LoanTransactionsApiResource {
|
|||||||
+ " | Undo Loan Write-off | \n" + "loans/1/transactions?command=recoverypayment" + " | Make Recovery Payment | \n"
|
+ " | 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=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=foreclosure" + " | Foreclosure of an Active Loan | \n"
|
||||||
+ "loans/1/transactions?command=creditBalanceRefund" + " | Credit Balance Refund" + " | \n")
|
+ "loans/1/transactions?command=creditBalanceRefund" + " | Credit Balance Refund" + " | \n"
|
||||||
|
+ "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=charge-off" + " | Charge-off Loan" + " | \n")
|
||||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsRequest.class)))
|
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsRequest.class)))
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsResponse.class))) })
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsResponse.class))) })
|
||||||
@@ -257,6 +261,7 @@ public class LoanTransactionsApiResource {
|
|||||||
+ "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=foreclosure"
|
+ "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=foreclosure"
|
||||||
+ " | Foreclosure of an Active Loan | \n"
|
+ " | Foreclosure of an Active Loan | \n"
|
||||||
+ "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=creditBalanceRefund" + " | Credit Balance Refund"
|
+ "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=creditBalanceRefund" + " | Credit Balance Refund"
|
||||||
|
+ " | \n" + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?command=charge-off" + " | Charge-off Loan"
|
||||||
+ " | \n")
|
+ " | \n")
|
||||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsRequest.class)))
|
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsRequest.class)))
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@@ -456,6 +461,8 @@ public class LoanTransactionsApiResource {
|
|||||||
commandRequest = builder.loanForeclosure(resolvedLoanId).build();
|
commandRequest = builder.loanForeclosure(resolvedLoanId).build();
|
||||||
} else if (CommandParameterUtil.is(commandParam, "creditBalanceRefund")) {
|
} else if (CommandParameterUtil.is(commandParam, "creditBalanceRefund")) {
|
||||||
commandRequest = builder.creditBalanceRefund(resolvedLoanId).build();
|
commandRequest = builder.creditBalanceRefund(resolvedLoanId).build();
|
||||||
|
} else if (CommandParameterUtil.is(commandParam, CHARGE_OFF_COMMAND_VALUE)) {
|
||||||
|
commandRequest = builder.chargeOff(resolvedLoanId).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commandRequest == null) {
|
if (commandRequest == null) {
|
||||||
@@ -529,6 +536,8 @@ public class LoanTransactionsApiResource {
|
|||||||
transactionData = this.loanReadPlatformService.retrieveLoanForeclosureTemplate(resolvedLoanId, transactionDate);
|
transactionData = this.loanReadPlatformService.retrieveLoanForeclosureTemplate(resolvedLoanId, transactionDate);
|
||||||
} else if (CommandParameterUtil.is(commandParam, "creditBalanceRefund")) {
|
} else if (CommandParameterUtil.is(commandParam, "creditBalanceRefund")) {
|
||||||
transactionData = this.loanReadPlatformService.retrieveCreditBalanceRefundTemplate(resolvedLoanId);
|
transactionData = this.loanReadPlatformService.retrieveCreditBalanceRefundTemplate(resolvedLoanId);
|
||||||
|
} else if (CommandParameterUtil.is(commandParam, CHARGE_OFF_COMMAND_VALUE)) {
|
||||||
|
transactionData = this.loanReadPlatformService.retrieveLoanChargeOffTemplate(resolvedLoanId);
|
||||||
} else {
|
} else {
|
||||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -258,7 +258,10 @@ final class LoanTransactionsApiResourceSwagger {
|
|||||||
public Integer loanChargeId;
|
public Integer loanChargeId;
|
||||||
@Schema(example = "28 June 2022")
|
@Schema(example = "28 June 2022")
|
||||||
public String dueDate;
|
public String dueDate;
|
||||||
|
@Schema(example = "1")
|
||||||
|
public Long chargeOffReasonId;
|
||||||
|
@Schema(example = "1")
|
||||||
|
public Long writeoffReasonId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "PostLoansLoanIdTransactionsResponse")
|
@Schema(description = "PostLoansLoanIdTransactionsResponse")
|
||||||
|
|||||||
+14
@@ -247,6 +247,14 @@ final class LoansApiResourceSwagger {
|
|||||||
public LocalDate expectedMaturityDate;
|
public LocalDate expectedMaturityDate;
|
||||||
@Schema(example = "[2012, 4, 3]")
|
@Schema(example = "[2012, 4, 3]")
|
||||||
public LocalDate closedOnDate;
|
public LocalDate closedOnDate;
|
||||||
|
@Schema(example = "[2012, 4, 10]")
|
||||||
|
public LocalDate chargedOffOnDate;
|
||||||
|
@Schema(example = "admin")
|
||||||
|
public String chargedOffByUsername;
|
||||||
|
@Schema(example = "App")
|
||||||
|
public String chargedOffByFirstname;
|
||||||
|
@Schema(example = "Administrator")
|
||||||
|
public String chargedOffByLastname;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class GetLoansLoanIdRepaymentSchedule {
|
static final class GetLoansLoanIdRepaymentSchedule {
|
||||||
@@ -601,6 +609,10 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalRepaymentTransactionReversed;
|
public Double totalRepaymentTransactionReversed;
|
||||||
public Set<GetLoansLoanIdOverdueCharges> overdueCharges;
|
public Set<GetLoansLoanIdOverdueCharges> overdueCharges;
|
||||||
|
@Schema(example = "1")
|
||||||
|
public Long chargeOffReasonId;
|
||||||
|
@Schema(example = "reason")
|
||||||
|
public String chargeOffReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class GetLoansLoanIdPaymentType {
|
static final class GetLoansLoanIdPaymentType {
|
||||||
@@ -1028,6 +1040,8 @@ final class LoansApiResourceSwagger {
|
|||||||
public LocalDate lastClosedBusinessDate;
|
public LocalDate lastClosedBusinessDate;
|
||||||
@Schema(example = "[2013, 11, 1]")
|
@Schema(example = "[2013, 11, 1]")
|
||||||
public LocalDate overpaidOnDate;
|
public LocalDate overpaidOnDate;
|
||||||
|
@Schema(example = "false")
|
||||||
|
public Boolean chargedOff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "GetLoansResponse")
|
@Schema(description = "GetLoansResponse")
|
||||||
|
|||||||
+14
-12
@@ -248,11 +248,12 @@ public class LoanAccountData {
|
|||||||
private String linkAccountId;
|
private String linkAccountId;
|
||||||
private Long groupId;
|
private Long groupId;
|
||||||
private LocalDate expectedDisbursementDate;
|
private LocalDate expectedDisbursementDate;
|
||||||
private LocalDate overpaidOnDate;
|
|
||||||
|
|
||||||
|
private LocalDate overpaidOnDate;
|
||||||
private CollectionData delinquent;
|
private CollectionData delinquent;
|
||||||
private DelinquencyRangeData delinquencyRange;
|
private DelinquencyRangeData delinquencyRange;
|
||||||
private LocalDate lastClosedBusinessDate;
|
private LocalDate lastClosedBusinessDate;
|
||||||
|
private Boolean chargedOff;
|
||||||
|
|
||||||
public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId,
|
public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId,
|
||||||
Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments,
|
Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments,
|
||||||
@@ -381,7 +382,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate);
|
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -452,7 +453,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate);
|
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
|
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
|
||||||
@@ -620,7 +621,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(product.isEqualAmortization()).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
.setIsEqualAmortization(product.isEqualAmortization()).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
||||||
.setFixedPrincipalPercentagePerInstallment(product.getFixedPrincipalPercentagePerInstallment()).setDelinquent(delinquent)
|
.setFixedPrincipalPercentagePerInstallment(product.getFixedPrincipalPercentagePerInstallment()).setDelinquent(delinquent)
|
||||||
.setDisallowExpectedDisbursements(product.getDisallowExpectedDisbursements()).setFraud(acc.fraud)
|
.setDisallowExpectedDisbursements(product.getDisallowExpectedDisbursements()).setFraud(acc.fraud)
|
||||||
.setOverpaidOnDate(acc.overpaidOnDate);
|
.setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -654,7 +655,7 @@ public class LoanAccountData {
|
|||||||
final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo,
|
final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo,
|
||||||
final BigDecimal topupAmount, final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
final BigDecimal topupAmount, final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
||||||
final DelinquencyRangeData delinquencyRange, final boolean disallowExpectedDisbursements, final boolean fraud,
|
final DelinquencyRangeData delinquencyRange, final boolean disallowExpectedDisbursements, final boolean fraud,
|
||||||
LocalDate lastClosedBusinessDate, LocalDate overpaidOnDate) {
|
LocalDate lastClosedBusinessDate, LocalDate overpaidOnDate, final boolean chargedOff) {
|
||||||
|
|
||||||
final CollectionData delinquent = CollectionData.template();
|
final CollectionData delinquent = CollectionData.template();
|
||||||
|
|
||||||
@@ -694,7 +695,7 @@ public class LoanAccountData {
|
|||||||
.setClosureLoanAccountNo(closureLoanAccountNo).setTopupAmount(topupAmount).setIsEqualAmortization(isEqualAmortization)
|
.setClosureLoanAccountNo(closureLoanAccountNo).setTopupAmount(topupAmount).setIsEqualAmortization(isEqualAmortization)
|
||||||
.setFixedPrincipalPercentagePerInstallment(fixedPrincipalPercentagePerInstallment).setDelinquent(delinquent)
|
.setFixedPrincipalPercentagePerInstallment(fixedPrincipalPercentagePerInstallment).setDelinquent(delinquent)
|
||||||
.setDelinquencyRange(delinquencyRange).setDisallowExpectedDisbursements(disallowExpectedDisbursements).setFraud(fraud)
|
.setDelinquencyRange(delinquencyRange).setDisallowExpectedDisbursements(disallowExpectedDisbursements).setFraud(fraud)
|
||||||
.setLastClosedBusinessDate(lastClosedBusinessDate).setOverpaidOnDate(overpaidOnDate);
|
.setLastClosedBusinessDate(lastClosedBusinessDate).setOverpaidOnDate(overpaidOnDate).setChargedOff(chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -778,7 +779,8 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization)
|
.setIsEqualAmortization(acc.isEqualAmortization)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud).setLastClosedBusinessDate(acc.getLastClosedBusinessDate()).setOverpaidOnDate(acc.overpaidOnDate);
|
.setFraud(acc.fraud).setLastClosedBusinessDate(acc.getLastClosedBusinessDate()).setOverpaidOnDate(acc.overpaidOnDate)
|
||||||
|
.setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||||
@@ -853,7 +855,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate);
|
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||||
@@ -950,7 +952,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate);
|
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData,
|
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData,
|
||||||
@@ -1015,7 +1017,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate);
|
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
||||||
@@ -1073,7 +1075,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate);
|
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
||||||
@@ -1134,7 +1136,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
.setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate);
|
.setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Comparator<LoanAccountData> ClientNameComparator = (loan1, loan2) -> {
|
public static final Comparator<LoanAccountData> ClientNameComparator = (loan1, loan2) -> {
|
||||||
|
|||||||
+11
-1
@@ -62,6 +62,11 @@ public class LoanApplicationTimelineData {
|
|||||||
private final String writeOffByFirstname;
|
private final String writeOffByFirstname;
|
||||||
private final String writeOffByLastname;
|
private final String writeOffByLastname;
|
||||||
|
|
||||||
|
private final LocalDate chargedOffOnDate;
|
||||||
|
private final String chargedOffByUsername;
|
||||||
|
private final String chargedOffByFirstname;
|
||||||
|
private final String chargedOffByLastname;
|
||||||
|
|
||||||
public static LoanApplicationTimelineData templateDefault(final LocalDate expectedDisbursementDate) {
|
public static LoanApplicationTimelineData templateDefault(final LocalDate expectedDisbursementDate) {
|
||||||
|
|
||||||
final LocalDate submittedOnDate = null;
|
final LocalDate submittedOnDate = null;
|
||||||
@@ -93,13 +98,18 @@ public class LoanApplicationTimelineData {
|
|||||||
final String writeOffByUsername = null;
|
final String writeOffByUsername = null;
|
||||||
final String writeOffByFirstname = null;
|
final String writeOffByFirstname = null;
|
||||||
final String writeOffByLastname = null;
|
final String writeOffByLastname = null;
|
||||||
|
final LocalDate chargedOffOnDate = null;
|
||||||
|
final String chargedOffByUsername = null;
|
||||||
|
final String chargedOffByFirstname = null;
|
||||||
|
final String chargedOffByLastname = null;
|
||||||
|
|
||||||
return new LoanApplicationTimelineData(submittedOnDate, submittedByUsername, submittedByFirstname, submittedByLastname,
|
return new LoanApplicationTimelineData(submittedOnDate, submittedByUsername, submittedByFirstname, submittedByLastname,
|
||||||
rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname, withdrawnOnDate, withdrawnByUsername,
|
rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname, withdrawnOnDate, withdrawnByUsername,
|
||||||
withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname,
|
withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname,
|
||||||
expectedDisbursementDate, actualDisbursementDate, disbursedByUsername, disbursedByFirstname, disbursedByLastname,
|
expectedDisbursementDate, actualDisbursementDate, disbursedByUsername, disbursedByFirstname, disbursedByLastname,
|
||||||
closedOnDate, closedByUsername, closedByFirstname, closedByLastname, expectedMaturityDate, writeOffOnDate,
|
closedOnDate, closedByUsername, closedByFirstname, closedByLastname, expectedMaturityDate, writeOffOnDate,
|
||||||
writeOffByUsername, writeOffByFirstname, writeOffByLastname);
|
writeOffByUsername, writeOffByFirstname, writeOffByLastname, chargedOffOnDate, chargedOffByUsername, chargedOffByFirstname,
|
||||||
|
chargedOffByLastname);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData(final CurrencyData currency, final BigDecimal principal,
|
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData(final CurrencyData currency, final BigDecimal principal,
|
||||||
|
|||||||
+12
-7
@@ -87,6 +87,8 @@ public class LoanSummaryData {
|
|||||||
private BigDecimal totalCreditBalanceRefundReversed;
|
private BigDecimal totalCreditBalanceRefundReversed;
|
||||||
private BigDecimal totalRepaymentTransaction;
|
private BigDecimal totalRepaymentTransaction;
|
||||||
private BigDecimal totalRepaymentTransactionReversed;
|
private BigDecimal totalRepaymentTransactionReversed;
|
||||||
|
private final Long chargeOffReasonId;
|
||||||
|
private final String chargeOffReason;
|
||||||
|
|
||||||
public LoanSummaryData(final CurrencyData currency, final BigDecimal principalDisbursed, final BigDecimal principalAdjustments,
|
public LoanSummaryData(final CurrencyData currency, final BigDecimal principalDisbursed, final BigDecimal principalAdjustments,
|
||||||
final BigDecimal principalPaid, final BigDecimal principalWrittenOff, final BigDecimal principalOutstanding,
|
final BigDecimal principalPaid, final BigDecimal principalWrittenOff, final BigDecimal principalOutstanding,
|
||||||
@@ -100,7 +102,7 @@ public class LoanSummaryData {
|
|||||||
final BigDecimal totalRepayment, final BigDecimal totalExpectedCostOfLoan, final BigDecimal totalCostOfLoan,
|
final BigDecimal totalRepayment, final BigDecimal totalExpectedCostOfLoan, final BigDecimal totalCostOfLoan,
|
||||||
final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding,
|
final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding,
|
||||||
final BigDecimal totalOverdue, final LocalDate overdueSinceDate, final Long writeoffReasonId, final String writeoffReason,
|
final BigDecimal totalOverdue, final LocalDate overdueSinceDate, final Long writeoffReasonId, final String writeoffReason,
|
||||||
final BigDecimal totalRecovered) {
|
final BigDecimal totalRecovered, final Long chargeOffReasonId, final String chargeOffReason) {
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.principalDisbursed = principalDisbursed;
|
this.principalDisbursed = principalDisbursed;
|
||||||
this.principalAdjustments = principalAdjustments;
|
this.principalAdjustments = principalAdjustments;
|
||||||
@@ -139,6 +141,8 @@ public class LoanSummaryData {
|
|||||||
this.writeoffReasonId = writeoffReasonId;
|
this.writeoffReasonId = writeoffReasonId;
|
||||||
this.writeoffReason = writeoffReason;
|
this.writeoffReason = writeoffReason;
|
||||||
this.totalRecovered = totalRecovered;
|
this.totalRecovered = totalRecovered;
|
||||||
|
this.chargeOffReasonId = chargeOffReasonId;
|
||||||
|
this.chargeOffReason = chargeOffReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanSummaryData withTransactionAmountsSummary(final LoanSummaryData defaultSummaryData,
|
public static LoanSummaryData withTransactionAmountsSummary(final LoanSummaryData defaultSummaryData,
|
||||||
@@ -193,12 +197,13 @@ public class LoanSummaryData {
|
|||||||
defaultSummaryData.totalExpectedRepayment, defaultSummaryData.totalRepayment, defaultSummaryData.totalExpectedCostOfLoan,
|
defaultSummaryData.totalExpectedRepayment, defaultSummaryData.totalRepayment, defaultSummaryData.totalExpectedCostOfLoan,
|
||||||
defaultSummaryData.totalCostOfLoan, defaultSummaryData.totalWaived, defaultSummaryData.totalWrittenOff,
|
defaultSummaryData.totalCostOfLoan, defaultSummaryData.totalWaived, defaultSummaryData.totalWrittenOff,
|
||||||
defaultSummaryData.totalOutstanding, defaultSummaryData.totalOverdue, defaultSummaryData.overdueSinceDate,
|
defaultSummaryData.totalOutstanding, defaultSummaryData.totalOverdue, defaultSummaryData.overdueSinceDate,
|
||||||
defaultSummaryData.writeoffReasonId, defaultSummaryData.writeoffReason, defaultSummaryData.totalRecovered)
|
defaultSummaryData.writeoffReasonId, defaultSummaryData.writeoffReason, defaultSummaryData.totalRecovered,
|
||||||
.setTotalMerchantRefund(totalMerchantRefund).setTotalMerchantRefundReversed(totalMerchantRefundReversed)
|
defaultSummaryData.chargeOffReasonId, defaultSummaryData.chargeOffReason).setTotalMerchantRefund(totalMerchantRefund)
|
||||||
.setTotalPayoutRefund(totalPayoutRefund).setTotalPayoutRefundReversed(totalPayoutRefundReversed)
|
.setTotalMerchantRefundReversed(totalMerchantRefundReversed).setTotalPayoutRefund(totalPayoutRefund)
|
||||||
.setTotalGoodwillCredit(totalGoodwillCredit).setTotalGoodwillCreditReversed(totalGoodwillCreditReversed)
|
.setTotalPayoutRefundReversed(totalPayoutRefundReversed).setTotalGoodwillCredit(totalGoodwillCredit)
|
||||||
.setTotalChargeAdjustment(totalChargeAdjustment).setTotalChargeAdjustmentReversed(totalChargeAdjustmentReversed)
|
.setTotalGoodwillCreditReversed(totalGoodwillCreditReversed).setTotalChargeAdjustment(totalChargeAdjustment)
|
||||||
.setTotalChargeback(totalChargeback).setTotalCreditBalanceRefund(totalCreditBalanceRefund)
|
.setTotalChargeAdjustmentReversed(totalChargeAdjustmentReversed).setTotalChargeback(totalChargeback)
|
||||||
|
.setTotalCreditBalanceRefund(totalCreditBalanceRefund)
|
||||||
.setTotalCreditBalanceRefundReversed(totalCreditBalanceRefundReversed)
|
.setTotalCreditBalanceRefundReversed(totalCreditBalanceRefundReversed)
|
||||||
.setTotalRepaymentTransaction(totalRepaymentTransaction)
|
.setTotalRepaymentTransaction(totalRepaymentTransaction)
|
||||||
.setTotalRepaymentTransactionReversed(totalRepaymentTransactionReversed);
|
.setTotalRepaymentTransactionReversed(totalRepaymentTransactionReversed);
|
||||||
|
|||||||
+6
@@ -96,6 +96,8 @@ public class LoanTransactionData {
|
|||||||
|
|
||||||
private List<LoanTransactionRelationData> transactionRelations;
|
private List<LoanTransactionRelationData> transactionRelations;
|
||||||
|
|
||||||
|
private Collection<CodeValueData> chargeOffReasonOptions = null;
|
||||||
|
|
||||||
public static LoanTransactionData importInstance(BigDecimal repaymentAmount, LocalDate lastRepaymentDate, Long repaymentTypeId,
|
public static LoanTransactionData importInstance(BigDecimal repaymentAmount, LocalDate lastRepaymentDate, Long repaymentTypeId,
|
||||||
Integer rowIndex, String locale, String dateFormat) {
|
Integer rowIndex, String locale, String dateFormat) {
|
||||||
return new LoanTransactionData(repaymentAmount, lastRepaymentDate, repaymentTypeId, rowIndex, locale, dateFormat);
|
return new LoanTransactionData(repaymentAmount, lastRepaymentDate, repaymentTypeId, rowIndex, locale, dateFormat);
|
||||||
@@ -369,6 +371,10 @@ public class LoanTransactionData {
|
|||||||
this.writeOffReasonOptions = writeOffReasonOptions;
|
this.writeOffReasonOptions = writeOffReasonOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setChargeOffReasonOptions(Collection<CodeValueData> chargeOffReasonOptions) {
|
||||||
|
this.chargeOffReasonOptions = chargeOffReasonOptions;
|
||||||
|
}
|
||||||
|
|
||||||
public void setLoanChargePaidByList(Collection<LoanChargePaidByData> loanChargePaidByList) {
|
public void setLoanChargePaidByList(Collection<LoanChargePaidByData> loanChargePaidByList) {
|
||||||
this.loanChargePaidByList = loanChargePaidByList;
|
this.loanChargePaidByList = loanChargePaidByList;
|
||||||
}
|
}
|
||||||
|
|||||||
+33
@@ -439,6 +439,20 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
@Column(name = "last_closed_business_date")
|
@Column(name = "last_closed_business_date")
|
||||||
private LocalDate lastClosedBusinessDate;
|
private LocalDate lastClosedBusinessDate;
|
||||||
|
|
||||||
|
@Column(name = "is_charged_off", nullable = false)
|
||||||
|
private boolean chargedOff;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "charge_off_reason_cv_id")
|
||||||
|
private CodeValue chargeOffReason;
|
||||||
|
|
||||||
|
@Column(name = "charged_off_on_date")
|
||||||
|
private LocalDate chargedOffOnDate;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "charged_off_by_userid")
|
||||||
|
private AppUser chargedOffBy;
|
||||||
|
|
||||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
||||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||||
final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
||||||
@@ -6949,4 +6963,23 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
public void setLastClosedBusinessDate(LocalDate lastClosedBusinessDate) {
|
public void setLastClosedBusinessDate(LocalDate lastClosedBusinessDate) {
|
||||||
this.lastClosedBusinessDate = lastClosedBusinessDate;
|
this.lastClosedBusinessDate = lastClosedBusinessDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markAsChargedOff(final LocalDate chargedOffOn, final AppUser chargedOffBy, final CodeValue chargeOffReason) {
|
||||||
|
this.chargedOff = true;
|
||||||
|
this.chargedOffBy = chargedOffBy;
|
||||||
|
this.chargedOffOnDate = chargedOffOn;
|
||||||
|
this.chargeOffReason = chargeOffReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void liftChargeOff() {
|
||||||
|
this.chargedOff = false;
|
||||||
|
this.chargedOffBy = null;
|
||||||
|
this.chargedOffOnDate = null;
|
||||||
|
this.chargeOffReason = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChargedOff() {
|
||||||
|
return this.chargedOff;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-1
@@ -347,6 +347,25 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
return new LoanTransaction(loan, office, LoanTransactionType.WRITEOFF, null, writeOffDate, externalId);
|
return new LoanTransaction(loan, office, LoanTransactionType.WRITEOFF, null, writeOffDate, externalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LoanTransaction chargeOff(final Loan loan, final LocalDate chargeOffDate, final ExternalId externalId) {
|
||||||
|
BigDecimal principalPortion = loan.getLoanSummary().getTotalPrincipalOutstanding().compareTo(BigDecimal.ZERO) != 0
|
||||||
|
? loan.getLoanSummary().getTotalPrincipalOutstanding()
|
||||||
|
: null;
|
||||||
|
BigDecimal interestPortion = loan.getLoanSummary().getTotalInterestOutstanding().compareTo(BigDecimal.ZERO) != 0
|
||||||
|
? loan.getLoanSummary().getTotalInterestOutstanding()
|
||||||
|
: null;
|
||||||
|
BigDecimal feePortion = loan.getLoanSummary().getTotalFeeChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
|
||||||
|
? loan.getLoanSummary().getTotalFeeChargesOutstanding()
|
||||||
|
: null;
|
||||||
|
BigDecimal penaltyPortion = loan.getLoanSummary().getTotalPenaltyChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
|
||||||
|
? loan.getLoanSummary().getTotalPenaltyChargesOutstanding()
|
||||||
|
: null;
|
||||||
|
BigDecimal totalOutstanding = loan.getLoanSummary().getTotalOutstanding();
|
||||||
|
|
||||||
|
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CHARGE_OFF.getValue(), chargeOffDate, totalOutstanding,
|
||||||
|
principalPortion, interestPortion, feePortion, penaltyPortion, null, false, null, externalId);
|
||||||
|
}
|
||||||
|
|
||||||
private LoanTransaction(final Loan loan, final Office office, final LoanTransactionType type, final BigDecimal amount,
|
private LoanTransaction(final Loan loan, final Office office, final LoanTransactionType type, final BigDecimal amount,
|
||||||
final LocalDate date, final ExternalId externalId) {
|
final LocalDate date, final ExternalId externalId) {
|
||||||
this.loan = loan;
|
this.loan = loan;
|
||||||
@@ -647,6 +666,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
return getTypeOf().isWriteOff() && isNotReversed();
|
return getTypeOf().isWriteOff() && isNotReversed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isChargeOff() {
|
||||||
|
return getTypeOf().isChargeOff() && isNotReversed();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isIdentifiedBy(final Long identifier) {
|
public boolean isIdentifiedBy(final Long identifier) {
|
||||||
return getId().equals(identifier);
|
return getId().equals(identifier);
|
||||||
}
|
}
|
||||||
@@ -786,7 +809,8 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
return isNotReversed() && (LoanTransactionType.CONTRA.equals(getTypeOf())
|
return isNotReversed() && (LoanTransactionType.CONTRA.equals(getTypeOf())
|
||||||
|| LoanTransactionType.MARKED_FOR_RESCHEDULING.equals(getTypeOf())
|
|| LoanTransactionType.MARKED_FOR_RESCHEDULING.equals(getTypeOf())
|
||||||
|| LoanTransactionType.APPROVE_TRANSFER.equals(getTypeOf()) || LoanTransactionType.INITIATE_TRANSFER.equals(getTypeOf())
|
|| LoanTransactionType.APPROVE_TRANSFER.equals(getTypeOf()) || LoanTransactionType.INITIATE_TRANSFER.equals(getTypeOf())
|
||||||
|| LoanTransactionType.REJECT_TRANSFER.equals(getTypeOf()) || LoanTransactionType.WITHDRAW_TRANSFER.equals(getTypeOf()));
|
|| LoanTransactionType.REJECT_TRANSFER.equals(getTypeOf()) || LoanTransactionType.WITHDRAW_TRANSFER.equals(getTypeOf())
|
||||||
|
|| LoanTransactionType.CHARGE_OFF.equals(getTypeOf()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateOutstandingLoanBalance(BigDecimal outstandingLoanBalance) {
|
public void updateOutstandingLoanBalance(BigDecimal outstandingLoanBalance) {
|
||||||
|
|||||||
+6
-1
@@ -58,7 +58,8 @@ public enum LoanTransactionType {
|
|||||||
GOODWILL_CREDIT(23, "loanTransactionType.goodwillCredit"), //
|
GOODWILL_CREDIT(23, "loanTransactionType.goodwillCredit"), //
|
||||||
CHARGE_REFUND(24, "loanTransactionType.chargeRefund"), //
|
CHARGE_REFUND(24, "loanTransactionType.chargeRefund"), //
|
||||||
CHARGEBACK(25, "loanTransactionType.chargeback"), //
|
CHARGEBACK(25, "loanTransactionType.chargeback"), //
|
||||||
CHARGE_ADJUSTMENT(26, "loanTransactionType.chargeAdjustment");
|
CHARGE_ADJUSTMENT(26, "loanTransactionType.chargeAdjustment"), //
|
||||||
|
CHARGE_OFF(27, "loanTransactionType.chargeOff");
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
private final String code;
|
private final String code;
|
||||||
@@ -100,6 +101,7 @@ public enum LoanTransactionType {
|
|||||||
case 24 -> LoanTransactionType.CHARGE_REFUND;
|
case 24 -> LoanTransactionType.CHARGE_REFUND;
|
||||||
case 25 -> LoanTransactionType.CHARGEBACK;
|
case 25 -> LoanTransactionType.CHARGEBACK;
|
||||||
case 26 -> LoanTransactionType.CHARGE_ADJUSTMENT;
|
case 26 -> LoanTransactionType.CHARGE_ADJUSTMENT;
|
||||||
|
case 27 -> LoanTransactionType.CHARGE_OFF;
|
||||||
default -> LoanTransactionType.INVALID;
|
default -> LoanTransactionType.INVALID;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -184,4 +186,7 @@ public enum LoanTransactionType {
|
|||||||
return this.equals(LoanTransactionType.CHARGE_ADJUSTMENT);
|
return this.equals(LoanTransactionType.CHARGE_ADJUSTMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isChargeOff() {
|
||||||
|
return this.equals(LoanTransactionType.CHARGE_OFF);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* 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.service.LoanWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@CommandType(entity = "LOAN", action = "CHARGEOFF")
|
||||||
|
public class ChargeOffLoanCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final LoanWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
return writePlatformService.chargeOff(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -341,6 +341,36 @@ public final class LoanEventApiJsonValidator {
|
|||||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validateChargeOffTransaction(final String json) {
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<String> chargeOffParameters = new HashSet<>(
|
||||||
|
Arrays.asList("transactionDate", "note", "locale", "dateFormat", "chargeOffReasonId", "externalId"));
|
||||||
|
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, chargeOffParameters);
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||||
|
|
||||||
|
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||||
|
final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||||
|
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||||
|
|
||||||
|
final String note = fromApiJsonHelper.extractStringNamed("note", element);
|
||||||
|
baseDataValidator.reset().parameter("note").value(note).ignoreIfNull().notExceedingLengthOf(1000);
|
||||||
|
|
||||||
|
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
|
||||||
|
baseDataValidator.reset().parameter("externalId").value(externalId).ignoreIfNull().notExceedingLengthOf(100);
|
||||||
|
|
||||||
|
final Long chargeOffReasonId = fromApiJsonHelper.extractLongNamed("chargeOffReasonId", element);
|
||||||
|
baseDataValidator.reset().parameter("chargeOffReasonId").value(chargeOffReasonId).ignoreIfNull().integerGreaterThanZero();
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
public void validateUpdateOfLoanOfficer(final String json) {
|
public void validateUpdateOfLoanOfficer(final String json) {
|
||||||
if (StringUtils.isBlank(json)) {
|
if (StringUtils.isBlank(json)) {
|
||||||
throw new InvalidJsonException();
|
throw new InvalidJsonException();
|
||||||
|
|||||||
+2
@@ -116,6 +116,8 @@ public interface LoanReadPlatformService {
|
|||||||
|
|
||||||
Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(LocalDate tillDate);
|
Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(LocalDate tillDate);
|
||||||
|
|
||||||
|
LoanTransactionData retrieveLoanChargeOffTemplate(Long loanId);
|
||||||
|
|
||||||
Collection<Long> fetchLoansForInterestRecalculation();
|
Collection<Long> fetchLoansForInterestRecalculation();
|
||||||
|
|
||||||
List<Long> fetchLoansForInterestRecalculation(Integer pageSize, Long maxLoanIdInList, String officeHierarchy);
|
List<Long> fetchLoansForInterestRecalculation(Integer pageSize, Long maxLoanIdInList, String officeHierarchy);
|
||||||
|
|||||||
+38
-4
@@ -677,7 +677,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, "
|
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, "
|
||||||
+ " lp.can_use_for_topup as canUseForTopup, l.is_topup as isTopup, topup.closure_loan_id as closureLoanId, "
|
+ " lp.can_use_for_topup as canUseForTopup, l.is_topup as isTopup, topup.closure_loan_id as closureLoanId, "
|
||||||
+ " l.total_recovered_derived as totalRecovered, topuploan.account_no as closureLoanAccountNo, "
|
+ " l.total_recovered_derived as totalRecovered, topuploan.account_no as closureLoanAccountNo, "
|
||||||
+ " topup.topup_amount as topupAmount, l.last_closed_business_date as lastClosedBusinessDate,l.overpaidon_date as overpaidOnDate from m_loan l" //
|
+ " topup.topup_amount as topupAmount, l.last_closed_business_date as lastClosedBusinessDate,l.overpaidon_date as overpaidOnDate, "
|
||||||
|
+ " l.is_charged_off as isChargedOff, l.charge_off_reason_cv_id as chargeOffReasonId, codec.code_value as chargeOffReason, l.charged_off_on_date as chargedOffOnDate, "
|
||||||
|
+ " cobu.username as chargedOffByUsername, cobu.firstname as chargedOffByFirstname, cobu.lastname as chargedOffByLastname "
|
||||||
|
+ " from m_loan l" //
|
||||||
+ " join m_product_loan lp on lp.id = l.product_id" //
|
+ " join m_product_loan lp on lp.id = l.product_id" //
|
||||||
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id join m_currency rc on rc."
|
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id join m_currency rc on rc."
|
||||||
+ sqlGenerator.escape("code") + " = l.currency_code" //
|
+ sqlGenerator.escape("code") + " = l.currency_code" //
|
||||||
@@ -690,8 +693,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
+ " left join m_appuser wbu on wbu.id = l.withdrawnon_userid"
|
+ " left join m_appuser wbu on wbu.id = l.withdrawnon_userid"
|
||||||
+ " left join m_appuser abu on abu.id = l.approvedon_userid"
|
+ " left join m_appuser abu on abu.id = l.approvedon_userid"
|
||||||
+ " left join m_appuser dbu on dbu.id = l.disbursedon_userid left join m_appuser cbu on cbu.id = l.closedon_userid"
|
+ " left join m_appuser dbu on dbu.id = l.disbursedon_userid left join m_appuser cbu on cbu.id = l.closedon_userid"
|
||||||
|
+ " left join m_appuser cobu on cobu.id = l.charged_off_by_userid "
|
||||||
+ " left join m_code_value cv on cv.id = l.loanpurpose_cv_id"
|
+ " left join m_code_value cv on cv.id = l.loanpurpose_cv_id"
|
||||||
+ " left join m_code_value codev on codev.id = l.writeoff_reason_cv_id"
|
+ " left join m_code_value codev on codev.id = l.writeoff_reason_cv_id"
|
||||||
|
+ " left join m_code_value codec on codec.id = l.charge_off_reason_cv_id"
|
||||||
+ " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id"
|
+ " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id"
|
||||||
+ " left join m_loan_topup as topup on l.id = topup.loan_id"
|
+ " left join m_loan_topup as topup on l.id = topup.loan_id"
|
||||||
+ " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id ";
|
+ " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id ";
|
||||||
@@ -792,12 +797,21 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
final Integer minimumGap = rs.getInt("minimuminstallmentgap");
|
final Integer minimumGap = rs.getInt("minimuminstallmentgap");
|
||||||
final Integer maximumGap = rs.getInt("maximuminstallmentgap");
|
final Integer maximumGap = rs.getInt("maximuminstallmentgap");
|
||||||
|
|
||||||
|
final LocalDate chargedOffOnDate = JdbcSupport.getLocalDate(rs, "chargedOffOnDate");
|
||||||
|
final String chargedOffByUsername = rs.getString("chargedOffByUsername");
|
||||||
|
final String chargedOffByFirstname = rs.getString("chargedOffByFirstname");
|
||||||
|
final String chargedOffByLastname = rs.getString("chargedOffByLastname");
|
||||||
|
final Long chargeOffReasonId = JdbcSupport.getLong(rs, "chargeOffReasonId");
|
||||||
|
final String chargeOffReason = rs.getString("chargeOffReason");
|
||||||
|
final boolean isChargedOff = rs.getBoolean("isChargedOff");
|
||||||
|
|
||||||
final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername,
|
final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername,
|
||||||
submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname,
|
submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname,
|
||||||
withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername,
|
withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername,
|
||||||
approvedByFirstname, approvedByLastname, expectedDisbursementDate, actualDisbursementDate, disbursedByUsername,
|
approvedByFirstname, approvedByLastname, expectedDisbursementDate, actualDisbursementDate, disbursedByUsername,
|
||||||
disbursedByFirstname, disbursedByLastname, closedOnDate, closedByUsername, closedByFirstname, closedByLastname,
|
disbursedByFirstname, disbursedByLastname, closedOnDate, closedByUsername, closedByFirstname, closedByLastname,
|
||||||
expectedMaturityDate, writtenOffOnDate, closedByUsername, closedByFirstname, closedByLastname);
|
expectedMaturityDate, writtenOffOnDate, closedByUsername, closedByFirstname, closedByLastname, chargedOffOnDate,
|
||||||
|
chargedOffByUsername, chargedOffByFirstname, chargedOffByLastname);
|
||||||
|
|
||||||
final BigDecimal principal = rs.getBigDecimal("principal");
|
final BigDecimal principal = rs.getBigDecimal("principal");
|
||||||
final BigDecimal approvedPrincipal = rs.getBigDecimal("approvedPrincipal");
|
final BigDecimal approvedPrincipal = rs.getBigDecimal("approvedPrincipal");
|
||||||
@@ -914,7 +928,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
penaltyChargesCharged, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff,
|
penaltyChargesCharged, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff,
|
||||||
penaltyChargesOutstanding, penaltyChargesOverdue, totalExpectedRepayment, totalRepayment, totalExpectedCostOfLoan,
|
penaltyChargesOutstanding, penaltyChargesOverdue, totalExpectedRepayment, totalRepayment, totalExpectedCostOfLoan,
|
||||||
totalCostOfLoan, totalWaived, totalWrittenOff, totalOutstanding, totalOverdue, overdueSinceDate, writeoffReasonId,
|
totalCostOfLoan, totalWaived, totalWrittenOff, totalOutstanding, totalOverdue, overdueSinceDate, writeoffReasonId,
|
||||||
writeoffReason, totalRecovered);
|
writeoffReason, totalRecovered, chargeOffReasonId, chargeOffReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupGeneralData groupData = null;
|
GroupGeneralData groupData = null;
|
||||||
@@ -1022,7 +1036,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap, maximumGap, loanSubStatus,
|
createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap, maximumGap, loanSubStatus,
|
||||||
canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
|
canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
|
||||||
fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud,
|
fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud,
|
||||||
lastClosedBusinessDate, overpaidOnDate);
|
lastClosedBusinessDate, overpaidOnDate, isChargedOff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1917,6 +1931,26 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
return loanTransactionData;
|
return loanTransactionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoanTransactionData retrieveLoanChargeOffTemplate(final Long loanId) {
|
||||||
|
|
||||||
|
final LoanAccountData loan = this.retrieveOne(loanId);
|
||||||
|
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.CHARGE_OFF);
|
||||||
|
final BigDecimal totalOutstanding = loan.getSummary() != null ? loan.getSummary().getTotalOutstanding() : null;
|
||||||
|
final BigDecimal totalPrincipalOutstanding = loan.getSummary() != null ? loan.getSummary().getPrincipalOutstanding() : null;
|
||||||
|
final BigDecimal totalInterestOutstanding = loan.getSummary() != null ? loan.getSummary().getInterestOutstanding() : null;
|
||||||
|
final BigDecimal totalFeeOutstanding = loan.getSummary() != null ? loan.getSummary().getFeeChargesOutstanding() : null;
|
||||||
|
final BigDecimal totalPenaltyOutstanding = loan.getSummary() != null ? loan.getSummary().getPenaltyChargesOutstanding() : null;
|
||||||
|
final List<CodeValueData> chargeOffReasonOptions = new ArrayList<>(
|
||||||
|
this.codeValueReadPlatformService.retrieveCodeValuesByCode(LoanApiConstants.CHARGE_OFF_REASONS));
|
||||||
|
LoanTransactionData loanTransactionData = new LoanTransactionData(null, null, null, transactionType, null, loan.getCurrency(),
|
||||||
|
DateUtils.getBusinessLocalDate(), totalOutstanding, loan.getNetDisbursalAmount(), totalPrincipalOutstanding,
|
||||||
|
totalInterestOutstanding, totalFeeOutstanding, totalPenaltyOutstanding, null, ExternalId.empty(), null, null, null, null,
|
||||||
|
false, loanId, loan.getExternalId());
|
||||||
|
loanTransactionData.setChargeOffReasonOptions(chargeOffReasonOptions);
|
||||||
|
return loanTransactionData;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Long> fetchLoansForInterestRecalculation() {
|
public Collection<Long> fetchLoansForInterestRecalculation() {
|
||||||
StringBuilder sqlBuilder = new StringBuilder();
|
StringBuilder sqlBuilder = new StringBuilder();
|
||||||
|
|||||||
+1
@@ -111,4 +111,5 @@ public interface LoanWritePlatformService {
|
|||||||
|
|
||||||
CommandProcessingResult markLoanAsFraud(Long loanId, JsonCommand command);
|
CommandProcessingResult markLoanAsFraud(Long loanId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult chargeOff(JsonCommand command);
|
||||||
}
|
}
|
||||||
|
|||||||
+52
@@ -2644,6 +2644,58 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult chargeOff(JsonCommand command) {
|
||||||
|
|
||||||
|
loanEventApiJsonValidator.validateChargeOffTransaction(command.json());
|
||||||
|
|
||||||
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
|
changes.put(LoanApiConstants.transactionDateParamName,
|
||||||
|
command.stringValueOfParameterNamed(LoanApiConstants.transactionDateParamName));
|
||||||
|
changes.put(LoanApiConstants.localeParameterName, command.locale());
|
||||||
|
changes.put(LoanApiConstants.dateFormatParameterName, command.dateFormat());
|
||||||
|
|
||||||
|
// TODO: add business logic validation (transaction date cannot be future, cannot be earlier than latest
|
||||||
|
// transactions, etc)
|
||||||
|
Loan loan = loanAssembler.assembleFrom(command.getLoanId());
|
||||||
|
final LocalDate transactionDate = command.localDateValueOfParameterNamed(LoanApiConstants.transactionDateParamName);
|
||||||
|
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
|
||||||
|
final AppUser currentUser = getAppUserIfPresent();
|
||||||
|
|
||||||
|
if (command.hasParameter(LoanApiConstants.chargeOffReasonIdParamName)) {
|
||||||
|
Long chargeOffReasonId = command.longValueOfParameterNamed(LoanApiConstants.chargeOffReasonIdParamName);
|
||||||
|
CodeValue chargeOffReason = this.codeValueRepository
|
||||||
|
.findOneByCodeNameAndIdWithNotFoundDetection(LoanApiConstants.CHARGE_OFF_REASONS, chargeOffReasonId);
|
||||||
|
changes.put(LoanApiConstants.chargeOffReasonIdParamName, chargeOffReasonId);
|
||||||
|
loan.markAsChargedOff(transactionDate, currentUser, chargeOffReason);
|
||||||
|
} else {
|
||||||
|
loan.markAsChargedOff(transactionDate, currentUser, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId);
|
||||||
|
loanTransactionRepository.saveAndFlush(chargeOffTransaction);
|
||||||
|
|
||||||
|
String noteText = command.stringValueOfParameterNamed(LoanApiConstants.noteParameterName);
|
||||||
|
if (StringUtils.isNotBlank(noteText)) {
|
||||||
|
changes.put(LoanApiConstants.noteParameterName, noteText);
|
||||||
|
final Note note = Note.loanTransactionNote(loan, chargeOffTransaction, noteText);
|
||||||
|
this.noteRepository.save(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add accounting
|
||||||
|
// TODO: add external events
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withCommandId(command.commandId()) //
|
||||||
|
.withEntityId(chargeOffTransaction.getId()) //
|
||||||
|
.withEntityExternalId(chargeOffTransaction.getExternalId()) //
|
||||||
|
.withOfficeId(loan.getOfficeId()) //
|
||||||
|
.withClientId(loan.getClientId()) //
|
||||||
|
.withGroupId(loan.getGroupId()) //
|
||||||
|
.withLoanId(command.getLoanId()) //
|
||||||
|
.with(changes).build();
|
||||||
|
}
|
||||||
|
|
||||||
private void validateIsMultiDisbursalLoanAndDisbursedMoreThanOneTranche(Loan loan) {
|
private void validateIsMultiDisbursalLoanAndDisbursedMoreThanOneTranche(Loan loan) {
|
||||||
if (!loan.isMultiDisburmentLoan()) {
|
if (!loan.isMultiDisburmentLoan()) {
|
||||||
final String errorMessage = "loan.product.does.not.support.multiple.disbursals.cannot.undo.last.disbursal";
|
final String errorMessage = "loan.product.does.not.support.multiple.disbursals.cannot.undo.last.disbursal";
|
||||||
|
|||||||
+2
@@ -291,6 +291,8 @@ public final class LoanEnumerations {
|
|||||||
LoanTransactionType.CHARGEBACK.getCode(), "Chargeback");
|
LoanTransactionType.CHARGEBACK.getCode(), "Chargeback");
|
||||||
case CHARGE_ADJUSTMENT -> new LoanTransactionEnumData(LoanTransactionType.CHARGE_ADJUSTMENT.getValue().longValue(),
|
case CHARGE_ADJUSTMENT -> new LoanTransactionEnumData(LoanTransactionType.CHARGE_ADJUSTMENT.getValue().longValue(),
|
||||||
LoanTransactionType.CHARGE_ADJUSTMENT.getCode(), "Charge Adjustment");
|
LoanTransactionType.CHARGE_ADJUSTMENT.getCode(), "Charge Adjustment");
|
||||||
|
case CHARGE_OFF -> new LoanTransactionEnumData(LoanTransactionType.CHARGE_OFF.getValue().longValue(),
|
||||||
|
LoanTransactionType.CHARGE_OFF.getCode(), "Charge-off");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -165,9 +165,9 @@ final class SelfLoansApiResourceSwagger {
|
|||||||
public String description;
|
public String description;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class GetLoansLoanIdTimeline {
|
static final class GetSelfLoanIdTimeline {
|
||||||
|
|
||||||
private GetLoansLoanIdTimeline() {}
|
private GetSelfLoanIdTimeline() {}
|
||||||
|
|
||||||
@Schema(example = "[2012, 4, 3]")
|
@Schema(example = "[2012, 4, 3]")
|
||||||
public LocalDate submittedOnDate;
|
public LocalDate submittedOnDate;
|
||||||
@@ -201,9 +201,9 @@ final class SelfLoansApiResourceSwagger {
|
|||||||
public LocalDate closedOnDate;
|
public LocalDate closedOnDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class GetLoansLoanIdSummary {
|
static final class GetSelfLoanIdSummary {
|
||||||
|
|
||||||
private GetLoansLoanIdSummary() {}
|
private GetSelfLoanIdSummary() {}
|
||||||
|
|
||||||
static final class GetLoansLoanIdEmiVariations {
|
static final class GetLoansLoanIdEmiVariations {
|
||||||
|
|
||||||
@@ -510,8 +510,8 @@ final class SelfLoansApiResourceSwagger {
|
|||||||
public GetLoansLoanIdInterestCalculationPeriodType interestCalculationPeriodType;
|
public GetLoansLoanIdInterestCalculationPeriodType interestCalculationPeriodType;
|
||||||
@Schema(example = "mifos-standard-strategy")
|
@Schema(example = "mifos-standard-strategy")
|
||||||
public String transactionProcessingStrategyCode;
|
public String transactionProcessingStrategyCode;
|
||||||
public GetLoansLoanIdTimeline timeline;
|
public GetSelfLoanIdTimeline timeline;
|
||||||
public GetLoansLoanIdSummary summary;
|
public GetSelfLoanIdSummary summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "GetSelfLoansLoanIdTransactionsTransactionIdResponse")
|
@Schema(description = "GetSelfLoansLoanIdTransactionsTransactionIdResponse")
|
||||||
@@ -554,7 +554,7 @@ final class SelfLoansApiResourceSwagger {
|
|||||||
public LocalDate date;
|
public LocalDate date;
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public Boolean manuallyReversed;
|
public Boolean manuallyReversed;
|
||||||
public GetSelfLoansLoanIdResponse.GetLoansLoanIdSummary.GetLoansLoanIdOverdueCharges.GetLoanCurrency currency;
|
public GetSelfLoansLoanIdResponse.GetSelfLoanIdSummary.GetLoansLoanIdOverdueCharges.GetLoanCurrency currency;
|
||||||
@Schema(example = "559.88")
|
@Schema(example = "559.88")
|
||||||
public Float amount;
|
public Float amount;
|
||||||
@Schema(example = "559.88")
|
@Schema(example = "559.88")
|
||||||
@@ -602,7 +602,7 @@ final class SelfLoansApiResourceSwagger {
|
|||||||
public Double percentage;
|
public Double percentage;
|
||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
public Double amountPercentageAppliedTo;
|
public Double amountPercentageAppliedTo;
|
||||||
public GetSelfLoansLoanIdResponse.GetLoansLoanIdSummary.GetLoansLoanIdOverdueCharges.GetLoanCurrency currency;
|
public GetSelfLoansLoanIdResponse.GetSelfLoanIdSummary.GetLoansLoanIdOverdueCharges.GetLoanCurrency currency;
|
||||||
@Schema(example = "100")
|
@Schema(example = "100")
|
||||||
public Float amount;
|
public Float amount;
|
||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
|
|||||||
@@ -98,4 +98,5 @@
|
|||||||
<include file="parts/0076_add_loan_transaction_enum_values.xml" relativeToChangelogFile="true" />
|
<include file="parts/0076_add_loan_transaction_enum_values.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0077_add_overpaid_date_for_loan.xml" relativeToChangelogFile="true" />
|
<include file="parts/0077_add_overpaid_date_for_loan.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0078_add_configuration_cob_bulk_event.xml" relativeToChangelogFile="true" />
|
<include file="parts/0078_add_configuration_cob_bulk_event.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="parts/0079_add_charge_off_details_to_loan.xml" relativeToChangelogFile="true" />
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||||
|
<changeSet author="fineract" id="1">
|
||||||
|
<insert tableName="m_code">
|
||||||
|
<column name="code_name" value="ChargeOffReasons"/>
|
||||||
|
<column name="is_system_defined" valueBoolean="true"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="fineract" id="2">
|
||||||
|
<addColumn tableName="m_loan">
|
||||||
|
<column defaultValueBoolean="false" name="is_charged_off" type="boolean">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column defaultValueComputed="NULL" name="charged_off_on_date" type="date"/>
|
||||||
|
<column defaultValueComputed="NULL" name="charge_off_reason_cv_id" type="BIGINT"/>
|
||||||
|
<column defaultValueComputed="NULL" name="charged_off_by_userid" type="BIGINT"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet id="3" author="fineract">
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="transaction_loan"/>
|
||||||
|
<column name="code" value="CHARGEOFF_LOAN"/>
|
||||||
|
<column name="entity_name" value="LOAN"/>
|
||||||
|
<column name="action_name" value="CHARGEOFF"/>
|
||||||
|
<column name="can_maker_checker" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+55
@@ -19,7 +19,9 @@
|
|||||||
package org.apache.fineract.integrationtests;
|
package org.apache.fineract.integrationtests;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@@ -89,6 +91,7 @@ import org.apache.fineract.integrationtests.common.savings.AccountTransferHelper
|
|||||||
import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
|
import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
|
||||||
import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
|
import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
|
||||||
import org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker;
|
import org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker;
|
||||||
|
import org.apache.fineract.integrationtests.common.system.CodeHelper;
|
||||||
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||||
@@ -6939,6 +6942,58 @@ public class ClientLoanIntegrationTest {
|
|||||||
assertEquals(0.0f, loanSummary.get("totalWaived"));
|
assertEquals(0.0f, loanSummary.get("totalWaived"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void chargeOff() {
|
||||||
|
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||||
|
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||||
|
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||||
|
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||||
|
String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6) + Utils.randomStringGenerator("is", 5);
|
||||||
|
Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1);
|
||||||
|
final Integer loanProductID = createLoanProductWithPeriodicAccrualAccountingNoInterest(assetAccount, incomeAccount, expenseAccount,
|
||||||
|
overpaymentAccount);
|
||||||
|
|
||||||
|
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2011");
|
||||||
|
|
||||||
|
final Integer loanID = applyForLoanApplication(clientID, loanProductID);
|
||||||
|
|
||||||
|
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.approveLoan("02 September 2022", loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("03 September 2022", loanID, "1000");
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
assertEquals(1000.0, loanDetails.getSummary().getTotalOutstanding());
|
||||||
|
assertFalse(loanDetails.getChargedOff());
|
||||||
|
assertNull(loanDetails.getSummary().getChargeOffReasonId());
|
||||||
|
assertNull(loanDetails.getSummary().getChargeOffReason());
|
||||||
|
assertNull(loanDetails.getTimeline().getChargedOffOnDate());
|
||||||
|
assertNull(loanDetails.getTimeline().getChargedOffByUsername());
|
||||||
|
assertNull(loanDetails.getTimeline().getChargedOffByFirstname());
|
||||||
|
assertNull(loanDetails.getTimeline().getChargedOffByLastname());
|
||||||
|
|
||||||
|
String transactionExternalId = UUID.randomUUID().toString();
|
||||||
|
this.loanTransactionHelper.chargeOffLoan((long) loanID, new PostLoansLoanIdTransactionsRequest().transactionDate("4 September 2022")
|
||||||
|
.locale("en").dateFormat("dd MMMM yyyy").externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
|
||||||
|
|
||||||
|
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
assertEquals(1000.0, loanDetails.getSummary().getTotalOutstanding());
|
||||||
|
assertTrue(loanDetails.getChargedOff());
|
||||||
|
assertEquals((long) chargeOffReasonId, loanDetails.getSummary().getChargeOffReasonId());
|
||||||
|
assertEquals(randomText, loanDetails.getSummary().getChargeOffReason());
|
||||||
|
assertEquals(LocalDate.of(2022, 9, 4), loanDetails.getTimeline().getChargedOffOnDate());
|
||||||
|
assertEquals("mifos", loanDetails.getTimeline().getChargedOffByUsername());
|
||||||
|
assertEquals("App", loanDetails.getTimeline().getChargedOffByFirstname());
|
||||||
|
assertEquals("Administrator", loanDetails.getTimeline().getChargedOffByLastname());
|
||||||
|
}
|
||||||
|
|
||||||
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID) {
|
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID) {
|
||||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||||
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
|
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
|
||||||
|
|||||||
+18
@@ -188,6 +188,7 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
|
|||||||
loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "refundbycash", null, null, null);
|
loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "refundbycash", null, null, null);
|
||||||
loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "foreclosure", null, null, null);
|
loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "foreclosure", null, null, null);
|
||||||
loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "creditBalanceRefund", null, null, null);
|
loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "creditBalanceRefund", null, null, null);
|
||||||
|
loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "charge-off", null, null, null);
|
||||||
|
|
||||||
// Check whether an external id was generated
|
// Check whether an external id was generated
|
||||||
String waiveChargeExternalIdStr = UUID.randomUUID().toString();
|
String waiveChargeExternalIdStr = UUID.randomUUID().toString();
|
||||||
@@ -1069,6 +1070,23 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
|
|||||||
.externalId(transactionExternalId2));
|
.externalId(transactionExternalId2));
|
||||||
assertEquals(transactionExternalId2, forecloseResult.getResourceExternalId());
|
assertEquals(transactionExternalId2, forecloseResult.getResourceExternalId());
|
||||||
|
|
||||||
|
String loanExternalIdStr16 = UUID.randomUUID().toString();
|
||||||
|
String transactionExternalId3 = UUID.randomUUID().toString();
|
||||||
|
applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr16);
|
||||||
|
this.loanTransactionHelper.approveLoan(loanExternalIdStr16,
|
||||||
|
new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
|
||||||
|
.expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
|
||||||
|
this.loanTransactionHelper.disburseLoan(loanExternalIdStr16,
|
||||||
|
new PostLoansLoanIdRequest().actualDisbursementDate("2 September 2022").transactionAmount(new BigDecimal("1000"))
|
||||||
|
.locale("en").dateFormat("dd MMMM yyyy"));
|
||||||
|
this.loanTransactionHelper.disburseLoan(loanExternalIdStr16,
|
||||||
|
new PostLoansLoanIdRequest().actualDisbursementDate("2 September 2022").transactionAmount(new BigDecimal("1000"))
|
||||||
|
.locale("en").dateFormat("dd MMMM yyyy"));
|
||||||
|
PostLoansLoanIdTransactionsResponse chargeOffResult = this.loanTransactionHelper.chargeOffLoan(loanExternalIdStr16,
|
||||||
|
new PostLoansLoanIdTransactionsRequest().transactionDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")
|
||||||
|
.externalId(transactionExternalId3));
|
||||||
|
assertEquals(transactionExternalId3, chargeOffResult.getResourceExternalId());
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 50, false);
|
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 50, false);
|
||||||
|
|||||||
+8
@@ -1719,4 +1719,12 @@ public class LoanTransactionHelper extends IntegrationTest {
|
|||||||
public PostLoansLoanIdTransactionsResponse forecloseLoan(String loanExternalId, PostLoansLoanIdTransactionsRequest request) {
|
public PostLoansLoanIdTransactionsResponse forecloseLoan(String loanExternalId, PostLoansLoanIdTransactionsRequest request) {
|
||||||
return ok(fineract().loanTransactions.executeLoanTransaction1(loanExternalId, request, "foreclosure"));
|
return ok(fineract().loanTransactions.executeLoanTransaction1(loanExternalId, request, "foreclosure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PostLoansLoanIdTransactionsResponse chargeOffLoan(String loanExternalId, PostLoansLoanIdTransactionsRequest request) {
|
||||||
|
return ok(fineract().loanTransactions.executeLoanTransaction1(loanExternalId, request, "charge-off"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostLoansLoanIdTransactionsResponse chargeOffLoan(Long loanId, PostLoansLoanIdTransactionsRequest request) {
|
||||||
|
return ok(fineract().loanTransactions.executeLoanTransaction(loanId, request, "charge-off"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -34,6 +34,7 @@ public final class CodeHelper {
|
|||||||
private static final String COUNTRY_CODE_NAME = "COUNTRY";
|
private static final String COUNTRY_CODE_NAME = "COUNTRY";
|
||||||
private static final String STATE_CODE_NAME = "STATE";
|
private static final String STATE_CODE_NAME = "STATE";
|
||||||
private static final String ADDRESS_TYPE_CODE_NAME = "ADDRESS_TYPE";
|
private static final String ADDRESS_TYPE_CODE_NAME = "ADDRESS_TYPE";
|
||||||
|
private static final String CHARGE_OFF_REASONS_CODE_NAME = "ChargeOffReasons";
|
||||||
|
|
||||||
private CodeHelper() {
|
private CodeHelper() {
|
||||||
|
|
||||||
@@ -175,6 +176,13 @@ public final class CodeHelper {
|
|||||||
return createCodeValue(requestSpec, responseSpec, countryCode, countryName, position);
|
return createCodeValue(requestSpec, responseSpec, countryCode, countryName, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Integer createChargeOffCodeValue(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||||
|
final String value, final Integer position) {
|
||||||
|
HashMap<String, Object> code = getCodeByName(requestSpec, responseSpec, CHARGE_OFF_REASONS_CODE_NAME);
|
||||||
|
Integer countryCode = (Integer) code.get("id");
|
||||||
|
return createCodeValue(requestSpec, responseSpec, countryCode, value, position);
|
||||||
|
}
|
||||||
|
|
||||||
public static Integer createCodeValue(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
public static Integer createCodeValue(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||||
final Integer codeId, final String codeValueName, final Integer position) {
|
final Integer codeId, final String codeValueName, final Integer position) {
|
||||||
return (Integer) createCodeValue(requestSpec, responseSpec, codeId, codeValueName, position, SUBRESPONSE_ID_ATTRIBUTE_NAME);
|
return (Integer) createCodeValue(requestSpec, responseSpec, codeId, codeValueName, position, SUBRESPONSE_ID_ATTRIBUTE_NAME);
|
||||||
|
|||||||
Reference in New Issue
Block a user