Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,46 @@ public CommandWrapperBuilder deleteWorkingCapitalLoanApplication() {
return this;
}

public CommandWrapperBuilder approveWorkingCapitalLoanApplication(final Long loanId) {
this.actionName = "APPROVE";
this.entityName = "WORKINGCAPITALLOAN";
this.entityId = loanId;
this.href = "/workingcapitalloans/" + loanId;
return this;
}

public CommandWrapperBuilder rejectWorkingCapitalLoanApplication(final Long loanId) {
this.actionName = "REJECT";
this.entityName = "WORKINGCAPITALLOAN";
this.entityId = loanId;
this.href = "/workingcapitalloans/" + loanId;
return this;
}

public CommandWrapperBuilder undoWorkingCapitalLoanApplicationApproval(final Long loanId) {
this.actionName = "APPROVALUNDO";
this.entityName = "WORKINGCAPITALLOAN";
this.entityId = loanId;
this.href = "/workingcapitalloans/" + loanId;
return this;
}

public CommandWrapperBuilder disburseWorkingCapitalLoanApplication(final Long loanId) {
this.actionName = "DISBURSE";
this.entityName = "WORKINGCAPITALLOAN";
this.entityId = loanId;
this.href = "/workingcapitalloans/" + loanId;
return this;
}

public CommandWrapperBuilder undoWorkingCapitalLoanApplicationDisbursal(final Long loanId) {
this.actionName = "DISBURSALUNDO";
this.entityName = "WORKINGCAPITALLOAN";
this.entityId = loanId;
this.href = "/workingcapitalloans/" + loanId;
return this;
}

