Added reschedule loans api resource to Fineract Client and batch; minor swagger polishing

This commit is contained in:
Arnold Galovics
2022-07-29 20:36:58 +02:00
committed by Arnold Gálovics
parent b21cdf59b1
commit 87204d5a8a
14 changed files with 1154 additions and 195 deletions
@@ -103,6 +103,7 @@ import org.apache.fineract.client.services.RecurringDepositAccountTransactionsAp
import org.apache.fineract.client.services.RecurringDepositProductApi; import org.apache.fineract.client.services.RecurringDepositProductApi;
import org.apache.fineract.client.services.ReportMailingJobsApi; import org.apache.fineract.client.services.ReportMailingJobsApi;
import org.apache.fineract.client.services.ReportsApi; import org.apache.fineract.client.services.ReportsApi;
import org.apache.fineract.client.services.RescheduleLoansApi;
import org.apache.fineract.client.services.RolesApi; import org.apache.fineract.client.services.RolesApi;
import org.apache.fineract.client.services.RunReportsApi; import org.apache.fineract.client.services.RunReportsApi;
import org.apache.fineract.client.services.SavingsAccountApi; import org.apache.fineract.client.services.SavingsAccountApi;
@@ -234,6 +235,7 @@ public final class FineractClient {
public final RecurringDepositProductApi recurringDepositProducts; public final RecurringDepositProductApi recurringDepositProducts;
public final ReportMailingJobsApi reportMailingJobs; public final ReportMailingJobsApi reportMailingJobs;
public final ReportsApi reports; public final ReportsApi reports;
public final RescheduleLoansApi rescheduleLoans;
public final RolesApi roles; public final RolesApi roles;
public final RunReportsApi reportsRun; public final RunReportsApi reportsRun;
public final SavingsAccountApi savingsAccounts; public final SavingsAccountApi savingsAccounts;
@@ -346,6 +348,7 @@ public final class FineractClient {
recurringDepositProducts = retrofit.create(RecurringDepositProductApi.class); recurringDepositProducts = retrofit.create(RecurringDepositProductApi.class);
reportMailingJobs = retrofit.create(ReportMailingJobsApi.class); reportMailingJobs = retrofit.create(ReportMailingJobsApi.class);
reports = retrofit.create(ReportsApi.class); reports = retrofit.create(ReportsApi.class);
rescheduleLoans = retrofit.create(RescheduleLoansApi.class);
roles = retrofit.create(RolesApi.class); roles = retrofit.create(RolesApi.class);
reportsRun = retrofit.create(RunReportsApi.class); reportsRun = retrofit.create(RunReportsApi.class);
savingsAccounts = retrofit.create(SavingsAccountApi.class); savingsAccounts = retrofit.create(SavingsAccountApi.class);
@@ -102,6 +102,8 @@ public class CommandStrategyProvider {
"approveLoanCommandStrategy"); "approveLoanCommandStrategy");
this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\?command=disburse").method("POST").build(), this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\?command=disburse").method("POST").build(),
"disburseLoanCommandStrategy"); "disburseLoanCommandStrategy");
this.commandStrategies.put(CommandContext.resource("rescheduleloans").method("POST").build(),
"createLoanRescheduleRequestCommandStrategy");
this.commandStrategies.put(CommandContext.resource("rescheduleloans\\/\\d+\\?command=approve").method("POST").build(), this.commandStrategies.put(CommandContext.resource("rescheduleloans\\/\\d+\\?command=approve").method("POST").build(),
"approveLoanRescheduleCommandStrategy"); "approveLoanRescheduleCommandStrategy");
this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\/\\d+").method("GET").build(), this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\/\\d+").method("GET").build(),
@@ -0,0 +1,65 @@
/**
* 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.batch.command.internal;
import javax.ws.rs.core.UriInfo;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.api.RescheduleLoansApiResource;
import org.springframework.stereotype.Component;
/**
* Implements {@link CommandStrategy} and applies a new reschedule loan request on an existing loan. It passes the
* contents of the body from the BatchRequest to {@link RescheduleLoansApiResource} and gets back the response. This
* class will also catch any errors raised by {@link RescheduleLoansApiResource} and map those errors to appropriate
* status codes in BatchResponse.
*
* @see CommandStrategy
* @see BatchRequest
* @see BatchResponse
*/
@Component
@RequiredArgsConstructor
public class CreateLoanRescheduleRequestCommandStrategy implements CommandStrategy {
private final RescheduleLoansApiResource rescheduleLoansApiResource;
@Override
public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
final String responseBody;
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
// Calls 'createLoanRescheduleRequest' function from
// 'RescheduleLoansApiResource' to create a reschedule request on an existing loan
responseBody = rescheduleLoansApiResource.createLoanRescheduleRequest(request.getBody());
response.setStatusCode(200);
// Sets the body of the response after savings is successfully
// applied
response.setBody(responseBody);
return response;
}
}
@@ -320,7 +320,8 @@ final class ClientsApiResourceSwagger {
public List<PostClientsDatatable> datatables; public List<PostClientsDatatable> datatables;
@Schema(description = "Address requests") @Schema(description = "Address requests")
public List<PostClientsAddressRequest> address; public List<PostClientsAddressRequest> address;
@Schema(example = "test@test.com")
public String emailAddress;
} }
@Schema(description = "PostClientsResponse") @Schema(description = "PostClientsResponse")
@@ -291,44 +291,44 @@ final class LoansApiResourceSwagger {
public Double principalDue; public Double principalDue;
@Schema(example = "200.000000") @Schema(example = "200.000000")
public Double principalPaid; public Double principalPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long principalWrittenOff; public Double principalWrittenOff;
@Schema(example = "20.000000") @Schema(example = "20.000000")
public Double principalOutstanding; public Double principalOutstanding;
@Schema(example = "20.000000") @Schema(example = "20.000000")
public Double principalLoanBalanceOutstanding; public Double principalLoanBalanceOutstanding;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestOriginalDue; public Double interestOriginalDue;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestDue; public Double interestDue;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestPaid; public Double interestPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestWaived; public Double interestWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestWrittenOff; public Double interestWrittenOff;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestOutstanding; public Double interestOutstanding;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesDue; public Double feeChargesDue;
@Schema(example = "20") @Schema(example = "20.000000")
public Long feeChargesPaid; public Double feeChargesPaid;
@Schema(example = "20") @Schema(example = "20.000000")
public Long feeChargesWaived; public Double feeChargesWaived;
@Schema(example = "20") @Schema(example = "20.000000")
public Long feeChargesWrittenOff; public Double feeChargesWrittenOff;
@Schema(example = "20") @Schema(example = "20.000000")
public Long feeChargesOutstanding; public Double feeChargesOutstanding;
@Schema(example = "20") @Schema(example = "20.000000")
public Long penaltyChargesDue; public Double penaltyChargesDue;
@Schema(example = "20") @Schema(example = "20.000000")
public Long penaltyChargesPaid; public Double penaltyChargesPaid;
@Schema(example = "20") @Schema(example = "20.000000")
public Long penaltyChargesWaived; public Double penaltyChargesWaived;
@Schema(example = "20") @Schema(example = "20.000000")
public Long penaltyChargesWrittenOff; public Double penaltyChargesWrittenOff;
@Schema(example = "20") @Schema(example = "20.000000")
public Long penaltyChargesOutstanding; public Double penaltyChargesOutstanding;
@Schema(example = "20.000000") @Schema(example = "20.000000")
public Double totalOriginalDueForPeriod; public Double totalOriginalDueForPeriod;
@Schema(example = "20.000000") @Schema(example = "20.000000")
@@ -337,20 +337,48 @@ final class LoansApiResourceSwagger {
public Double totalPaidForPeriod; public Double totalPaidForPeriod;
@Schema(example = "20.000000") @Schema(example = "20.000000")
public Double totalPaidInAdvanceForPeriod; public Double totalPaidInAdvanceForPeriod;
@Schema(example = "20") @Schema(example = "20.000000")
public Long totalPaidLateForPeriod; public Double totalPaidLateForPeriod;
@Schema(example = "20") @Schema(example = "20.000000")
public Long totalWaivedForPeriod; public Double totalWaivedForPeriod;
@Schema(example = "20") @Schema(example = "20.000000")
public Long totalWrittenOffForPeriod; public Double totalWrittenOffForPeriod;
@Schema(example = "200.000000") @Schema(example = "200.000000")
public Double totalOutstandingForPeriod; public Double totalOutstandingForPeriod;
@Schema(example = "20") @Schema(example = "20.000000")
public Long totalActualCostOfLoanForPeriod; public Double totalActualCostOfLoanForPeriod;
@Schema(example = "200.000000") @Schema(example = "200.000000")
public Double totalInstallmentAmountForPeriod; public Double totalInstallmentAmountForPeriod;
} }
static final class GetLoansLoanIdDisbursementDetails {
private GetLoansLoanIdDisbursementDetails() {}
@Schema(example = "71")
public Integer id;
@Schema(example = "[2022, 07, 01]")
public LocalDate expectedDisbursementDate;
@Schema(example = "[2022, 07, 01]")
public LocalDate actualDisbursementDate;
@Schema(example = "22000.000000")
public Double principal;
@Schema(example = "22000.000000")
public Double netDisbursalAmount;
@Schema(example = "1")
public String loanChargeId;
@Schema(example = "22000.000000")
public Double chargeAmount;
@Schema(example = "22000.000000")
public Double waivedChargeAmount;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "de_DE")
public String locale;
@Schema(example = "some note")
public String note;
}
static final class GetLoansLoanIdSummary { static final class GetLoansLoanIdSummary {
private GetLoansLoanIdSummary() {} private GetLoansLoanIdSummary() {}
@@ -370,20 +398,6 @@ final class LoansApiResourceSwagger {
public Long accountNo; public Long accountNo;
} }
static final class GetLoansLoanIdDisbursementDetails {
private GetLoansLoanIdDisbursementDetails() {}
@Schema(example = "71")
public Integer id;
@Schema(example = "[2013, 11, 1]")
public LocalDate expectedDisbursementDate;
@Schema(example = "22000.000000")
public Float principal;
@Schema(example = "22000.000000")
public Float approvedPrincipal;
}
static final class GetLoansLoanIdOverdueCharges { static final class GetLoansLoanIdOverdueCharges {
private GetLoansLoanIdOverdueCharges() {} private GetLoansLoanIdOverdueCharges() {}
@@ -457,78 +471,84 @@ final class LoansApiResourceSwagger {
} }
public GetLoansLoanIdCurrency currency; public GetLoansLoanIdCurrency currency;
@Schema(example = "1000000") @Schema(example = "1000000.000000")
public Long principalDisbursed; public Double principalDisbursed;
@Schema(example = "0") @Schema(example = "0.000000")
public Long principalPaid; public Double principalPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long principalWrittenOff; public Double principalWrittenOff;
@Schema(example = "1000000") @Schema(example = "1000000.000000")
public Long principalOutstanding; public Double principalOutstanding;
@Schema(example = "833333.3") @Schema(example = "833333.300000")
public Double principalOverdue; public Double principalOverdue;
@Schema(example = "240000") @Schema(example = "240000.000000")
public Long interestCharged; public Double interestCharged;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestPaid; public Double interestPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestWaived; public Double interestWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestWrittenOff; public Double interestWrittenOff;
@Schema(example = "240000") @Schema(example = "240000.000000")
public Long interestOutstanding; public Double interestOutstanding;
@Schema(example = "200000") @Schema(example = "200000.000000")
public Long interestOverdue; public Double interestOverdue;
@Schema(example = "18000") @Schema(example = "18000.000000")
public Long feeChargesCharged; public Double feeChargesCharged;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesDueAtDisbursementCharged; public Double feeChargesDueAtDisbursementCharged;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesPaid; public Double feeChargesPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesWaived; public Double feeChargesWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesWrittenOff; public Double feeChargesWrittenOff;
@Schema(example = "18000") @Schema(example = "18000.000000")
public Long feeChargesOutstanding; public Double feeChargesOutstanding;
@Schema(example = "15000") @Schema(example = "15000.000000")
public Long feeChargesOverdue; public Double feeChargesOverdue;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesCharged; public Double penaltyChargesCharged;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesPaid; public Double penaltyChargesPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesWaived; public Double penaltyChargesWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesWrittenOff; public Double penaltyChargesWrittenOff;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesOutstanding; public Double penaltyChargesOutstanding;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesOverdue; public Double penaltyChargesOverdue;
@Schema(example = "1258000") @Schema(example = "1258000.000000")
public Long totalExpectedRepayment; public Double totalExpectedRepayment;
@Schema(example = "0") @Schema(example = "0.000000")
public Long totalRepayment; public Double totalRepayment;
@Schema(example = "258000") @Schema(example = "258000.000000")
public Long totalExpectedCostOfLoan; public Double totalExpectedCostOfLoan;
@Schema(example = "0") @Schema(example = "0.000000")
public Long totalCostOfLoan; public Double totalCostOfLoan;
@Schema(example = "0") @Schema(example = "0.000000")
public Long totalWaived; public Double totalWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long totalWrittenOff; public Double totalWrittenOff;
@Schema(example = "1258000") @Schema(example = "1258000.000000")
public Long totalOutstanding; public Double totalOutstanding;
@Schema(example = "1048333.3") @Schema(example = "1048333.30000")
public Double totalOverdue; public Double totalOverdue;
@Schema(example = "2456.30000")
public Double totalRecovered;
@Schema(example = "[2012, 5, 10]") @Schema(example = "[2012, 5, 10]")
public LocalDate overdueSinceDate; public LocalDate overdueSinceDate;
@Schema(example = "1")
public Long writeoffReasonId;
@Schema(example = "reason")
public String writeoffReason;
public GetLoansLoanIdLinkedAccount linkedAccount; public GetLoansLoanIdLinkedAccount linkedAccount;
public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails; public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
@Schema(example = "1100.000000") @Schema(example = "1100.000000")
public Float fixedEmiAmount; public Double fixedEmiAmount;
@Schema(example = "35000") @Schema(example = "35000.000000")
public Long maxOutstandingLoanBalance; public Double maxOutstandingLoanBalance;
@Schema(example = "false") @Schema(example = "false")
public Boolean canDisburse; public Boolean canDisburse;
public Set<GetLoansLoanIdEmiVariations> emiAmountVariations; public Set<GetLoansLoanIdEmiVariations> emiAmountVariations;
@@ -539,6 +559,343 @@ final class LoansApiResourceSwagger {
public Set<GetLoansLoanIdOverdueCharges> overdueCharges; public Set<GetLoansLoanIdOverdueCharges> overdueCharges;
} }
static final class GetLoansLoanIdPaymentType {
private GetLoansLoanIdPaymentType() {}
@Schema(example = "11")
public Integer id;
@Schema(example = "Cash")
public String name;
@Schema(example = "Cash Payment")
public String description;
@Schema(example = "true")
public Boolean isCashPayment;
@Schema(example = "0")
public Long position;
}
static final class GetLoansLoanIdTransactions {
private GetLoansLoanIdTransactions() {}
static final class GetLoansLoanIdLoanTransactionEnumData {
private GetLoansLoanIdLoanTransactionEnumData() {}
@Schema(example = "1")
public Long id;
@Schema(example = "loanTransactionType.repayment")
public String code;
@Schema(example = "2")
public String value;
@Schema(example = "false")
public boolean disbursement;
@Schema(example = "false")
public boolean repaymentAtDisbursement;
@Schema(example = "true")
public boolean repayment;
@Schema(example = "false")
public boolean merchantIssuedRefund;
@Schema(example = "false")
public boolean payoutRefund;
@Schema(example = "false")
public boolean goodwillCredit;
@Schema(example = "false")
public boolean contra;
@Schema(example = "false")
public boolean waiveInterest;
@Schema(example = "false")
public boolean waiveCharges;
@Schema(example = "false")
public boolean accrual;
@Schema(example = "false")
public boolean writeOff;
@Schema(example = "false")
public boolean recoveryRepayment;
@Schema(example = "false")
public boolean initiateTransfer;
@Schema(example = "false")
public boolean approveTransfer;
@Schema(example = "false")
public boolean withdrawTransfer;
@Schema(example = "false")
public boolean rejectTransfer;
@Schema(example = "false")
public boolean chargePayment;
@Schema(example = "false")
public boolean refund;
@Schema(example = "false")
public boolean refundForActiveLoans;
@Schema(example = "false")
public boolean creditBalanceRefund;
}
static final class GetLoansLoanIdPaymentDetailData {
private GetLoansLoanIdPaymentDetailData() {}
@Schema(example = "62")
public Integer id;
public GetLoansLoanIdPaymentType paymentType;
@Schema(example = "acc123")
public String accountNumber;
@Schema(example = "che123")
public String checkNumber;
@Schema(example = "rou123")
public String routingCode;
@Schema(example = "rec123")
public String receiptNumber;
@Schema(example = "ban123")
public String bankNumber;
}
static final class GetLoansLoanIdLoanChargePaidByData {
private GetLoansLoanIdLoanChargePaidByData() {}
@Schema(example = "11")
public Long id;
@Schema(example = "100.000000")
public Double amount;
@Schema(example = "9679")
public Integer installmentNumber;
@Schema(example = "1")
public Long chargeId;
@Schema(example = "636")
public Long transactionId;
@Schema(example = "name")
public String name;
}
static final class GetLoansLoanIdCodeValueData {
private GetLoansLoanIdCodeValueData() {}
@Schema(example = "1")
public Long id;
@Schema(example = "code name")
public String name;
@Schema(example = "0")
public Integer position;
@Schema(example = "code description")
public String description;
@Schema(example = "true")
public Boolean active;
@Schema(example = "false")
public Boolean mandatory;
}
static final class GetLoansLoanIdLoanRepaymentScheduleInstallmentData {
private GetLoansLoanIdLoanRepaymentScheduleInstallmentData() {}
@Schema(example = "1")
public Long id;
@Schema(example = "11")
public Integer installmentId;
@Schema(example = "[2022, 07, 01]")
public LocalDate date;
@Schema(example = "100.000000")
public BigDecimal amount;
}
@Schema(example = "1")
public Long id;
@Schema(example = "1")
public Long officeId;
@Schema(example = "Head Office")
public String officeName;
@Schema(description = "Transaction type")
public GetLoansLoanIdLoanTransactionEnumData type;
@Schema(example = "[2022, 07, 01]")
public LocalDate date;
@Schema(description = "Currency")
public GetLoansLoanIdCurrency currency;
@Schema(description = "Payment detail")
public GetLoansLoanIdPaymentDetailData paymentDetailData;
@Schema(example = "100.000000")
public Double amount;
@Schema(example = "100.000000")
public Double netDisbursalAmount;
@Schema(example = "100.000000")
public Double principalPortion;
@Schema(example = "100.000000")
public Double interestPortion;
@Schema(example = "100.000000")
public Double feeChargesPortion;
@Schema(example = "100.000000")
public Double penaltyChargesPortion;
@Schema(example = "100.000000")
public Double overpaymentPortion;
@Schema(example = "100.000000")
public Double unrecognizedIncomePortion;
@Schema(example = "3")
public String externalId;
@Schema(example = "100.000000")
public Double fixedEmiAmount;
@Schema(example = "100.000000")
public Double outstandingLoanBalance;
@Schema(example = "[2022, 07, 01]")
public LocalDate submittedOnDate;
public boolean manuallyReversed;
@Schema(example = "[2022, 07, 01]")
public LocalDate possibleNextRepaymentDate;
@Schema(description = "List of GetLoansLoanIdLoanChargePaidByData")
public List<GetLoansLoanIdLoanChargePaidByData> loanChargePaidByList;
@Schema(description = "List of GetLoansLoanIdPaymentType")
public List<GetLoansLoanIdPaymentType> paymentTypeOptions;
@Schema(description = "List of GetLoansLoanIdCodeValueData")
public List<GetLoansLoanIdCodeValueData> writeOffReasonOptions;
@Schema(example = "4")
public Integer numberOfRepayments;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "de_DE")
public String locale;
@Schema(example = "100.000000")
public Double transactionAmount;
@Schema(example = "[2022, 07, 01]")
public LocalDate transactionDate;
@Schema(example = "101")
public Long paymentTypeId;
@Schema(example = "acct123")
public String accountNumber;
@Schema(example = "10001")
public Integer checkNumber;
@Schema(example = "6337")
public Integer routingCode;
@Schema(example = "67863")
public Integer receiptNumber;
@Schema(example = "34645568")
public Integer bankNumber;
@Schema(example = "7327")
public Long accountId;
@Schema(example = "repayment")
public String transactionType;
@Schema(description = "List of GetLoansLoanIdLoanRepaymentScheduleInstallmentData")
public List<GetLoansLoanIdLoanRepaymentScheduleInstallmentData> loanRepaymentScheduleInstallments;
}
static final class GetLoansLoanIdLoanChargeData {
private GetLoansLoanIdLoanChargeData() {}
static final class GetLoansLoanIdEnumOptionData {
private GetLoansLoanIdEnumOptionData() {}
@Schema(example = "2")
public Long id;
@Schema(example = "chargeTimeType.specifiedDueDate")
public String code;
@Schema(example = "Specified due date")
public String value;
}
static final class GetLoansLoanIdLoanInstallmentChargeData {
private GetLoansLoanIdLoanInstallmentChargeData() {}
@Schema(example = "2")
public Integer installmentNumber;
@Schema(example = "[2022, 07, 01]")
public LocalDate dueDate;
@Schema(example = "13.560000")
public Double amount;
@Schema(example = "13.560000")
public Double amountOutstanding;
@Schema(example = "13.560000")
public Double amountWaived;
@Schema(example = "false")
public boolean paid;
@Schema(example = "false")
public boolean waived;
@Schema(example = "13.560000")
public Double amountAccrued;
@Schema(example = "13.560000")
public Double amountUnrecognized;
}
@Schema(example = "3")
public Long id;
@Schema(example = "5")
public Long chargeId;
@Schema(example = "snooze fee")
public String name;
@Schema(description = "Enum option data")
public GetLoansLoanIdEnumOptionData chargeTimeType;
@Schema(example = "[2022, 07, 01]")
public LocalDate dueDate;
@Schema(description = "Enum option data")
public GetLoansLoanIdEnumOptionData chargeCalculationType;
@Schema(example = "3.400000")
public Double percentage;
@Schema(example = "13.560000")
public Double amountPercentageAppliedTo;
@Schema(description = "currency")
public GetLoansLoanIdCurrency currency;
@Schema(example = "102.000000")
public Double amount;
@Schema(example = "12.000000")
public Double amountPaid;
@Schema(example = "14.000000")
public Double amountWaived;
@Schema(example = "102.000000")
public Double amountWrittenOff;
@Schema(example = "102.000000")
public Double amountOutstanding;
@Schema(example = "102.000000")
public Double amountOrPercentage;
@Schema(example = "false")
public boolean penalty;
@Schema(description = "Enum option data")
public GetLoansLoanIdEnumOptionData chargePaymentMode;
@Schema(example = "false")
public boolean paid;
@Schema(example = "false")
public boolean waived;
@Schema(example = "false")
public boolean chargePayable;
@Schema(example = "3")
public Long loanId;
@Schema(example = "30.000000")
public Double minCap;
@Schema(example = "30.000000")
public Double maxCap;
@Schema(description = "List of GetLoansLoanIdLoanInstallmentChargeData")
public List<GetLoansLoanIdLoanInstallmentChargeData> installmentChargeData;
@Schema(example = "30.000000")
private Double amountAccrued;
@Schema(example = "30.000000")
private Double amountUnrecognized;
@Schema(example = "3ert3453")
private String externalId;
}
static final class GetLoansLoanIdCollectionData {
private GetLoansLoanIdCollectionData() {}
@Schema(example = "100.000000")
public Double availableDisbursementAmount;
@Schema(example = "12")
public Integer pastDueDays;
@Schema(example = "[2022, 07, 01]")
public LocalDate nextPaymentDueDate;
@Schema(example = "4")
public Integer delinquentDays;
@Schema(example = "[2022, 07, 01]")
public LocalDate delinquentDate;
@Schema(example = "100.000000")
public Double delinquentAmount;
@Schema(example = "[2022, 07, 01]")
public LocalDate lastPaymentDate;
@Schema(example = "100.000000")
public Double lastPaymentAmount;
}
@Schema(example = "1") @Schema(example = "1")
public Long id; public Long id;
@Schema(example = "000000001") @Schema(example = "000000001")
@@ -566,7 +923,7 @@ final class LoansApiResourceSwagger {
public String loanOfficerName; public String loanOfficerName;
public GetLoansLoanIdLoanType loanType; public GetLoansLoanIdLoanType loanType;
public GetLoansLoanIdCurrency currency; public GetLoansLoanIdCurrency currency;
@Schema(example = "1000000") @Schema(example = "1000000.00")
public BigDecimal principal; public BigDecimal principal;
@Schema(example = "1000.000000") @Schema(example = "1000.000000")
public Double approvedPrincipal; public Double approvedPrincipal;
@@ -595,6 +952,14 @@ final class LoansApiResourceSwagger {
public GetLoansLoanIdTimeline timeline; public GetLoansLoanIdTimeline timeline;
public GetLoansLoanIdSummary summary; public GetLoansLoanIdSummary summary;
public GetLoansLoanIdRepaymentSchedule repaymentSchedule; public GetLoansLoanIdRepaymentSchedule repaymentSchedule;
@Schema(description = "Set of GetLoansLoanIdDisbursementDetails")
public Set<GetLoansLoanIdTransactions> transactions;
@Schema(description = "Set of GetLoansLoanIdDisbursementDetails")
public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
@Schema(description = "Delinquent data")
public GetLoansLoanIdCollectionData delinquent;
@Schema(description = "Set of charges")
public Set<GetLoansLoanIdLoanChargeData> charges;
} }
@Schema(description = "GetLoansResponse") @Schema(description = "GetLoansResponse")
@@ -18,6 +18,13 @@
*/ */
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.api; package org.apache.fineract.portfolio.loanaccount.rescheduleloan.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -86,6 +93,9 @@ public class RescheduleLoansApiResource {
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve all reschedule loan reasons", description = "Retrieve all reschedule loan reasons as a template")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RescheduleLoansApiResourceSwagger.GetRescheduleReasonsTemplateResponse.class))) })
public String retrieveTemplate(@Context final UriInfo uriInfo) { public String retrieveTemplate(@Context final UriInfo uriInfo) {
this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME); this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME);
@@ -102,6 +112,9 @@ public class RescheduleLoansApiResource {
@Path("{scheduleId}") @Path("{scheduleId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve loan reschedule request by schedule id", description = "Retrieve loan reschedule request by schedule id")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RescheduleLoansApiResourceSwagger.GetLoanRescheduleRequestResponse.class))) })
public String readLoanRescheduleRequest(@Context final UriInfo uriInfo, @PathParam("scheduleId") final Long scheduleId, public String readLoanRescheduleRequest(@Context final UriInfo uriInfo, @PathParam("scheduleId") final Long scheduleId,
@QueryParam("command") final String command) { @QueryParam("command") final String command) {
this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME); this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME);
@@ -123,6 +136,10 @@ public class RescheduleLoansApiResource {
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create loan reschedule request", description = "Create a loan reschedule request.")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = String.class)), description = "{\"submittedOnDate\": \"05/02/2022\",\"rescheduleFromDate\": \"05/02/2022\",\"rescheduleReasonId\": 67,\"adjustedDueDate\": \"07/01/2022\",\"loanId\": \"18\",\"dateFormat\": \"MM/dd/yyyy\",\"locale\": \"en\"}")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RescheduleLoansApiResourceSwagger.PostCreateRescheduleLoansResponse.class))) })
public String createLoanRescheduleRequest(final String apiRequestBodyAsJson) { public String createLoanRescheduleRequest(final String apiRequestBodyAsJson) {
final CommandWrapper commandWrapper = new CommandWrapperBuilder() final CommandWrapper commandWrapper = new CommandWrapperBuilder()
.createLoanRescheduleRequest(RescheduleLoansApiConstants.ENTITY_NAME).withJson(apiRequestBodyAsJson).build(); .createLoanRescheduleRequest(RescheduleLoansApiConstants.ENTITY_NAME).withJson(apiRequestBodyAsJson).build();
@@ -136,6 +153,10 @@ public class RescheduleLoansApiResource {
@Path("{scheduleId}") @Path("{scheduleId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update loan reschedule request", description = "Update a loan reschedule request by either approving/rejecting it.")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = String.class)), description = "{\"approvedOnDate\": \"05/02/2022\",\"dateFormat\": \"MM/dd/yyyy\",\"locale\": \"en\"}")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RescheduleLoansApiResourceSwagger.PostUpdateRescheduleLoansResponse.class))) })
public String updateLoanRescheduleRequest(@PathParam("scheduleId") final Long scheduleId, @QueryParam("command") final String command, public String updateLoanRescheduleRequest(@PathParam("scheduleId") final Long scheduleId, @QueryParam("command") final String command,
final String apiRequestBodyAsJson) { final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = null; CommandWrapper commandWrapper = null;
@@ -175,6 +196,9 @@ public class RescheduleLoansApiResource {
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve all reschedule requests", description = "Retrieve all reschedule requests.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = RescheduleLoansApiResourceSwagger.GetLoanRescheduleRequestResponse.class)))) })
public String retrieveAllRescheduleRequest(@Context final UriInfo uriInfo, @QueryParam("command") final String command) { public String retrieveAllRescheduleRequest(@Context final UriInfo uriInfo, @QueryParam("command") final String command) {
this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME); this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME);
@@ -0,0 +1,221 @@
/**
* 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.rescheduleloan.api;
import io.swagger.v3.oas.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Set;
/**
* Reschedule Loans Api Resource Swagger.
*/
final class RescheduleLoansApiResourceSwagger {
private RescheduleLoansApiResourceSwagger() {}
@Schema(description = "GetRescheduleReasonsTemplateResponse")
public static final class GetRescheduleReasonsTemplateResponse {
private GetRescheduleReasonsTemplateResponse() {}
static final class GetRescheduleReasonsAllowedTypes {
private GetRescheduleReasonsAllowedTypes() {}
@Schema(example = "1")
public Integer id;
@Schema(example = "reason")
public String name;
@Schema(example = "0")
public Integer position;
@Schema(example = "description")
public String description;
@Schema(example = "true")
public Boolean active;
@Schema(example = "false")
public Boolean mandatory;
}
public Set<GetRescheduleReasonsAllowedTypes> rescheduleReasons;
}
@Schema(description = "GetLoanRescheduleRequestResponse")
public static final class GetLoanRescheduleRequestResponse {
private GetLoanRescheduleRequestResponse() {}
static final class GetLoanRescheduleRequestStatus {
private GetLoanRescheduleRequestStatus() {}
@Schema(example = "100")
public Integer id;
@Schema(example = "loanStatusType.submitted.and.pending.approval")
public String code;
@Schema(example = "Submitted and pending approval")
public String value;
@Schema(example = "true")
public Boolean pendingApproval;
@Schema(example = "false")
public Boolean approved;
@Schema(example = "false")
public Boolean rejected;
}
static final class RescheduleReasonsCodeValue {
private RescheduleReasonsCodeValue() {}
@Schema(example = "1")
public Integer id;
@Schema(example = "reason")
public String name;
@Schema(example = "true")
public Boolean active;
@Schema(example = "false")
public Boolean mandatory;
}
static final class RescheduleReasonsTimeline {
private RescheduleReasonsTimeline() {}
@Schema(example = "[2014, 5, 1]")
public LocalDate submittedOnDate;
@Schema(example = "mifos")
public String submittedByUsername;
@Schema(example = "App")
public String submittedByFirstname;
@Schema(example = "Administrator")
public String submittedByLastname;
@Schema(example = "[2014, 5, 1]")
public LocalDate approvedOnDate;
@Schema(example = "mifos")
public String approvedByUsername;
@Schema(example = "App")
public String approvedByFirstname;
@Schema(example = "Administrator")
public String approvedByLastname;
}
static final class LoanTermTypeOptions {
private LoanTermTypeOptions() {}
@Schema(example = "2")
public Integer id;
@Schema(example = "loanTermType.dueDate")
public String code;
@Schema(example = "dueDate")
public String value;
}
static final class LoanTermVariationsData {
private LoanTermVariationsData() {}
@Schema(example = "1")
public Integer id;
public LoanTermTypeOptions termType;
@Schema(example = "[2022, 5, 1]")
public LocalDate termVariationApplicableFrom;
@Schema(example = "100.00")
public BigDecimal decimalValue;
@Schema(example = "[2022, 5, 2]")
public LocalDate dateValue;
@Schema(example = "true")
public Boolean isSpecificToInstallment;
@Schema(example = "true")
public Boolean isProcessed;
}
@Schema(example = "1")
public Integer id;
@Schema(example = "18")
public Integer loanId;
@Schema(example = "15")
public Integer clientId;
@Schema(example = "clientName")
public String clientName;
@Schema(example = "000000018")
public String loanAccountNumber;
public GetLoanRescheduleRequestStatus statusEnum;
@Schema(example = "1")
public Integer rescheduleFromInstallment;
@Schema(example = "[2022, 5, 1]")
public LocalDate rescheduleFromDate;
@Schema(example = "false")
public Boolean recalculateInterest;
public RescheduleReasonsCodeValue rescheduleReasonCodeValue;
public RescheduleReasonsTimeline timeline;
@Schema(example = "rescheduleReasonComment")
public String rescheduleReasonComment;
public Set<LoanTermVariationsData> loanTermVariationsData;
}
@Schema(description = "PostCreateRescheduleLoansResponse ")
public static final class PostCreateRescheduleLoansResponse {
private PostCreateRescheduleLoansResponse() {}
@Schema(example = "1")
public Long officeId;
@Schema(example = "12")
public Long clientId;
@Schema(example = "18")
public Long loanId;
@Schema(example = "12")
public Long resourceId;
}
@Schema(description = "PostUpdateRescheduleLoansResponse ")
public static final class PostUpdateRescheduleLoansResponse {
private PostUpdateRescheduleLoansResponse() {}
static final class PostUpdateRescheduleLoanChanges {
private PostUpdateRescheduleLoanChanges() {}
@Schema(example = "en")
public String locale;
@Schema(example = "en")
public String dateFormat;
@Schema(example = "05/02/2022")
public String approvedOnDate;
@Schema(example = "1")
public Long approvedByUserId;
}
@Schema(example = "1")
public Long officeId;
@Schema(example = "12")
public Long clientId;
@Schema(example = "18")
public Long loanId;
@Schema(example = "12")
public Long resourceId;
public PostUpdateRescheduleLoanChanges changes;
}
}
@@ -2332,7 +2332,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
public CollectionData retrieveLoanCollectionData(Long loanId) { public CollectionData retrieveLoanCollectionData(Long loanId) {
final CollectionDataMapper mapper = new CollectionDataMapper(sqlGenerator); final CollectionDataMapper mapper = new CollectionDataMapper(sqlGenerator);
String sql = "select " + mapper.schema(); String sql = "select " + mapper.schema();
CollectionData collectionData = this.jdbcTemplate.queryForObject(sql, mapper, loanId); // NOSONAR CollectionData collectionData = this.jdbcTemplate.queryForObject(sql, mapper, loanId, loanId); // NOSONAR
return collectionData; return collectionData;
} }
@@ -2363,7 +2363,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
sqlBuilder.append( sqlBuilder.append(
"left join (select lt.loan_id, lt.transaction_date as transactionDate, lt.amount as amount from m_loan_transaction lt "); "left join (select lt.loan_id, lt.transaction_date as transactionDate, lt.amount as amount from m_loan_transaction lt ");
sqlBuilder.append( sqlBuilder.append(
"where lt.is_reversed = false and lt.transaction_type_enum=2 order by lt.transaction_date desc limit 1) lre on lre.loan_id = l.id "); "where lt.loan_id =? and lt.is_reversed = false and lt.transaction_type_enum=2 order by lt.transaction_date desc limit 1) lre on lre.loan_id = l.id ");
sqlBuilder.append("where l.id=? "); sqlBuilder.append("where l.id=? ");
return sqlBuilder.toString(); return sqlBuilder.toString();
} }
@@ -228,10 +228,26 @@ final class SelfLoansApiResourceSwagger {
public Integer id; public Integer id;
@Schema(example = "[2013, 11, 1]") @Schema(example = "[2013, 11, 1]")
public LocalDate expectedDisbursementDate; public LocalDate expectedDisbursementDate;
@Schema(example = "[2022, 07, 01]")
public LocalDate actualDisbursementDate;
@Schema(example = "22000.000000") @Schema(example = "22000.000000")
public Float principal; public Double principal;
@Schema(example = "22000.000000")
public Double netDisbursalAmount;
@Schema(example = "1")
public String loanChargeId;
@Schema(example = "22000.000000")
public Double chargeAmount;
@Schema(example = "22000.000000")
public Double waivedChargeAmount;
@Schema(example = "22000.000000") @Schema(example = "22000.000000")
public Float approvedPrincipal; public Float approvedPrincipal;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "de_DE")
public String locale;
@Schema(example = "some note")
public String note;
} }
static final class GetLoansLoanIdOverdueCharges { static final class GetLoansLoanIdOverdueCharges {
@@ -337,78 +353,78 @@ final class SelfLoansApiResourceSwagger {
} }
public GetLoansLoanIdCurrency currency; public GetLoansLoanIdCurrency currency;
@Schema(example = "1000000") @Schema(example = "1000000.000000")
public Long principalDisbursed; public Double principalDisbursed;
@Schema(example = "0") @Schema(example = "0.000000")
public Long principalPaid; public Double principalPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long principalWrittenOff; public Double principalWrittenOff;
@Schema(example = "1000000") @Schema(example = "1000000.000000")
public Long principalOutstanding; public Double principalOutstanding;
@Schema(example = "833333.3") @Schema(example = "833333.300000")
public Double principalOverdue; public Double principalOverdue;
@Schema(example = "240000") @Schema(example = "240000.000000")
public Long interestCharged; public Double interestCharged;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestPaid; public Double interestPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestWaived; public Double interestWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long interestWrittenOff; public Double interestWrittenOff;
@Schema(example = "240000") @Schema(example = "240000.000000")
public Long interestOutstanding; public Double interestOutstanding;
@Schema(example = "200000") @Schema(example = "200000.000000")
public Long interestOverdue; public Double interestOverdue;
@Schema(example = "18000") @Schema(example = "18000.000000")
public Long feeChargesCharged; public Double feeChargesCharged;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesDueAtDisbursementCharged; public Double feeChargesDueAtDisbursementCharged;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesPaid; public Double feeChargesPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesWaived; public Double feeChargesWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long feeChargesWrittenOff; public Double feeChargesWrittenOff;
@Schema(example = "18000") @Schema(example = "18000.000000")
public Long feeChargesOutstanding; public Double feeChargesOutstanding;
@Schema(example = "15000") @Schema(example = "15000.000000")
public Long feeChargesOverdue; public Double feeChargesOverdue;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesCharged; public Double penaltyChargesCharged;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesPaid; public Double penaltyChargesPaid;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesWaived; public Double penaltyChargesWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesWrittenOff; public Double penaltyChargesWrittenOff;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesOutstanding; public Double penaltyChargesOutstanding;
@Schema(example = "0") @Schema(example = "0.000000")
public Long penaltyChargesOverdue; public Double penaltyChargesOverdue;
@Schema(example = "1258000") @Schema(example = "1258000.000000")
public Long totalExpectedRepayment; public Double totalExpectedRepayment;
@Schema(example = "0") @Schema(example = "0.000000")
public Long totalRepayment; public Double totalRepayment;
@Schema(example = "258000") @Schema(example = "258000.000000")
public Long totalExpectedCostOfLoan; public Double totalExpectedCostOfLoan;
@Schema(example = "0") @Schema(example = "0.000000")
public Long totalCostOfLoan; public Double totalCostOfLoan;
@Schema(example = "0") @Schema(example = "0.000000")
public Long totalWaived; public Double totalWaived;
@Schema(example = "0") @Schema(example = "0.000000")
public Long totalWrittenOff; public Double totalWrittenOff;
@Schema(example = "1258000") @Schema(example = "1258000.000000")
public Long totalOutstanding; public Double totalOutstanding;
@Schema(example = "1048333.3") @Schema(example = "1048333.30000")
public Double totalOverdue; public Double totalOverdue;
@Schema(example = "[2012, 5, 10]") @Schema(example = "[2012, 5, 10]")
public LocalDate overdueSinceDate; public LocalDate overdueSinceDate;
public GetLoansLoanIdLinkedAccount linkedAccount; public GetLoansLoanIdLinkedAccount linkedAccount;
public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails; public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
@Schema(example = "1100.000000") @Schema(example = "1100.000000")
public Float fixedEmiAmount; public Double fixedEmiAmount;
@Schema(example = "35000") @Schema(example = "35000.000000")
public Long maxOutstandingLoanBalance; public Double maxOutstandingLoanBalance;
@Schema(example = "false") @Schema(example = "false")
public Boolean canDisburse; public Boolean canDisburse;
public Set<GetLoansLoanIdEmiVariations> emiAmountVariations; public Set<GetLoansLoanIdEmiVariations> emiAmountVariations;
@@ -32,6 +32,7 @@ import org.apache.fineract.batch.command.internal.ApproveLoanRescheduleCommandSt
import org.apache.fineract.batch.command.internal.CollectChargesCommandStrategy; import org.apache.fineract.batch.command.internal.CollectChargesCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateChargeCommandStrategy; import org.apache.fineract.batch.command.internal.CreateChargeCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateClientCommandStrategy; import org.apache.fineract.batch.command.internal.CreateClientCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateLoanRescheduleRequestCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy; import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
import org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy; import org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy;
import org.apache.fineract.batch.command.internal.GetDatatableEntryByAppTableIdCommandStrategy; import org.apache.fineract.batch.command.internal.GetDatatableEntryByAppTableIdCommandStrategy;
@@ -77,6 +78,8 @@ public class CommandStrategyProviderTest {
mock(ApproveLoanCommandStrategy.class)), mock(ApproveLoanCommandStrategy.class)),
Arguments.of("loans/123?command=disburse", HttpMethod.POST, "disburseLoanCommandStrategy", Arguments.of("loans/123?command=disburse", HttpMethod.POST, "disburseLoanCommandStrategy",
mock(DisburseLoanCommandStrategy.class)), mock(DisburseLoanCommandStrategy.class)),
Arguments.of("rescheduleloans", HttpMethod.POST, "createLoanRescheduleRequestCommandStrategy",
mock(CreateLoanRescheduleRequestCommandStrategy.class)),
Arguments.of("rescheduleloans/123?command=approve", HttpMethod.POST, "approveLoanRescheduleCommandStrategy", Arguments.of("rescheduleloans/123?command=approve", HttpMethod.POST, "approveLoanRescheduleCommandStrategy",
mock(ApproveLoanRescheduleCommandStrategy.class)), mock(ApproveLoanRescheduleCommandStrategy.class)),
Arguments.of("loans/123/transactions/123", HttpMethod.GET, "getTransactionByIdCommandStrategy", Arguments.of("loans/123/transactions/123", HttpMethod.GET, "getTransactionByIdCommandStrategy",
@@ -0,0 +1,113 @@
/**
* 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.batch.command.internal;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.api.RescheduleLoansApiResource;
import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Test class fpr {@link CreateLoanRescheduleRequestCommandStrategy}.
*/
public class CreateLoanRescheduleRequestCommandStrategyTest {
/**
* Test {@link CreateLoanRescheduleRequestCommandStrategy#execute} happy path scenario.
*/
@Test
public void testExecuteSuccessScenario() {
final TestContext testContext = new TestContext();
final BatchRequest batchRequest = getBatchRequest();
final String responseBody = "myResponseBody";
when(testContext.rescheduleLoansApiResource.createLoanRescheduleRequest(batchRequest.getBody())).thenReturn(responseBody);
BatchResponse batchResponse = testContext.subjectToTest.execute(batchRequest, testContext.uriInfo);
assertEquals(HttpStatus.SC_OK, batchResponse.getStatusCode());
assertSame(responseBody, batchResponse.getBody());
assertEquals(batchRequest.getRequestId(), batchResponse.getRequestId());
assertEquals(batchRequest.getHeaders(), batchResponse.getHeaders());
verify(testContext.rescheduleLoansApiResource).createLoanRescheduleRequest(batchRequest.getBody());
}
/**
* Creates and returns a request with the given loan id and command value.
*
*
* @return BatchRequest
*/
private BatchRequest getBatchRequest() {
final BatchRequest br = new BatchRequest();
String relativeUrl = "rescheduleloans";
br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
br.setRelativeUrl(relativeUrl);
br.setMethod(HttpMethod.POST);
br.setReference(Long.valueOf(RandomStringUtils.randomNumeric(5)));
br.setBody("{}");
return br;
}
/**
* Private test context class used since testng runs in parallel to avoid state between tests
*/
private static class TestContext {
/**
* Mock URI info.
*/
@Mock
private UriInfo uriInfo;
/**
* Mock reschedule loans API resource.
*/
@Mock
private RescheduleLoansApiResource rescheduleLoansApiResource;
/**
* The {@link CreateLoanRescheduleRequestCommandStrategy} under test.
*/
private final CreateLoanRescheduleRequestCommandStrategy subjectToTest;
/**
* Constructor.
*/
TestContext() {
MockitoAnnotations.openMocks(this);
subjectToTest = new CreateLoanRescheduleRequestCommandStrategy(rescheduleLoansApiResource);
}
}
}
@@ -27,8 +27,11 @@ import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy; import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest; import org.apache.fineract.batch.domain.BatchRequest;
@@ -41,12 +44,15 @@ import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper; import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
import org.apache.fineract.integrationtests.common.system.CodeHelper;
import org.apache.fineract.integrationtests.common.system.DatatableHelper; import org.apache.fineract.integrationtests.common.system.DatatableHelper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Test class for {@link org.apache.fineract.batch.command.CommandStrategyProvider}. This tests the response provided by * Test class for {@link org.apache.fineract.batch.command.CommandStrategyProvider}. This tests the response provided by
@@ -59,6 +65,8 @@ import org.junit.jupiter.api.Test;
*/ */
public class BatchApiTest { public class BatchApiTest {
private static final Logger LOG = LoggerFactory.getLogger(BatchApiTest.class);
/** /**
* The response specification * The response specification
*/ */
@@ -718,6 +726,81 @@ public class BatchApiTest {
Assertions.assertEquals(HttpStatus.SC_OK, responses.get(3).getStatusCode(), "Verify Status Code 200 for Get Transaction By Id"); Assertions.assertEquals(HttpStatus.SC_OK, responses.get(3).getStatusCode(), "Verify Status Code 200 for Get Transaction By Id");
} }
/**
* Test for the successful new loan reschedule request and approval for the same. A '200' status code is expected on
* successful responses.
*
* @see org.apache.fineract.batch.command.internal.CreateLoanRescheduleRequestCommandStrategy
* @see org.apache.fineract.batch.command.internal.ApproveLoanRescheduleCommandStrategy
*/
@Test
public void shouldReturnOkStatusOnSuccessfulDisbursementAndRescheduleLoan() {
final String loanProductJSON = new LoanProductTestBuilder() //
.withPrincipal("10000000.00") //
.withNumberOfRepayments("24") //
.withRepaymentAfterEvery("1") //
.withRepaymentTypeAsMonth() //
.withinterestRatePerPeriod("2") //
.withInterestRateFrequencyTypeAsMonths() //
.withAmortizationTypeAsEqualPrincipalPayment() //
.withInterestTypeAsDecliningBalance() //
.currencyDetails("0", "100").build(null);
final Long createActiveClientRequestId = 8462L;
final Long applyLoanRequestId = createActiveClientRequestId + 1;
final Long approveLoanRequestId = applyLoanRequestId + 1;
final Long disburseLoanRequestId = approveLoanRequestId + 1;
final Long rescheduleLoanRequestId = disburseLoanRequestId + 1;
final Long approveRescheduleLoanRequestId = rescheduleLoanRequestId + 1;
// Create product
LOG.info("LoanProduct {}", loanProductJSON);
final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON);
// Create client
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientId);
/* Retrieve/Create Code Values for the Code "LoanRescheduleReason = 23" */
final HashMap<String, Object> codeValue = CodeHelper.retrieveOrCreateCodeValue(23, this.requestSpec, this.responseSpec);
final Integer codeValueId = (Integer) codeValue.get("id");
// Create an ApplyLoan request
final BatchRequest applyLoanRequestWithClientId = BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, productId);
// Create an approveLoan request
final BatchRequest approveLoanRequest = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
// Create a disbursement request
final LocalDate disburseLoanDate = LocalDate.now(ZoneId.systemDefault()).minusDays(1);
final BatchRequest disburseLoanRequest = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId,
disburseLoanDate);
// Create a reschedule loan request
final BatchRequest rescheduleLoanRequest = BatchHelper.createRescheduleLoanRequest(rescheduleLoanRequestId, disburseLoanRequestId,
disburseLoanDate.plusMonths(1), codeValueId);
// Approve reschedule loan request
final BatchRequest approveRescheduleLoanRequest = BatchHelper.approveRescheduleLoanRequest(approveRescheduleLoanRequestId,
rescheduleLoanRequestId);
final List<BatchRequest> batchRequests = Arrays.asList(applyLoanRequestWithClientId, approveLoanRequest, disburseLoanRequest,
rescheduleLoanRequest, approveRescheduleLoanRequest);
LOG.info("shouldReturnOkStatusOnSuccessfulDisbursementAndRescheduleLoan Request - {}", BatchHelper.toJsonString(batchRequests));
final List<BatchResponse> responses = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
BatchHelper.toJsonString(batchRequests));
Assertions.assertEquals(HttpStatus.SC_OK, responses.get(0).getStatusCode(), "Verify Status Code 200 for Apply Loan");
Assertions.assertEquals(HttpStatus.SC_OK, responses.get(1).getStatusCode(), "Verify Status Code 200 for Approve Loan");
Assertions.assertEquals(HttpStatus.SC_OK, responses.get(2).getStatusCode(), "Verify Status Code 200 for Disburse Loan");
Assertions.assertEquals(HttpStatus.SC_OK, responses.get(3).getStatusCode(),
"Verify Status Code 200 for Create Reschedule Loan request");
Assertions.assertEquals(HttpStatus.SC_OK, responses.get(4).getStatusCode(),
"Verify Status Code 200 for Approve Reschedule Loan request");
}
/** /**
* Test for the successful create client, creat, approve and get loan. A '200' status code is expected on successful * Test for the successful create client, creat, approve and get loan. A '200' status code is expected on successful
* responses. * responses.
@@ -98,6 +98,7 @@ public final class BatchHelper {
public static List<BatchResponse> postBatchRequestsWithoutEnclosingTransaction(final RequestSpecification requestSpec, public static List<BatchResponse> postBatchRequestsWithoutEnclosingTransaction(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec, final String jsonifiedBatchRequests) { final ResponseSpecification responseSpec, final String jsonifiedBatchRequests) {
final String response = Utils.performServerPost(requestSpec, responseSpec, BATCH_API_URL, jsonifiedBatchRequests, null); final String response = Utils.performServerPost(requestSpec, responseSpec, BATCH_API_URL, jsonifiedBatchRequests, null);
LOG.info("BatchHelper Response {}", response);
return BatchHelper.fromJsonString(response); return BatchHelper.fromJsonString(response);
} }
@@ -501,8 +502,6 @@ public final class BatchHelper {
* the reference ID * the reference ID
* @param amount * @param amount
* the amount * the amount
* @param date
* the transaction date
* @return BatchRequest the batch request * @return BatchRequest the batch request
*/ */
public static BatchRequest repayLoanRequest(final Long requestId, final Long reference, final String amount) { public static BatchRequest repayLoanRequest(final Long requestId, final Long reference, final String amount) {
@@ -578,6 +577,69 @@ public final class BatchHelper {
return br; return br;
} }
/**
* Creates and returns a
* {@link org.apache.fineract.batch.command.internal.CreateLoanRescheduleRequestCommandStrategy} request with given
* request ID.
*
*
* @param requestId
* the request ID
* @param reference
* teh reference
* @param rescheduleFromDate
* the reschedule from date
* @param rescheduleReasonId
* the reschedule reason code value id
*
* @return BatchRequest the created {@link BatchRequest}
*/
public static BatchRequest createRescheduleLoanRequest(final Long requestId, final Long reference, final LocalDate rescheduleFromDate,
final Integer rescheduleReasonId) {
final BatchRequest br = new BatchRequest();
br.setRequestId(requestId);
br.setReference(reference);
br.setRelativeUrl("rescheduleloans");
br.setMethod("POST");
final LocalDate today = LocalDate.now(ZoneId.systemDefault());
final LocalDate adjustedDueDate = LocalDate.now(ZoneId.systemDefault()).plusDays(40);
final String submittedOnDate = today.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
final String rescheduleFromDateString = rescheduleFromDate.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
final String adjustedDueDateString = adjustedDueDate.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
br.setBody(String.format("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", "
+ "\"submittedOnDate\": \"%s\", \"rescheduleFromDate\": \"%s\", \"rescheduleReasonId\": %d, \"adjustedDueDate\": \"%s\", \"loanId\": \"$.loanId\"}",
submittedOnDate, rescheduleFromDateString, rescheduleReasonId, adjustedDueDateString));
return br;
}
/**
* Creates and returns a {@link org.apache.fineract.batch.command.internal.ApproveLoanRescheduleCommandStrategy}
* request with given request ID.
*
*
* @param requestId
* the request ID
* @param reference
* teh reference
* @return BatchRequest the created {@link BatchRequest}
*/
public static BatchRequest approveRescheduleLoanRequest(final Long requestId, final Long reference) {
final BatchRequest br = new BatchRequest();
br.setRequestId(requestId);
br.setReference(reference);
br.setRelativeUrl("rescheduleloans/$.resourceId?command=approve");
br.setMethod("POST");
final LocalDate approvedOnDate = LocalDate.now(ZoneId.systemDefault());
final String approvedOnDateString = approvedOnDate.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
br.setBody(String.format("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + "\"approvedOnDate\": \"%s\"}",
approvedOnDateString));
return br;
}
/** /**
* Checks that the client with given externalId is not created on the server. * Checks that the client with given externalId is not created on the server.
* *
@@ -207,8 +207,9 @@ public class ClientHelper {
map.put("locale", "en"); map.put("locale", "en");
map.put("active", "true"); map.put("active", "true");
map.put("activationDate", dateOfJoining); map.put("activationDate", dateOfJoining);
log.info("map : {}", map); final String testClientAsJson = GSON.toJson(map);
return GSON.toJson(map); log.info("TestClient Request : {}", testClientAsJson);
return testClientAsJson;
} }
public static String getTestClientAsJSONPending(final String submittedOnDate, final String officeId) { public static String getTestClientAsJSONPending(final String submittedOnDate, final String officeId) {