diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index a7327e7ca..d867511f1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -994,6 +994,15 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder markAsFraud(final Long loanId) { + this.actionName = "SETFRAUD"; + this.entityName = "LOAN"; + this.entityId = loanId; + this.loanId = loanId; + this.href = "/loans/" + loanId; + return this; + } + public CommandWrapperBuilder updateDisbusementDate(final Long loanId, final Long disbursementId) { this.actionName = "UPDATE"; this.entityName = "DISBURSEMENTDETAIL"; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java index 42492928c..087c80acf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java @@ -161,7 +161,12 @@ public interface LoanApiConstants { String TRANSACTION_AMOUNT_PARAMNAME = "transactionAmount"; String PAYMENT_TYPE_PARAMNAME = "paymentTypeId"; + String FRAUD_ATTRIBUTE_NAME = "fraud"; // Commands String CHARGEBACK_TRANSACTION_COMMAND = "chargeback"; + String MARK_AS_FRAUD_COMMAND = "markAsFraud"; + + // Data Validator names + String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud"; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java index b37458424..ca4fc1639 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java @@ -55,7 +55,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import lombok.RequiredArgsConstructor; -import org.apache.commons.lang3.StringUtils; import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; @@ -75,6 +74,7 @@ import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamE import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.infrastructure.core.service.CommandParameterUtil; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant; @@ -759,7 +759,7 @@ public class LoansApiResource { @QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo, @Parameter(hidden = true) final String apiRequestBodyAsJson) { - if (is(commandParam, "calculateLoanSchedule")) { + if (CommandParameterUtil.is(commandParam, "calculateLoanSchedule")) { final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson); final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper); @@ -786,13 +786,18 @@ public class LoansApiResource { @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansLoanIdResponse.class))) }) public String modifyLoanApplication(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, + @QueryParam("command") @Parameter(description = "command") final String commandParam, @Parameter(hidden = true) final String apiRequestBodyAsJson) { - final CommandWrapper commandRequest = new CommandWrapperBuilder().updateLoanApplication(loanId).withJson(apiRequestBodyAsJson) - .build(); + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); + CommandWrapper commandRequest = null; + if (CommandParameterUtil.is(commandParam, LoanApiConstants.MARK_AS_FRAUD_COMMAND)) { + commandRequest = builder.markAsFraud(loanId).build(); + } else { + commandRequest = builder.updateLoanApplication(loanId).build(); + } final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - return this.toApiJsonSerializer.serialize(result); } @@ -837,29 +842,29 @@ public class LoansApiResource { final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); CommandWrapper commandRequest = null; - if (is(commandParam, "reject")) { + if (CommandParameterUtil.is(commandParam, "reject")) { commandRequest = builder.rejectLoanApplication(loanId).build(); - } else if (is(commandParam, "withdrawnByApplicant")) { + } else if (CommandParameterUtil.is(commandParam, "withdrawnByApplicant")) { commandRequest = builder.withdrawLoanApplication(loanId).build(); - } else if (is(commandParam, "approve")) { + } else if (CommandParameterUtil.is(commandParam, "approve")) { commandRequest = builder.approveLoanApplication(loanId).build(); - } else if (is(commandParam, "disburse")) { + } else if (CommandParameterUtil.is(commandParam, "disburse")) { commandRequest = builder.disburseLoanApplication(loanId).build(); - } else if (is(commandParam, "disburseToSavings")) { + } else if (CommandParameterUtil.is(commandParam, "disburseToSavings")) { commandRequest = builder.disburseLoanToSavingsApplication(loanId).build(); - } else if (is(commandParam, "undoapproval")) { + } else if (CommandParameterUtil.is(commandParam, "undoapproval")) { commandRequest = builder.undoLoanApplicationApproval(loanId).build(); - } else if (is(commandParam, "undodisbursal")) { + } else if (CommandParameterUtil.is(commandParam, "undodisbursal")) { commandRequest = builder.undoLoanApplicationDisbursal(loanId).build(); - } else if (is(commandParam, "undolastdisbursal")) { + } else if (CommandParameterUtil.is(commandParam, "undolastdisbursal")) { commandRequest = builder.undoLastDisbursalLoanApplication(loanId).build(); - } else if (is(commandParam, "assignloanofficer")) { + } else if (CommandParameterUtil.is(commandParam, "assignloanofficer")) { commandRequest = builder.assignLoanOfficer(loanId).build(); - } else if (is(commandParam, "unassignloanofficer")) { + } else if (CommandParameterUtil.is(commandParam, "unassignloanofficer")) { commandRequest = builder.unassignLoanOfficer(loanId).build(); - } else if (is(commandParam, "recoverGuarantees")) { + } else if (CommandParameterUtil.is(commandParam, "recoverGuarantees")) { commandRequest = new CommandWrapperBuilder().recoverFromGuarantor(loanId).build(); - } else if (is(commandParam, "assigndelinquency")) { + } else if (CommandParameterUtil.is(commandParam, "assigndelinquency")) { commandRequest = builder.assignDelinquency(loanId).build(); } @@ -871,10 +876,6 @@ public class LoansApiResource { return this.toApiJsonSerializer.serialize(result); } - private boolean is(final String commandParam, final String commandValue) { - return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); - } - @GET @Path("downloadtemplate") @Produces("application/vnd.ms-excel") @@ -915,17 +916,17 @@ public class LoansApiResource { final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); CommandWrapper commandRequest = null; - if (is(commandParam, "reject")) { + if (CommandParameterUtil.is(commandParam, "reject")) { commandRequest = builder.rejectGLIMApplication(glimId).build(); - } else if (is(commandParam, "approve")) { + } else if (CommandParameterUtil.is(commandParam, "approve")) { commandRequest = builder.approveGLIMLoanApplication(glimId).build(); - } else if (is(commandParam, "disburse")) { + } else if (CommandParameterUtil.is(commandParam, "disburse")) { commandRequest = builder.disburseGlimLoanApplication(glimId).build(); - } else if (is(commandParam, "glimrepayment")) { + } else if (CommandParameterUtil.is(commandParam, "glimrepayment")) { commandRequest = builder.repaymentGlimLoanApplication(glimId).build(); - } else if (is(commandParam, "undodisbursal")) { + } else if (CommandParameterUtil.is(commandParam, "undodisbursal")) { commandRequest = builder.undoGLIMLoanDisbursal(glimId).build(); - } else if (is(commandParam, "undoapproval")) { + } else if (CommandParameterUtil.is(commandParam, "undoapproval")) { commandRequest = builder.undoGLIMLoanApproval(glimId).build(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java index 34a066364..00be3eb35 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java @@ -972,6 +972,8 @@ final class LoansApiResourceSwagger { @Schema(description = "Set of charges") public Set charges; public GetDelinquencyRangesResponse delinquencyRange; + @Schema(example = "false") + public Boolean fraud; } @Schema(description = "GetLoansResponse") @@ -1169,6 +1171,8 @@ final class LoansApiResourceSwagger { public String interestChargedFromDate; @Schema(example = "true") public Boolean isEqualAmortization; + @Schema(example = "true") + public Boolean fraud; @Schema(example = "1") public Integer graceOnArrearsAgeing; @Schema(example = "1") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java index c515e9d81..43c36eefb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java @@ -189,6 +189,7 @@ public final class LoanAccountData { private final Collection clientActiveLoanOptions; private final Boolean canUseForTopup; private final boolean isTopup; + private final boolean fraud; private final Long closureLoanId; private final String closureLoanAccountNo; private final BigDecimal topupAmount; @@ -433,6 +434,7 @@ public final class LoanAccountData { this.delinquent = null; this.delinquencyRange = null; this.disallowExpectedDisbursements = false; + this.fraud = false; } public static final Comparator ClientNameComparator = new Comparator() { @@ -570,6 +572,7 @@ public final class LoanAccountData { final CollectionData delinquent = CollectionData.template(); final DelinquencyRangeData delinquencyRange = null; final Boolean disallowExpectedDisbursements = false; + final boolean fraud = false; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, group, loanType, loanProductId, loanProductName, loanProductDescription, @@ -592,7 +595,7 @@ public final class LoanAccountData { createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquent, delinquencyRange, - disallowExpectedDisbursements); + disallowExpectedDisbursements, fraud); } /** @@ -719,6 +722,7 @@ public final class LoanAccountData { final CollectionData delinquent = CollectionData.template(); final DelinquencyRangeData delinquencyRange = null; final Boolean disallowExpectedDisbursements = false; + final boolean fraud = false; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, group, loanType, loanProductId, loanProductName, loanProductDescription, @@ -741,7 +745,7 @@ public final class LoanAccountData { createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquent, delinquencyRange, - disallowExpectedDisbursements); + disallowExpectedDisbursements, fraud); } public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) { @@ -772,7 +776,8 @@ public final class LoanAccountData { acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled, - acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements); + acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements, + acc.fraud); } /** @@ -902,6 +907,7 @@ public final class LoanAccountData { final CollectionData delinquent = CollectionData.template(); final DelinquencyRangeData delinquencyRange = null; final Boolean disallowExpectedDisbursements = false; + final Boolean fraud = false; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, group, loanType, loanProductId, loanProductName, loanProductDescription, @@ -924,7 +930,7 @@ public final class LoanAccountData { createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquent, delinquencyRange, - disallowExpectedDisbursements); + disallowExpectedDisbursements, fraud); } public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) { @@ -955,7 +961,8 @@ public final class LoanAccountData { acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled, - acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements); + acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements, + acc.fraud); } public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product, @@ -1083,6 +1090,7 @@ public final class LoanAccountData { final Boolean isRatesEnabled = false; final CollectionData delinquent = CollectionData.template(); final DelinquencyRangeData delinquencyRange = null; + final boolean fraud = false; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, group, loanType, product.getId(), product.getName(), product.getDescription(), @@ -1109,7 +1117,7 @@ public final class LoanAccountData { interestRatesPeriods, product.isAllowVariableInstallments(), product.getMinimumGap(), product.getMaximumGap(), subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, product.isEqualAmortization(), rates, isRatesEnabled, product.getFixedPrincipalPercentagePerInstallment(), delinquent, - delinquencyRange, product.getDisallowExpectedDisbursements()); + delinquencyRange, product.getDisallowExpectedDisbursements(), fraud); } public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) { @@ -1180,7 +1188,7 @@ public final class LoanAccountData { product.isAllowVariableInstallments(), product.getMinimumGap(), product.getMaximumGap(), acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization(), acc.rates, acc.isRatesEnabled, product.getFixedPrincipalPercentagePerInstallment(), - delinquent, delinquencyRange, product.getDisallowExpectedDisbursements()); + delinquent, delinquencyRange, product.getDisallowExpectedDisbursements(), acc.fraud); } /* @@ -1212,7 +1220,7 @@ public final class LoanAccountData { final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap, final EnumOptionData subStatus, final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment, - final DelinquencyRangeData delinquencyRange, final boolean disallowExpectedDisbursements) { + final DelinquencyRangeData delinquencyRange, final boolean disallowExpectedDisbursements, final boolean fraud) { final LoanScheduleData repaymentSchedule = null; final Collection transactions = null; @@ -1274,7 +1282,7 @@ public final class LoanAccountData { createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquent, delinquencyRange, - disallowExpectedDisbursements); + disallowExpectedDisbursements, fraud); } /* @@ -1324,7 +1332,7 @@ public final class LoanAccountData { acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, rates, isRatesEnabled, - acc.fixedPrincipalPercentagePerInstallment, delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements); + acc.fixedPrincipalPercentagePerInstallment, delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements, acc.fraud); } public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection productOptions, @@ -1367,7 +1375,8 @@ public final class LoanAccountData { acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled, - acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements); + acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements, + acc.fraud); } public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map memberLoanCycle) { @@ -1433,7 +1442,8 @@ public final class LoanAccountData { acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled, - acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements); + acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements, + acc.fraud); } public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData, @@ -1467,7 +1477,8 @@ public final class LoanAccountData { acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled, - acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements); + acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements, + acc.fraud); } public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) { @@ -1496,7 +1507,8 @@ public final class LoanAccountData { acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled, - acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements); + acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements, + acc.fraud); } public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) { @@ -1526,7 +1538,8 @@ public final class LoanAccountData { acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled, - acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements); + acc.fixedPrincipalPercentagePerInstallment, acc.delinquent, acc.delinquencyRange, acc.disallowExpectedDisbursements, + acc.fraud); } private LoanAccountData(final Long id, // @@ -1581,7 +1594,7 @@ public final class LoanAccountData { final Collection clientActiveLoanOptions, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization, final List rates, final Boolean isRatesEnabled, final BigDecimal fixedPrincipalPercentagePerInstallment, final CollectionData delinquent, - final DelinquencyRangeData delinquencyRange, final Boolean disallowExpectedDisbursements) { + final DelinquencyRangeData delinquencyRange, final Boolean disallowExpectedDisbursements, final Boolean fraud) { this.id = id; this.accountNo = accountNo; @@ -1772,6 +1785,7 @@ public final class LoanAccountData { this.delinquent = delinquent; this.delinquencyRange = delinquencyRange; this.disallowExpectedDisbursements = disallowExpectedDisbursements; + this.fraud = fraud; } public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() { @@ -1910,4 +1924,5 @@ public final class LoanAccountData { public boolean isIsTopup() { return isTopup; } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 48b69595f..38dec1461 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -416,6 +416,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "is_topup", nullable = false) private boolean isTopup = false; + @Column(name = "is_fraud", nullable = false) + private boolean fraud = false; + @OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch = FetchType.LAZY) private LoanTopupDetails loanTopupDetails; @@ -6711,6 +6714,14 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return this.isTopup; } + public void markAsFraud(final boolean value) { + this.fraud = value; + } + + public boolean isFraud() { + return this.fraud; + } + public BigDecimal getFirstDisbursalAmount() { BigDecimal firstDisbursalAmount; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/MarkLoanAsFraudCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/MarkLoanAsFraudCommandHandler.java new file mode 100644 index 000000000..e481cb5c9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/MarkLoanAsFraudCommandHandler.java @@ -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 = "SETFRAUD") +public class MarkLoanAsFraudCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.markLoanAsFraud(command.getLoanId(), command); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java index 553c64f82..8e2a9dac2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java @@ -95,7 +95,7 @@ public final class LoanApplicationCommandFromApiJsonHelper { LoanApiConstants.applicationId, // glim specific LoanApiConstants.lastApplication, // glim specific LoanApiConstants.daysInYearTypeParameterName, LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, - LoanApiConstants.DISALLOW_EXPECTED_DISBURSEMENTS)); + LoanApiConstants.DISALLOW_EXPECTED_DISBURSEMENTS, LoanApiConstants.FRAUD_ATTRIBUTE_NAME)); public static final String LOANAPPLICATION_UNDO = "loanapplication.undo"; private final FromJsonHelper fromApiJsonHelper; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index 54348344a..01a76dc94 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -518,6 +518,27 @@ public final class LoanEventApiJsonValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } + public void validateMarkAsFraudLoan(final String json) { + if (StringUtils.isBlank(json)) { + return; + } + Set transactionParameters = new HashSet<>(Arrays.asList(LoanApiConstants.FRAUD_ATTRIBUTE_NAME)); + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(LoanApiConstants.LOAN_FRAUD_DATAVALIDATOR_PREFIX); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final boolean isFraud = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.FRAUD_ATTRIBUTE_NAME, element); + baseDataValidator.reset().parameter(LoanApiConstants.FRAUD_ATTRIBUTE_NAME).value(isFraud).notNull(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + public void validateInstallmentChargeTransaction(final String json) { if (StringUtils.isBlank(json)) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index a80bbde22..26514e05e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -629,14 +629,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.penalty_charges_repaid_derived as penaltyChargesPaid," + " l.penalty_charges_waived_derived as penaltyChargesWaived," + " l.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff," - + " l.penalty_charges_outstanding_derived as penaltyChargesOutstanding," + + " l.penalty_charges_outstanding_derived as penaltyChargesOutstanding, " + " l.total_expected_repayment_derived as totalExpectedRepayment, l.total_repayment_derived as totalRepayment," + " l.total_expected_costofloan_derived as totalExpectedCostOfLoan, l.total_costofloan_derived as totalCostOfLoan," + " l.total_waived_derived as totalWaived, l.total_writtenoff_derived as totalWrittenOff," + " l.writeoff_reason_cv_id as writeoffReasonId, codev.code_value as writeoffReason," + " l.total_outstanding_derived as totalOutstanding, l.total_overpaid_derived as totalOverpaid," + " l.fixed_emi_amount as fixedEmiAmount, l.max_outstanding_loan_balance as outstandingLoanBalance," - + " l.loan_sub_status_id as loanSubStatusId, la.principal_overdue_derived as principalOverdue," + + " l.loan_sub_status_id as loanSubStatusId, la.principal_overdue_derived as principalOverdue, l.is_fraud as isFraud, " + " la.interest_overdue_derived as interestOverdue, la.fee_charges_overdue_derived as feeChargesOverdue," + " la.penalty_charges_overdue_derived as penaltyChargesOverdue, la.total_overdue_derived as totalOverdue," + " la.overdue_since_date_derived as overdueSinceDate," @@ -989,6 +989,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { // Current Delinquency Range Data DelinquencyRangeData delinquencyRange = this.delinquencyReadPlatformService.retrieveCurrentDelinquencyTag(id); + final boolean isFraud = rs.getBoolean("isFraud"); + return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, @@ -1003,7 +1005,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap, maximumGap, loanSubStatus, canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, - fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements); + fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java index fb419edd7..d1db8f194 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java @@ -119,4 +119,6 @@ public interface LoanWritePlatformService { CommandProcessingResult creditBalanceRefund(Long loanId, JsonCommand command); + CommandProcessingResult markLoanAsFraud(Long loanId, JsonCommand command); + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 85ef0522f..dd709051e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -3303,6 +3303,37 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } + @Override + @Transactional + public CommandProcessingResult markLoanAsFraud(Long loanId, JsonCommand command) { + this.loanEventApiJsonValidator.validateMarkAsFraudLoan(command.json()); + + Loan loan = this.loanAssembler.assembleFrom(loanId); + final Map changes = new LinkedHashMap<>(); + + if (!loan.isOpen()) { + throw new PlatformServiceUnavailableException("error.msg.loan.mark.as.fraud.not.allowed", + "Loan Id: " + loanId + " mark as fraud is not allowed as loan status is not active", loan.getStatus().getCode()); + } + + final boolean fraud = command.booleanPrimitiveValueOfParameterNamed(LoanApiConstants.FRAUD_ATTRIBUTE_NAME); + if (loan.isFraud() != fraud) { + loan.markAsFraud(fraud); + this.loanRepository.save(loan); + changes.put(LoanApiConstants.FRAUD_ATTRIBUTE_NAME, fraud); + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(loan.getId()) // + .withOfficeId(loan.getOfficeId()) // + .withClientId(loan.getClientId()) // + .withGroupId(loan.getGroupId()) // + .withLoanId(loanId) // + .with(changes) // + .build(); + } + @Override @Transactional public CommandProcessingResult makeLoanRefund(Long loanId, JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java index 2e9c104a4..993d0ad64 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java @@ -243,8 +243,8 @@ public class SelfLoansApiResource { if (clientId != null) { validateAppuserClientsMapping(clientId); } - - return this.loansApiResource.modifyLoanApplication(loanId, apiRequestBodyAsJson); + final String command = null; + return this.loansApiResource.modifyLoanApplication(loanId, command, apiRequestBodyAsJson); } @POST diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index d66da7972..8e90dbb53 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -81,4 +81,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0062_add_fraud_attribute_to_loan.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0062_add_fraud_attribute_to_loan.xml new file mode 100644 index 000000000..c5b283e27 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0062_add_fraud_attribute_to_loan.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountFraudTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountFraudTest.java new file mode 100644 index 000000000..73d5ff71d --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountFraudTest.java @@ -0,0 +1,157 @@ +/** + * 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.integrationtests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.restassured.builder.RequestSpecBuilder; +import io.restassured.builder.ResponseSpecBuilder; +import io.restassured.http.ContentType; +import io.restassured.specification.RequestSpecification; +import io.restassured.specification.ResponseSpecification; +import java.time.LocalDate; +import java.util.HashMap; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.client.models.GetLoanProductsProductIdResponse; +import org.apache.fineract.client.models.GetLoansLoanIdResponse; +import org.apache.fineract.client.models.PutLoansLoanIdResponse; +import org.apache.fineract.integrationtests.common.ClientHelper; +import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@Slf4j +public class LoanAccountFraudTest { + + private ResponseSpecification responseSpec; + private ResponseSpecification responseSpecError; + private RequestSpecification requestSpec; + private LoanTransactionHelper loanTransactionHelper; + private final String amountVal = "1000"; + private LocalDate todaysDate; + private String operationDate; + + @BeforeEach + public void setup() { + Utils.initializeRESTAssured(); + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + this.responseSpecError = new ResponseSpecBuilder().expectStatusCode(503).build(); + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + + this.todaysDate = Utils.getLocalDateOfTenant(); + this.operationDate = Utils.dateFormatter.format(this.todaysDate); + } + + @Test + public void testMarkLoanAsFraud() { + final String command = "markAsFraud"; + // Client and Loan account creation + final Integer loanId = createAccounts(15, 1); + + GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId); + assertNotNull(getLoansLoanIdResponse); + + // Default values Not Null and False + assertNotNull(getLoansLoanIdResponse.getFraud()); + assertEquals(Boolean.FALSE, getLoansLoanIdResponse.getFraud()); + + String payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "true"); + // Send the request expecting an error because the Loan is not active yet + PutLoansLoanIdResponse putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, command, payload, + this.responseSpecError); + + String statusCode = getLoansLoanIdResponse.getStatus().getCode(); + log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode); + + // Approve the Loan active + approveAndDisburseLoan(loanId, this.operationDate, this.amountVal); + + // Default values Not Null and False + getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId); + assertNotNull(getLoansLoanIdResponse); + assertNotNull(getLoansLoanIdResponse.getFraud()); + assertEquals(Boolean.FALSE, getLoansLoanIdResponse.getFraud()); + statusCode = getLoansLoanIdResponse.getStatus().getCode(); + log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode); + + // Mark On the Fraud + putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, command, payload, this.responseSpec); + assertNotNull(putLoansLoanIdResponse); + + getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId); + assertNotNull(getLoansLoanIdResponse); + assertNotNull(getLoansLoanIdResponse.getFraud()); + assertEquals(Boolean.TRUE, getLoansLoanIdResponse.getFraud()); + + // Mark Off the Fraud + payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "false"); + putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, command, payload, this.responseSpec); + assertNotNull(putLoansLoanIdResponse); + + getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId); + assertNotNull(getLoansLoanIdResponse); + assertNotNull(getLoansLoanIdResponse.getFraud()); + assertEquals(Boolean.FALSE, getLoansLoanIdResponse.getFraud()); + } + + private Integer createAccounts(final Integer daysToSubtract, final Integer numberOfRepayments) { + // Client and Loan account creation + final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012"); + final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, null); + + // Older date to have more than one overdue installment + final LocalDate transactionDate = this.todaysDate.minusDays(daysToSubtract + (30 * (numberOfRepayments - 1))); + String operationDate = Utils.dateFormatter.format(transactionDate); + + return createLoanAccount(loanTransactionHelper, clientId.toString(), getLoanProductsProductResponse.getId().toString(), + operationDate, amountVal, numberOfRepayments.toString()); + } + + private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper, + final Integer delinquencyBucketId) { + final HashMap loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId); + final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap)); + return loanTransactionHelper.getLoanProduct(loanProductId); + } + + private Integer createLoanAccount(final LoanTransactionHelper loanTransactionHelper, final String clientId, final String loanProductId, + final String operationDate, final String principalAmount, final String numberOfRepayments) { + final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principalAmount) + .withLoanTermFrequency(numberOfRepayments).withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments) + .withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsMonths() // + .withInterestRatePerPeriod("0") // + .withExpectedDisbursementDate(operationDate) // + .withInterestTypeAsDecliningBalance() // + .withSubmittedOnDate(operationDate) // + .build(clientId, loanProductId, null); + return loanTransactionHelper.getLoanId(loanApplicationJSON); + } + + private void approveAndDisburseLoan(final Integer loanId, final String operationDate, final String principalAmount) { + loanTransactionHelper.approveLoan(operationDate, principalAmount, loanId, null); + loanTransactionHelper.disburseLoanWithNetDisbursalAmount(operationDate, loanId, principalAmount); + } + +} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java index 7f30e4341..e80419c36 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java @@ -147,6 +147,13 @@ public class LoanTransactionHelper { return GSON.fromJson(response, PutLoansLoanIdResponse.class); } + public PutLoansLoanIdResponse modifyLoanCommand(final Integer loanId, final String command, final String payload, + ResponseSpecification responseSpec) { + final String url = "/fineract-provider/api/v1/loans/" + loanId + "?" + Utils.TENANT_IDENTIFIER + "&command=" + command; + final String response = Utils.performServerPut(this.requestSpec, responseSpec, url, payload, null); + return GSON.fromJson(response, PutLoansLoanIdResponse.class); + } + public ArrayList getLoanRepaymentSchedule(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final Integer loanID) { final String URL = "/fineract-provider/api/v1/loans/" + loanID + "?associations=repaymentSchedule&" + Utils.TENANT_IDENTIFIER; @@ -1002,6 +1009,12 @@ public class LoanTransactionHelper { return new Gson().toJson(map); } + public String getLoanFraudPayloadAsJSON(final String attrName, final String attrValue) { + final HashMap map = new HashMap<>(); + map.put(attrName, attrValue); + return new Gson().toJson(map); + } + public HashMap createTrancheDetail(final String id, final String date, final String amount) { HashMap detail = new HashMap<>(); if (id != null) {