public CommandWrapperBuilder createClientIdentifier(final Long clientId) {
this.actionName = "CREATE";
this.entityName = "CLIENTIDENTIFIER";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
<class>org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanBalance</class>
<class>org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanDisbursementDetails</class>
<class>org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanPaymentAllocationRule</class>
<class>org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction</class>
<class>org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransactionAllocation</class>
<class>org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransactionPaymentDetail</class>

<!-- Loan Module Converters -->
<class>org.apache.fineract.portfolio.loanproduct.domain.AllocationTypeListConverter</class>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,24 @@ private WorkingCapitalLoanConstants() {
public static final String submittedOnNoteParameterName = "submittedOnNote";
public static final String totalPaymentParamName = "totalPayment";
public static final String principalAmountParamName = "principalAmount";

// Approval / Rejection / Undo-approval parameters
public static final String RESOURCE_NAME = WCL_RESOURCE_NAME;
public static final String approvedOnDateParamName = "approvedOnDate";
public static final String approvedLoanAmountParamName = "approvedLoanAmount";
public static final String expectedDisbursementDateParamName = "expectedDisbursementDate";
public static final String discountAmountParamName = "discountAmount";
public static final String noteParamName = "note";
public static final String rejectedOnDateParamName = "rejectedOnDate";

// Disbursal / Undo disbursal parameters
public static final String actualDisbursementDateParamName = "actualDisbursementDate";
public static final String transactionAmountParamName = "transactionAmount";
public static final String paymentDetailsParamName = "paymentDetails";
public static final String paymentTypeIdParamName = "paymentTypeId";
public static final String accountNumberParamName = "accountNumber";
public static final String checkNumberParamName = "checkNumber";
public static final String routingCodeParamName = "routingCode";
public static final String receiptNumberParamName = "receiptNumber";
public static final String bankNumberParamName = "bankNumber";
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
import org.apache.fineract.infrastructure.core.api.jersey.Pagination;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.workingcapitalloan.WorkingCapitalLoanConstants;
Expand Down Expand Up @@ -190,6 +192,36 @@ public CommandProcessingResult deleteLoanApplication(
return deleteLoanApplication(null, loanExternalId);
}

@POST
@Path("{loanId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(operationId = "stateTransitionWorkingCapitalLoanById", summary = "Approve/Reject/Undo-approve/Disburse/Undo-disburse a Working Capital Loan", description = "Mandatory command query parameter: approve, reject, undoapproval, disburse, or undodisbursal.")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PostWorkingCapitalLoansLoanIdRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PostWorkingCapitalLoansLoanIdResponse.class))) })
public CommandProcessingResult stateTransitionById(
@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId,
@QueryParam("command") @Parameter(description = "command", required = true) final String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
return handleStateTransition(loanId, null, commandParam, apiRequestBodyAsJson);
}

@POST
@Path("external-id/{loanExternalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(operationId = "stateTransitionWorkingCapitalLoanByExternalId", summary = "Approve/Reject/Undo-approve/Disburse/Undo-disburse a Working Capital Loan by external id", description = "Mandatory command query parameter: approve, reject, undoapproval, disburse, or undodisbursal.")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PostWorkingCapitalLoansLoanIdRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PostWorkingCapitalLoansLoanIdResponse.class))) })
public CommandProcessingResult stateTransitionByExternalId(
@PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId,
@QueryParam("command") @Parameter(description = "command", required = true) final String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
return handleStateTransition(null, loanExternalId, commandParam, apiRequestBodyAsJson);
}

private CommandProcessingResult modifyLoanApplication(final Long loanId, final String loanExternalIdStr,
final String apiRequestBodyAsJson) {
final Long resolvedLoanId = loanId != null ? loanId
Expand All @@ -212,4 +244,33 @@ private CommandProcessingResult deleteLoanApplication(final Long loanId, final S
.build();
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}

private CommandProcessingResult handleStateTransition(final Long loanId, final String loanExternalIdStr, final String commandParam,
final String apiRequestBodyAsJson) {
final Long resolvedLoanId = loanId != null ? loanId
: readPlatformService.getResolvedLoanId(ExternalIdFactory.produce(loanExternalIdStr));
if (resolvedLoanId == null) {
throw new WorkingCapitalLoanNotFoundException(ExternalIdFactory.produce(loanExternalIdStr));
}

final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandWrapper commandRequest = null;
if (CommandParameterUtil.is(commandParam, "approve")) {
commandRequest = builder.approveWorkingCapitalLoanApplication(resolvedLoanId).build();
} else if (CommandParameterUtil.is(commandParam, "reject")) {
commandRequest = builder.rejectWorkingCapitalLoanApplication(resolvedLoanId).build();
} else if (CommandParameterUtil.is(commandParam, "undoapproval")) {
commandRequest = builder.undoWorkingCapitalLoanApplicationApproval(resolvedLoanId).build();
} else if (CommandParameterUtil.is(commandParam, "disburse")) {
commandRequest = builder.disburseWorkingCapitalLoanApplication(resolvedLoanId).build();
} else if (CommandParameterUtil.is(commandParam, "undodisbursal")) {
commandRequest = builder.undoWorkingCapitalLoanApplicationDisbursal(resolvedLoanId).build();
}

if (commandRequest == null) {
throw new UnrecognizedQueryParamException("command", commandParam);
}

return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ private GetWorkingCapitalLoansLoanIdResponse() {}
public List<GetDisbursementDetail> disbursementDetails;
/** Running balances (principal outstanding, total payment, etc.). */
public GetBalance balance;
@Schema(description = "Transaction history (e.g. disbursement).")
public List<WorkingCapitalLoanTransactionsApiResourceSwagger.GetWorkingCapitalLoanTransactionIdResponse> transactions;
}

@Schema(description = "Working capital loan running balances")
Expand Down Expand Up @@ -244,6 +246,19 @@ private GetPaymentAllocation() {}
public List<GetPaymentAllocationOrder> paymentAllocationOrder;
}

@Schema(description = "Loan transaction type enum data (same as basic loan)")
public static final class LoanTransactionEnumData {

private LoanTransactionEnumData() {}

@Schema(example = "1")
public Long id;
@Schema(example = "loanTransactionType.disbursement")
public String code;
@Schema(example = "Disbursement")
public String value;
}

@Schema(description = "GetPaymentAllocationOrder")
public static final class GetPaymentAllocationOrder {

Expand All @@ -260,17 +275,17 @@ public static final class PostWorkingCapitalLoansRequest {

private PostWorkingCapitalLoansRequest() {}

@Schema(example = "1", required = true)
@Schema(example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
public Long clientId;
@Schema(example = "1", required = true)
@Schema(example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
public Long productId;
@Schema(example = "1")
public Long fundId;
@Schema(example = "WCL-1")
public String accountNo;
@Schema(example = "ext-id-001")
public String externalId;
@Schema(example = "10000.00", required = true, description = "Principal (disbursement) amount")
@Schema(example = "10000.00", requiredMode = Schema.RequiredMode.REQUIRED, description = "Principal (disbursement) amount")
public BigDecimal principalAmount;
@Schema(example = "10500.00")
public BigDecimal totalPayment;
Expand Down Expand Up @@ -335,6 +350,25 @@ private PostWorkingCapitalLoansResponse() {}
public Long loanId;
}

@Schema(description = "Payment details for disbursement (Account No, Cheque No, Routing Code, Receipt No, Bank code)")
public static final class PostWorkingCapitalLoansLoanIdDisbursementPaymentDetails {

private PostWorkingCapitalLoansLoanIdDisbursementPaymentDetails() {}

@Schema(example = "1", description = "Payment type id")
public Integer paymentTypeId;
@Schema(example = "acc123", description = "Account No")
public String accountNumber;
@Schema(example = "che123", description = "Cheque No")
public String checkNumber;
@Schema(example = "rou123", description = "Routing Code")
public String routingCode;
@Schema(example = "rec123", description = "Receipt No")
public String receiptNumber;
@Schema(example = "ban123", description = "Bank code")
public String bankNumber;
}

@Schema(description = "PutWorkingCapitalLoansLoanIdRequest")
public static final class PutWorkingCapitalLoansLoanIdRequest {

Expand Down Expand Up @@ -397,4 +431,53 @@ private DeleteWorkingCapitalLoansLoanIdResponse() {}
@Schema(example = "1")
public Long resourceId;
}

@Schema(description = "PostWorkingCapitalLoansLoanIdResponse")
public static final class PostWorkingCapitalLoansLoanIdResponse {

private PostWorkingCapitalLoansLoanIdResponse() {}

@Schema(example = "2")
public Long officeId;
@Schema(example = "6")
public Long clientId;
@Schema(example = "3")
public Long loanId;
@Schema(example = "3")
public Long resourceId;
@Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
public String resourceExternalId;
public Object changes;
}

@Schema(description = "Request for state transition: approve, reject, undoapproval, disburse, undodisbursal")
public static final class PostWorkingCapitalLoansLoanIdRequest {

private PostWorkingCapitalLoansLoanIdRequest() {}

@Schema(example = "15 January 2024", description = "Date of approval")
public String approvedOnDate;
@Schema(example = "10000.00", description = "Approved principal amount (optional, defaults to proposed principal)")
public BigDecimal approvedLoanAmount;
@Schema(example = "1 February 2024", description = "Expected disbursement date")
public String expectedDisbursementDate;
@Schema(example = "0.0", description = "Discount amount (cannot exceed creation-time discount)")
public BigDecimal discountAmount;
@Schema(example = "15 January 2024", description = "Date of rejection")
public String rejectedOnDate;
@Schema(example = "Approval/Rejection/Disbursal Note")
public String note;
@Schema(example = "en_GB")
public String locale;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "28 June 2024", description = "Required for disburse - Actual Disbursement date")
public String actualDisbursementDate;
@Schema(example = "1000", description = "Disbursement amount; required for disburse. Cannot exceed approved principal.")
public BigDecimal transactionAmount;
@Schema(example = "ext-disburse-001", description = "External ID; optional for disburse")
public String externalId;
@Schema(description = "Payment details (Account No, Cheque No, Routing Code, Receipt No, Bank code)")
public PostWorkingCapitalLoansLoanIdDisbursementPaymentDetails paymentDetails;
}
}
Loading
Loading