FINERACT-2152: API Create and retrieve interest pause
This commit is contained in:
committed by
Adam Saghy
parent
e94e6c421a
commit
c2ec015e64
@@ -133,6 +133,9 @@ public class CommandSource extends AbstractPersistableCustom<Long> {
|
||||
@Column(name = "result_status_code")
|
||||
private Integer resultStatusCode;
|
||||
|
||||
@Column(name = "loan_external_id", length = 100)
|
||||
private ExternalId loanExternalId;
|
||||
|
||||
public static CommandSource fullEntryFrom(final CommandWrapper wrapper, final JsonCommand command, final AppUser maker,
|
||||
String idempotencyKey, Integer status) {
|
||||
|
||||
@@ -156,6 +159,7 @@ public class CommandSource extends AbstractPersistableCustom<Long> {
|
||||
.transactionId(command.getTransactionId()) //
|
||||
.creditBureauId(command.getCreditBureauId()) //
|
||||
.organisationCreditBureauId(command.getOrganisationCreditBureauId()) //
|
||||
.loanExternalId(command.getLoanExternalId()) //
|
||||
.build(); //
|
||||
}
|
||||
|
||||
@@ -195,5 +199,6 @@ public class CommandSource extends AbstractPersistableCustom<Long> {
|
||||
this.resourceExternalId = result.getResourceExternalId();
|
||||
this.subResourceId = result.getSubResourceId();
|
||||
this.subResourceExternalId = result.getSubResourceExternalId();
|
||||
this.loanExternalId = result.getLoanExternalId();
|
||||
}
|
||||
}
|
||||
|
||||
+19
-4
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.commands.domain;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.useradministration.api.PasswordPreferencesApiConstants;
|
||||
|
||||
@Getter
|
||||
@@ -43,6 +44,7 @@ public class CommandWrapper {
|
||||
private final Long creditBureauId;
|
||||
private final Long organisationCreditBureauId;
|
||||
private final String jobName;
|
||||
private final ExternalId loanExternalId;
|
||||
|
||||
private final String idempotencyKey;
|
||||
|
||||
@@ -61,9 +63,11 @@ public class CommandWrapper {
|
||||
public static CommandWrapper fromExistingCommand(final Long commandId, final String actionName, final String entityName,
|
||||
final Long resourceId, final Long subresourceId, final String resourceGetUrl, final Long productId, final Long officeId,
|
||||
final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String transactionId,
|
||||
final Long creditBureauId, final Long organisationCreditBureauId, final String idempotencyKey) {
|
||||
final Long creditBureauId, final Long organisationCreditBureauId, final String idempotencyKey,
|
||||
final ExternalId loanExternalId) {
|
||||
return new CommandWrapper(commandId, actionName, entityName, resourceId, subresourceId, resourceGetUrl, productId, officeId,
|
||||
groupId, clientId, loanId, savingsId, transactionId, creditBureauId, organisationCreditBureauId, idempotencyKey);
|
||||
groupId, clientId, loanId, savingsId, transactionId, creditBureauId, organisationCreditBureauId, idempotencyKey,
|
||||
loanExternalId);
|
||||
}
|
||||
|
||||
private CommandWrapper(final Long commandId, final String actionName, final String entityName, final Long resourceId,
|
||||
@@ -87,12 +91,13 @@ public class CommandWrapper {
|
||||
this.organisationCreditBureauId = null;
|
||||
this.jobName = null;
|
||||
this.idempotencyKey = null;
|
||||
this.loanExternalId = null;
|
||||
}
|
||||
|
||||
public CommandWrapper(final Long officeId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId,
|
||||
final String actionName, final String entityName, final Long entityId, final Long subentityId, final String href,
|
||||
final String json, final String transactionId, final Long productId, final Long templateId, final Long creditBureauId,
|
||||
final Long organisationCreditBureauId, final String jobName, final String idempotencyKey) {
|
||||
final Long organisationCreditBureauId, final String jobName, final String idempotencyKey, final ExternalId loanExternalId) {
|
||||
|
||||
this.commandId = null;
|
||||
this.officeId = officeId;
|
||||
@@ -114,12 +119,13 @@ public class CommandWrapper {
|
||||
this.organisationCreditBureauId = organisationCreditBureauId;
|
||||
this.jobName = jobName;
|
||||
this.idempotencyKey = idempotencyKey;
|
||||
this.loanExternalId = loanExternalId;
|
||||
}
|
||||
|
||||
private CommandWrapper(final Long commandId, final String actionName, final String entityName, final Long resourceId,
|
||||
final Long subresourceId, final String resourceGetUrl, final Long productId, final Long officeId, final Long groupId,
|
||||
final Long clientId, final Long loanId, final Long savingsId, final String transactionId, final Long creditBureauId,
|
||||
final Long organisationCreditBureauId, final String idempotencyKey) {
|
||||
final Long organisationCreditBureauId, final String idempotencyKey, final ExternalId loanExternalId) {
|
||||
|
||||
this.commandId = commandId;
|
||||
this.officeId = officeId;
|
||||
@@ -140,6 +146,7 @@ public class CommandWrapper {
|
||||
this.organisationCreditBureauId = organisationCreditBureauId;
|
||||
this.jobName = null;
|
||||
this.idempotencyKey = idempotencyKey;
|
||||
this.loanExternalId = loanExternalId;
|
||||
}
|
||||
|
||||
public boolean isCreate() {
|
||||
@@ -240,6 +247,14 @@ public class CommandWrapper {
|
||||
return this.entityName.equalsIgnoreCase(PasswordPreferencesApiConstants.ENTITY_NAME);
|
||||
}
|
||||
|
||||
public boolean isInterestPauseResource() {
|
||||
return this.entityName.equalsIgnoreCase("INTEREST_PAUSE");
|
||||
}
|
||||
|
||||
public boolean isInterestPauseExternalIdResource() {
|
||||
return this.entityName.equalsIgnoreCase("INTEREST_PAUSE") && this.href.contains("/external-id/");
|
||||
}
|
||||
|
||||
public Long commandId() {
|
||||
return this.commandId;
|
||||
}
|
||||
|
||||
+20
-2
@@ -21,6 +21,7 @@ package org.apache.fineract.commands.service;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.infrastructure.accountnumberformat.service.AccountNumberFormatConstants;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
|
||||
import org.apache.fineract.portfolio.paymenttype.api.PaymentTypeApiResourceConstants;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
@@ -47,18 +48,19 @@ public class CommandWrapperBuilder {
|
||||
private Long organisationCreditBureauId;
|
||||
private String jobName;
|
||||
private String idempotencyKey;
|
||||
private ExternalId loanExternalId;
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "TODO: fix this!")
|
||||
public CommandWrapper build() {
|
||||
return new CommandWrapper(this.officeId, this.groupId, this.clientId, this.loanId, this.savingsId, this.actionName, this.entityName,
|
||||
this.entityId, this.subentityId, this.href, this.json, this.transactionId, this.productId, this.templateId,
|
||||
this.creditBureauId, this.organisationCreditBureauId, this.jobName, this.idempotencyKey);
|
||||
this.creditBureauId, this.organisationCreditBureauId, this.jobName, this.idempotencyKey, this.loanExternalId);
|
||||
}
|
||||
|
||||
public CommandWrapper build(String idempotencyKey) {
|
||||
return new CommandWrapper(this.officeId, this.groupId, this.clientId, this.loanId, this.savingsId, this.actionName, this.entityName,
|
||||
this.entityId, this.subentityId, this.href, this.json, this.transactionId, this.productId, this.templateId,
|
||||
this.creditBureauId, this.organisationCreditBureauId, this.jobName, idempotencyKey);
|
||||
this.creditBureauId, this.organisationCreditBureauId, this.jobName, idempotencyKey, this.loanExternalId);
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateCreditBureau() {
|
||||
@@ -3714,4 +3716,20 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/loans/" + loanId + "/delinquency-action";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createInterestPause(final long loanId) {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INTEREST_PAUSE";
|
||||
this.loanId = loanId;
|
||||
this.href = "/v1/loans/" + loanId + "/interest-pauses";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createInterestPauseByExternalId(final String loanExternalId) {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INTEREST_PAUSE";
|
||||
this.loanExternalId = new ExternalId(loanExternalId);
|
||||
this.href = "/v1/loans/external-id/" + loanExternalId + "/interest-pauses";
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -73,7 +73,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
|
||||
JsonCommand command = JsonCommand.from(json, parsedCommand, this.fromApiJsonHelper, wrapper.getEntityName(), wrapper.getEntityId(),
|
||||
wrapper.getSubentityId(), wrapper.getGroupId(), wrapper.getClientId(), wrapper.getLoanId(), wrapper.getSavingsId(),
|
||||
wrapper.getTransactionId(), wrapper.getHref(), wrapper.getProductId(), wrapper.getCreditBureauId(),
|
||||
wrapper.getOrganisationCreditBureauId(), wrapper.getJobName());
|
||||
wrapper.getOrganisationCreditBureauId(), wrapper.getJobName(), wrapper.getLoanExternalId());
|
||||
|
||||
return this.processAndLogCommandService.executeCommand(wrapper, command, isApprovedByChecker);
|
||||
}
|
||||
@@ -88,14 +88,16 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
|
||||
commandSourceInput.getResourceGetUrl(), commandSourceInput.getProductId(), commandSourceInput.getOfficeId(),
|
||||
commandSourceInput.getGroupId(), commandSourceInput.getClientId(), commandSourceInput.getLoanId(),
|
||||
commandSourceInput.getSavingsId(), commandSourceInput.getTransactionId(), commandSourceInput.getCreditBureauId(),
|
||||
commandSourceInput.getOrganisationCreditBureauId(), commandSourceInput.getIdempotencyKey());
|
||||
commandSourceInput.getOrganisationCreditBureauId(), commandSourceInput.getIdempotencyKey(),
|
||||
commandSourceInput.getLoanExternalId());
|
||||
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(commandSourceInput.getCommandAsJson());
|
||||
final JsonCommand command = JsonCommand.fromExistingCommand(makerCheckerId, commandSourceInput.getCommandAsJson(), parsedCommand,
|
||||
this.fromApiJsonHelper, commandSourceInput.getEntityName(), commandSourceInput.getResourceId(),
|
||||
commandSourceInput.getSubResourceId(), commandSourceInput.getGroupId(), commandSourceInput.getClientId(),
|
||||
commandSourceInput.getLoanId(), commandSourceInput.getSavingsId(), commandSourceInput.getTransactionId(),
|
||||
commandSourceInput.getResourceGetUrl(), commandSourceInput.getProductId(), commandSourceInput.getCreditBureauId(),
|
||||
commandSourceInput.getOrganisationCreditBureauId(), commandSourceInput.getJobName());
|
||||
commandSourceInput.getOrganisationCreditBureauId(), commandSourceInput.getJobName(),
|
||||
commandSourceInput.getLoanExternalId());
|
||||
|
||||
return this.processAndLogCommandService.executeCommand(wrapper, command, true);
|
||||
}
|
||||
|
||||
+2
@@ -249,6 +249,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
} else {
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
}
|
||||
} else if (wrapper.isInterestPauseResource() || wrapper.isInterestPauseExternalIdResource()) {
|
||||
handler = applicationContext.getBean("interestPauseCommandHandler", NewCommandSourceHandler.class);
|
||||
} else {
|
||||
handler = commandHandlerProvider.getHandler(wrapper.entityName(), wrapper.actionName());
|
||||
}
|
||||
|
||||
+20
-11
@@ -71,30 +71,34 @@ public final class JsonCommand {
|
||||
private final Long creditBureauId;
|
||||
private final Long organisationCreditBureauId;
|
||||
private final String jobName;
|
||||
private final ExternalId loanExternalId;
|
||||
|
||||
public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper,
|
||||
final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId,
|
||||
final Long loanId, final Long savingsId, final String transactionId, final String url, final Long productId,
|
||||
final Long creditBureauId, final Long organisationCreditBureauId, final String jobName) {
|
||||
final Long creditBureauId, final Long organisationCreditBureauId, final String jobName, final ExternalId loanExternalId) {
|
||||
return new JsonCommand(null, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, groupId,
|
||||
clientId, loanId, savingsId, transactionId, url, productId, creditBureauId, organisationCreditBureauId, jobName);
|
||||
clientId, loanId, savingsId, transactionId, url, productId, creditBureauId, organisationCreditBureauId, jobName,
|
||||
loanExternalId);
|
||||
|
||||
}
|
||||
|
||||
public static JsonCommand fromExistingCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
|
||||
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId,
|
||||
final String url, final Long productId, final Long creditBureauId, final Long organisationCreditBureauId,
|
||||
final String jobName) {
|
||||
final String url, final Long productId, final Long creditBureauId, final Long organisationCreditBureauId, final String jobName,
|
||||
final ExternalId loanExternalId) {
|
||||
return new JsonCommand(commandId, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, null, null,
|
||||
null, null, null, url, productId, creditBureauId, organisationCreditBureauId, jobName);
|
||||
null, null, null, url, productId, creditBureauId, organisationCreditBureauId, jobName, loanExternalId);
|
||||
}
|
||||
|
||||
public static JsonCommand fromExistingCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
|
||||
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId,
|
||||
final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String transactionId, final String url,
|
||||
final Long productId, Long creditBureauId, final Long organisationCreditBureauId, final String jobName) {
|
||||
final Long productId, Long creditBureauId, final Long organisationCreditBureauId, final String jobName,
|
||||
final ExternalId loanExternalId) {
|
||||
return new JsonCommand(commandId, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, groupId,
|
||||
clientId, loanId, savingsId, transactionId, url, productId, creditBureauId, organisationCreditBureauId, jobName);
|
||||
clientId, loanId, savingsId, transactionId, url, productId, creditBureauId, organisationCreditBureauId, jobName,
|
||||
loanExternalId);
|
||||
|
||||
}
|
||||
|
||||
@@ -103,7 +107,7 @@ public final class JsonCommand {
|
||||
return new JsonCommand(command.commandId, jsonCommand, parsedCommand, command.fromApiJsonHelper, command.entityName,
|
||||
command.resourceId, command.subresourceId, command.groupId, command.clientId, command.loanId, command.savingsId,
|
||||
command.transactionId, command.url, command.productId, command.creditBureauId, command.organisationCreditBureauId,
|
||||
command.jobName);
|
||||
command.jobName, command.loanExternalId);
|
||||
}
|
||||
|
||||
public static JsonCommand fromExistingCommand(JsonCommand command, final JsonElement parsedCommand, final Long clientId) {
|
||||
@@ -111,13 +115,14 @@ public final class JsonCommand {
|
||||
return new JsonCommand(command.commandId, jsonCommand, parsedCommand, command.fromApiJsonHelper, command.entityName,
|
||||
command.resourceId, command.subresourceId, command.groupId, clientId, command.loanId, command.savingsId,
|
||||
command.transactionId, command.url, command.productId, command.creditBureauId, command.organisationCreditBureauId,
|
||||
command.jobName);
|
||||
command.jobName, command.loanExternalId);
|
||||
}
|
||||
|
||||
public JsonCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
|
||||
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId,
|
||||
final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String transactionId, final String url,
|
||||
final Long productId, final Long creditBureauId, final Long organisationCreditBureauId, final String jobName) {
|
||||
final Long productId, final Long creditBureauId, final Long organisationCreditBureauId, final String jobName,
|
||||
final ExternalId loanExternalId) {
|
||||
|
||||
this.commandId = commandId;
|
||||
this.jsonCommand = jsonCommand;
|
||||
@@ -136,6 +141,7 @@ public final class JsonCommand {
|
||||
this.creditBureauId = creditBureauId;
|
||||
this.organisationCreditBureauId = organisationCreditBureauId;
|
||||
this.jobName = jobName;
|
||||
this.loanExternalId = loanExternalId;
|
||||
}
|
||||
|
||||
public static JsonCommand fromJsonElement(final Long resourceId, final JsonElement parsedCommand) {
|
||||
@@ -165,6 +171,7 @@ public final class JsonCommand {
|
||||
this.creditBureauId = null;
|
||||
this.organisationCreditBureauId = null;
|
||||
this.jobName = null;
|
||||
this.loanExternalId = null;
|
||||
}
|
||||
|
||||
public JsonCommand(final Long resourceId, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper) {
|
||||
@@ -185,10 +192,12 @@ public final class JsonCommand {
|
||||
this.creditBureauId = null;
|
||||
this.organisationCreditBureauId = null;
|
||||
this.jobName = null;
|
||||
this.loanExternalId = null;
|
||||
}
|
||||
|
||||
public static JsonCommand from(final String jsonCommand) {
|
||||
return new JsonCommand(null, jsonCommand, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
return new JsonCommand(null, jsonCommand, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -49,12 +49,13 @@ public class CommandProcessingResult implements Serializable {
|
||||
private Boolean rollbackTransaction;
|
||||
private final ExternalId resourceExternalId;
|
||||
private final ExternalId subResourceExternalId;
|
||||
private final ExternalId loanExternalId;
|
||||
|
||||
private CommandProcessingResult(final Long commandId, final Long officeId, final Long groupId, final Long clientId, final Long loanId,
|
||||
final Long savingsId, final String resourceIdentifier, final Long resourceId, final String transactionId,
|
||||
final Map<String, Object> changes, final Long productId, final Long gsimId, final Long glimId,
|
||||
final Map<String, Object> creditBureauReportData, Boolean rollbackTransaction, final Long subResourceId,
|
||||
final ExternalId resourceExternalId, final ExternalId subResourceExternalId) {
|
||||
final ExternalId resourceExternalId, final ExternalId subResourceExternalId, final ExternalId loanExternalId) {
|
||||
this.commandId = commandId;
|
||||
this.officeId = officeId;
|
||||
this.groupId = groupId;
|
||||
@@ -73,12 +74,13 @@ public class CommandProcessingResult implements Serializable {
|
||||
this.subResourceId = subResourceId;
|
||||
this.resourceExternalId = resourceExternalId;
|
||||
this.subResourceExternalId = subResourceExternalId;
|
||||
this.loanExternalId = loanExternalId;
|
||||
}
|
||||
|
||||
protected CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map<String, Object> changes,
|
||||
Long clientId) {
|
||||
this(commandId, officeId, null, clientId, null, null, resourceId == null ? null : resourceId.toString(), resourceId, null, changes,
|
||||
null, null, null, null, null, null, ExternalId.empty(), ExternalId.empty());
|
||||
null, null, null, null, null, null, ExternalId.empty(), ExternalId.empty(), ExternalId.empty());
|
||||
}
|
||||
|
||||
protected CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map<String, Object> changes) {
|
||||
@@ -94,7 +96,7 @@ public class CommandProcessingResult implements Serializable {
|
||||
commandResult.loanId, commandResult.savingsId, commandResult.resourceIdentifier, resourceId, commandResult.transactionId,
|
||||
commandResult.changes, commandResult.productId, commandResult.gsimId, commandResult.glimId,
|
||||
commandResult.creditBureauReportData, commandResult.rollbackTransaction, commandResult.subResourceId,
|
||||
commandResult.resourceExternalId, commandResult.subResourceExternalId);
|
||||
commandResult.resourceExternalId, commandResult.subResourceExternalId, commandResult.loanExternalId);
|
||||
}
|
||||
|
||||
public static CommandProcessingResult fromCommandProcessingResult(CommandProcessingResult commandResult) {
|
||||
@@ -105,10 +107,10 @@ public class CommandProcessingResult implements Serializable {
|
||||
final Long loanId, final Long savingsId, final String resourceIdentifier, final Long entityId, final Long gsimId,
|
||||
final Long glimId, final Map<String, Object> creditBureauReportData, final String transactionId,
|
||||
final Map<String, Object> changes, final Long productId, final Boolean rollbackTransaction, final Long subResourceId,
|
||||
final ExternalId resourceExternalId, final ExternalId subResourceExternalId) {
|
||||
final ExternalId resourceExternalId, final ExternalId subResourceExternalId, final ExternalId loanExternalId) {
|
||||
return new CommandProcessingResult(commandId, officeId, groupId, clientId, loanId, savingsId, resourceIdentifier, entityId,
|
||||
transactionId, changes, productId, gsimId, glimId, creditBureauReportData, rollbackTransaction, subResourceId,
|
||||
resourceExternalId, subResourceExternalId);
|
||||
resourceExternalId, subResourceExternalId, loanExternalId);
|
||||
}
|
||||
|
||||
public static CommandProcessingResult commandOnlyResult(final Long commandId) {
|
||||
|
||||
+8
-1
@@ -46,10 +46,13 @@ public class CommandProcessingResultBuilder {
|
||||
|
||||
private ExternalId subEntityExternalId = ExternalId.empty();
|
||||
|
||||
private ExternalId loanExternalId = ExternalId.empty();
|
||||
|
||||
public CommandProcessingResult build() {
|
||||
return CommandProcessingResult.fromDetails(this.commandId, this.officeId, this.groupId, this.clientId, this.loanId, this.savingsId,
|
||||
this.resourceIdentifier, this.entityId, this.gsimId, this.glimId, this.creditBureauReportData, this.transactionId,
|
||||
this.changes, this.productId, this.rollbackTransaction, this.subEntityId, this.entityExternalId, this.subEntityExternalId);
|
||||
this.changes, this.productId, this.rollbackTransaction, this.subEntityId, this.entityExternalId, this.subEntityExternalId,
|
||||
this.loanExternalId);
|
||||
}
|
||||
|
||||
public CommandProcessingResultBuilder withCommandId(final Long withCommandId) {
|
||||
@@ -142,4 +145,8 @@ public class CommandProcessingResultBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandProcessingResultBuilder withLoanExternalId(final ExternalId loanExternalId) {
|
||||
this.loanExternalId = loanExternalId;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class IdempotencyCommandProcessFailedExceptionTest {
|
||||
public void testInconsistentStatus() {
|
||||
IdempotentCommandExceptionMapper mapper = new IdempotentCommandExceptionMapper();
|
||||
CommandWrapper command = new CommandWrapper(null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null);
|
||||
null, null, null, null, null);
|
||||
CommandSource source = CommandSource.fullEntryFrom(command, JsonCommand.from("{}"), null, "dummy-key", null);
|
||||
IdempotentCommandProcessFailedException exception = new IdempotentCommandProcessFailedException(command, null, source);
|
||||
Response result = mapper.toResponse(exception);
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* 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.interestpauses.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseRequestDto;
|
||||
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseResponseDto;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/loans")
|
||||
@Component
|
||||
@Tag(name = "Loan Interest Pause", description = "APIs for managing interest pause periods on loans.")
|
||||
@RequiredArgsConstructor
|
||||
public class LoanInterestPauseApiResource {
|
||||
|
||||
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final InterestPauseReadPlatformService interestPauseReadPlatformService;
|
||||
|
||||
@POST
|
||||
@Path("/{loanId}/interest-pauses")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Create a new interest pause period for a loan", description = "Allows users to define a period during which no interest will be accrued for a specific loan.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public CommandProcessingResult createInterestPause(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
|
||||
@RequestBody(required = true) final InterestPauseRequestDto request) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createInterestPause(loanId).withJson(request.toJson()).build();
|
||||
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/external-id/{loanExternalId}/interest-pauses")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Create a new interest pause for a loan using external ID", description = "Allows users to define a period during which no interest will be accrued for a specific loan using the external loan ID.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public CommandProcessingResult createInterestPauseByExternalId(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId") final String loanExternalId,
|
||||
@RequestBody(required = true) final InterestPauseRequestDto request) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createInterestPauseByExternalId(loanExternalId)
|
||||
.withJson(request.toJson()).build();
|
||||
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{loanId}/interest-pauses")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve all interest pause periods for a loan", description = "Fetches a list of all active interest pause periods for a specific loan.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public List<InterestPauseResponseDto> retrieveInterestPauses(
|
||||
@PathParam("loanId") @Parameter(description = "loanId") final Long loanId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
return this.interestPauseReadPlatformService.retrieveInterestPauses(loanId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/external-id/{loanExternalId}/interest-pauses")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve all interest pause periods for a loan using external ID", description = "Fetches a list of all active interest pause periods for a specific loan using the external loan ID.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public List<InterestPauseResponseDto> retrieveInterestPausesByExternalId(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId") final String loanExternalId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
return this.interestPauseReadPlatformService.retrieveInterestPauses(loanExternalId);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.interestpauses.data;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "Request DTO for creating an interest pause")
|
||||
public class InterestPauseRequestDto {
|
||||
|
||||
@Schema(example = "2024-01-01", description = "Start date of the interest pause period")
|
||||
private String startDate;
|
||||
|
||||
@Schema(example = "2024-01-11", description = "End date of the interest pause period")
|
||||
private String endDate;
|
||||
|
||||
@Schema(example = "yyyy-MM-dd", description = "Format of the dates provided")
|
||||
private String dateFormat;
|
||||
|
||||
@Schema(example = "en", description = "Locale to interpret the date format")
|
||||
private String locale;
|
||||
|
||||
public String toJson() {
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException("Error serializing request to JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.interestpauses.data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "Response DTO for Interest Pause periods")
|
||||
public class InterestPauseResponseDto {
|
||||
|
||||
@Schema(example = "1", description = "ID of the loan term variation")
|
||||
private Long id;
|
||||
|
||||
@Schema(example = "2024-01-01", description = "Start date of the interest pause period")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(example = "2024-01-11", description = "End date of the interest pause period")
|
||||
private LocalDate endDate;
|
||||
|
||||
@Schema(example = "yyyy-MM-dd", description = "Date format used to interpret start and end dates")
|
||||
private String dateFormat;
|
||||
|
||||
@Schema(example = "en", description = "Locale used for date formatting")
|
||||
private String locale;
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.interestpauses.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("interestPauseCommandHandler")
|
||||
@RequiredArgsConstructor
|
||||
public class InterestPauseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InterestPauseWritePlatformService interestPauseService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
CommandProcessingResult result;
|
||||
|
||||
final String startDate = command.stringValueOfParameterNamed("startDate");
|
||||
final String endDate = command.stringValueOfParameterNamed("endDate");
|
||||
final String dateFormat = command.stringValueOfParameterNamed("dateFormat");
|
||||
final String locale = command.stringValueOfParameterNamed("locale");
|
||||
|
||||
if (command.getLoanId() != null) {
|
||||
final Long loanId = command.getLoanId();
|
||||
result = interestPauseService.createInterestPause(loanId, startDate, endDate, dateFormat, locale);
|
||||
} else if (command.getLoanExternalId() != null) {
|
||||
final ExternalId loanExternalId = command.getLoanExternalId();
|
||||
result = interestPauseService.createInterestPause(loanExternalId, startDate, endDate, dateFormat, locale);
|
||||
} else {
|
||||
throw new PlatformApiDataValidationException("validation.msg.missing.loan.id.or.external.id",
|
||||
"Either loanId or loanExternalId must be provided.", "loanId");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.interestpauses.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseResponseDto;
|
||||
|
||||
public interface InterestPauseReadPlatformService {
|
||||
|
||||
/**
|
||||
* Retrieve all interest pause periods for a valid loan.
|
||||
*
|
||||
* @param loanId
|
||||
* @return List of InterestPauseData
|
||||
*/
|
||||
List<InterestPauseResponseDto> retrieveInterestPauses(Long loanId);
|
||||
|
||||
/**
|
||||
* Retrieve all interest pause periods for a loan using external ID.
|
||||
*
|
||||
* @param loanExternalId
|
||||
* @return List of InterestPauseData
|
||||
*/
|
||||
List<InterestPauseResponseDto> retrieveInterestPauses(String loanExternalId);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.interestpauses.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseResponseDto;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class InterestPauseReadPlatformServiceImpl implements InterestPauseReadPlatformService {
|
||||
|
||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||
|
||||
@Override
|
||||
public List<InterestPauseResponseDto> retrieveInterestPauses(Long loanId) {
|
||||
List<LoanTermVariationsData> variations = this.loanTermVariationsRepository.findLoanTermVariationsByLoanIdAndTermType(loanId,
|
||||
LoanTermVariationType.INTEREST_PAUSE.getValue());
|
||||
|
||||
return mapToInterestPauseResponse(variations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InterestPauseResponseDto> retrieveInterestPauses(String loanExternalId) {
|
||||
List<LoanTermVariationsData> variations = this.loanTermVariationsRepository.findLoanTermVariationsByExternalLoanIdAndTermType(
|
||||
new ExternalId(loanExternalId), LoanTermVariationType.INTEREST_PAUSE.getValue());
|
||||
|
||||
return mapToInterestPauseResponse(variations);
|
||||
}
|
||||
|
||||
private List<InterestPauseResponseDto> mapToInterestPauseResponse(List<LoanTermVariationsData> variations) {
|
||||
return variations.stream()
|
||||
.map(variation -> new InterestPauseResponseDto(variation.getId(), variation.getTermVariationApplicableFrom(),
|
||||
variation.getDateValue(), DateUtils.DEFAULT_DATE_FORMAT, Locale.getDefault().toString()))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.interestpauses.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
|
||||
public interface InterestPauseWritePlatformService {
|
||||
|
||||
CommandProcessingResult createInterestPause(ExternalId loanExternalId, String startDate, String endDate, String dateFormat,
|
||||
String locale);
|
||||
|
||||
/**
|
||||
* Create a new interest pause period for a loan identified by its internal ID.
|
||||
*
|
||||
* @param loanId
|
||||
* the ID of the loan
|
||||
* @param startDate
|
||||
* the start date of the interest pause period (inclusive)
|
||||
* @param endDate
|
||||
* the end date of the interest pause period (inclusive)
|
||||
* @return the ID of the created loan term variation representing the interest pause
|
||||
*/
|
||||
CommandProcessingResult createInterestPause(Long loanId, String startDate, String endDate, String dateFormat, String locale);
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 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.interestpauses.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Transactional
|
||||
public class InterestPauseWritePlatformServiceImpl implements InterestPauseWritePlatformService {
|
||||
|
||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createInterestPause(ExternalId loanExternalId, String startDateString, String endDateString,
|
||||
String dateFormat, String locale) {
|
||||
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
|
||||
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
|
||||
|
||||
return processInterestPause(() -> loanRepositoryWrapper.findOneWithNotFoundDetection(loanExternalId), startDate, endDate,
|
||||
dateFormat, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createInterestPause(Long loanId, String startDateString, String endDateString, String dateFormat,
|
||||
String locale) {
|
||||
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
|
||||
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
|
||||
|
||||
return processInterestPause(() -> loanRepositoryWrapper.findOneWithNotFoundDetection(loanId), startDate, endDate, dateFormat,
|
||||
locale);
|
||||
}
|
||||
|
||||
private CommandProcessingResult processInterestPause(Supplier<Loan> loanSupplier, LocalDate startDate, LocalDate endDate,
|
||||
String dateFormat, String locale) {
|
||||
final Loan loan = loanSupplier.get();
|
||||
|
||||
validateInterestPauseDates(loan, startDate, endDate, dateFormat, locale);
|
||||
|
||||
LoanTermVariations variation = new LoanTermVariations(LoanTermVariationType.INTEREST_PAUSE.getValue(), startDate, null, endDate,
|
||||
false, loan);
|
||||
|
||||
LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
|
||||
|
||||
return new CommandProcessingResultBuilder().withEntityId(savedVariation.getId()).build();
|
||||
}
|
||||
|
||||
private void validateInterestPauseDates(Loan loan, LocalDate startDate, LocalDate endDate, String dateFormat, String locale) {
|
||||
|
||||
validateOrThrow(baseDataValidator -> {
|
||||
baseDataValidator.reset().parameter("startDate").value(startDate).notBlank();
|
||||
baseDataValidator.reset().parameter("endDate").value(endDate).notBlank();
|
||||
baseDataValidator.reset().parameter("dateFormat").value(dateFormat).notBlank();
|
||||
baseDataValidator.reset().parameter("locale").value(locale).notBlank();
|
||||
});
|
||||
|
||||
if (startDate.isBefore(loan.getSubmittedOnDate())) {
|
||||
throw new GeneralPlatformDomainRuleException("interest.pause.start.date.before.loan.start.date",
|
||||
String.format("Interest pause start date (%s) cannot be earlier than loan start date (%s).", startDate,
|
||||
loan.getSubmittedOnDate()),
|
||||
startDate, loan.getSubmittedOnDate());
|
||||
}
|
||||
|
||||
if (endDate.isAfter(loan.getMaturityDate())) {
|
||||
throw new GeneralPlatformDomainRuleException("interest.pause.end.date.after.loan.maturity.date", String
|
||||
.format("Interest pause end date (%s) cannot be later than loan maturity date (%s).", endDate, loan.getMaturityDate()),
|
||||
endDate, loan.getMaturityDate());
|
||||
}
|
||||
|
||||
if (endDate.isBefore(startDate)) {
|
||||
throw new GeneralPlatformDomainRuleException("interest.pause.end.date.before.start.date", String
|
||||
.format("Interest pause end date (%s) must not be before the interest pause start date (%s).", endDate, startDate),
|
||||
endDate, startDate);
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDate parseDate(String date, String dateFormat, String locale) {
|
||||
try {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat, Locale.forLanguageTag(locale));
|
||||
return LocalDate.parse(date, formatter);
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.invalid.date.format",
|
||||
String.format("Invalid date format. Provided: %s, Expected format: %s, Locale: %s", date, dateFormat, locale),
|
||||
e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateOrThrow(Consumer<DataValidatorBuilder> baseDataValidator) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors).resource("InterestPause");
|
||||
|
||||
baseDataValidator.accept(dataValidatorBuilder);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -113,6 +113,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
+ "and (:futureCharges = true or ls.fromDate < :tillDate or (ls.installmentNumber = (select min(lsi.installmentNumber) from LoanRepaymentScheduleInstallment lsi where lsi.loan.id = l.id and lsi.isDownPayment = false) and ls.fromDate = :tillDate))))";
|
||||
String FIND_LOANS_FOR_ADD_ACCRUAL = LOANS_FOR_ACCRUAL + "and (:futureCharges = true or ls.dueDate <= :tillDate)))";
|
||||
|
||||
String FIND_LOAN_BY_EXTERNAL_ID = "SELECT loan FROM Loan loan WHERE loan.externalId = :externalId";
|
||||
|
||||
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
|
||||
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") LocalDate disbursementDate, @Param("groupId") Long groupId,
|
||||
@Param("loanType") Integer loanType);
|
||||
@@ -247,4 +249,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
@Query(FIND_LOANS_FOR_ADD_ACCRUAL)
|
||||
List<Loan> findLoansForAddAccrual(@Param("accountingType") Integer accountingType, @Param("tillDate") LocalDate tillDate,
|
||||
@Param("futureCharges") boolean futureCharges);
|
||||
|
||||
@Query(FIND_LOAN_BY_EXTERNAL_ID)
|
||||
Optional<Loan> findByExternalId(@Param("externalId") ExternalId externalId);
|
||||
}
|
||||
|
||||
+14
@@ -49,6 +49,20 @@ public class LoanRepositoryWrapper {
|
||||
return this.findOneWithNotFoundDetection(id, false);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Loan findOneWithNotFoundDetection(final ExternalId externalId) {
|
||||
return this.findOneWithNotFoundDetection(externalId, false);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Loan findOneWithNotFoundDetection(final ExternalId externalId, boolean loadLazyCollections) {
|
||||
final Loan loan = this.repository.findByExternalId(externalId).orElseThrow(() -> new LoanNotFoundException(externalId));
|
||||
if (loadLazyCollections) {
|
||||
loan.initializeLazyCollections();
|
||||
}
|
||||
return loan;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Loan findOneWithNotFoundDetection(final Long id, boolean loadLazyCollections) {
|
||||
final Loan loan = this.repository.findById(id).orElseThrow(() -> new LoanNotFoundException(id));
|
||||
|
||||
+5
-1
@@ -30,7 +30,8 @@ public enum LoanTermVariationType {
|
||||
GRACE_ON_INTEREST(7, "loanTermType.graceOnInterest"), //
|
||||
GRACE_ON_PRINCIPAL(8, "loanTermType.graceOnPrincipal"), //
|
||||
EXTEND_REPAYMENT_PERIOD(9, "loanTermType.extendRepaymentPeriod"), //
|
||||
INTEREST_RATE_FROM_INSTALLMENT(10, "loanTermType.interestRateFromInstallment"); //
|
||||
INTEREST_RATE_FROM_INSTALLMENT(10, "loanTermType.interestRateFromInstallment"), //
|
||||
INTEREST_PAUSE(11, "loanTermType.interestPause"); //
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -82,6 +83,9 @@ public enum LoanTermVariationType {
|
||||
case 10:
|
||||
enumeration = LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT;
|
||||
break;
|
||||
case 11:
|
||||
enumeration = LoanTermVariationType.INTEREST_PAUSE;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
|
||||
+11
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
@@ -49,4 +50,14 @@ public interface LoanTermVariationsRepository
|
||||
""")
|
||||
List<LoanTermVariationsData> findLoanTermVariationsByLoanIdAndTermType(@Param("loanId") long loanId, @Param("termType") int termType);
|
||||
|
||||
@Query("""
|
||||
select new org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData(
|
||||
ltv.id, ltv.termType, ltv.termApplicableFrom, ltv.decimalValue, ltv.dateValue, ltv.isSpecificToInstallment
|
||||
)
|
||||
from LoanTermVariations ltv
|
||||
where ltv.loan.externalId = :loanExternalId and ltv.termType = :termType
|
||||
order by ltv.termApplicableFrom
|
||||
""")
|
||||
List<LoanTermVariationsData> findLoanTermVariationsByExternalLoanIdAndTermType(@Param("loanExternalId") ExternalId loanExternalId,
|
||||
@Param("termType") int termType);
|
||||
}
|
||||
|
||||
+1
-1
@@ -230,7 +230,7 @@ class AdvancedPaymentAllocationsJsonParserTest {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonMap);
|
||||
JsonCommand command = JsonCommand.from(json, JsonParser.parseString(json), fromJsonHelper, null, 1L, 2L, 3L, 4L, null, null, null,
|
||||
null, null, null, null, null);
|
||||
null, null, null, null, null, null);
|
||||
return command;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -118,7 +118,7 @@ public class CreditAllocationsJsonParserTest {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonMap);
|
||||
JsonCommand command = JsonCommand.from(json, JsonParser.parseString(json), fromJsonHelper, null, 1L, 2L, 3L, 4L, null, null, null,
|
||||
null, null, null, null, null);
|
||||
null, null, null, null, null, null);
|
||||
return command;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ public class CenterImportHandler implements ImportHandler {
|
||||
String payload = gsonBuilder.create().toJson(calendarData);
|
||||
CommandWrapper commandWrapper = new CommandWrapper(result.getOfficeId(), result.getGroupId(), result.getClientId(),
|
||||
result.getLoanId(), result.getSavingsId(), null, null, null, null, null, payload, result.getTransactionId(),
|
||||
result.getProductId(), null, null, null, null, idempotencyKeyGenerator.create());
|
||||
result.getProductId(), null, null, null, null, idempotencyKeyGenerator.create(), null);
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.createCalendar(commandWrapper, TemplatePopulateImportConstants.CENTER_ENTITY_TYPE, result.getGroupId()) //
|
||||
.withJson(payload) //
|
||||
|
||||
+1
-1
@@ -246,7 +246,7 @@ public class GroupImportHandler implements ImportHandler {
|
||||
String payload = gsonBuilder.create().toJson(calendarData);
|
||||
CommandWrapper commandWrapper = new CommandWrapper(result.getOfficeId(), result.getGroupId(), result.getClientId(),
|
||||
result.getLoanId(), result.getSavingsId(), null, null, null, null, null, payload, result.getTransactionId(),
|
||||
result.getProductId(), null, null, null, null, idempotencyKeyGenerator.create());
|
||||
result.getProductId(), null, null, null, null, idempotencyKeyGenerator.create(), null);
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.createCalendar(commandWrapper, TemplatePopulateImportConstants.CENTER_ENTITY_TYPE, result.getGroupId()) //
|
||||
.withJson(payload) //
|
||||
|
||||
+2
-1
@@ -429,7 +429,8 @@ public class ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl implemen
|
||||
JsonCommand apicommand = JsonCommand.from(json, parsedCommand, this.fromApiJsonHelper, wrapper.getEntityName(),
|
||||
wrapper.getEntityId(), wrapper.getSubentityId(), wrapper.getGroupId(), wrapper.getClientId(), wrapper.getLoanId(),
|
||||
wrapper.getSavingsId(), wrapper.getTransactionId(), wrapper.getHref(), wrapper.getProductId(),
|
||||
wrapper.getCreditBureauId(), wrapper.getOrganisationCreditBureauId(), wrapper.getJobName());
|
||||
wrapper.getCreditBureauId(), wrapper.getOrganisationCreditBureauId(), wrapper.getJobName(),
|
||||
wrapper.getLoanExternalId());
|
||||
|
||||
this.fromApiJsonDeserializer.validateForCreate(apicommand.json());
|
||||
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ public class InteropWrapperBuilder {
|
||||
|
||||
public CommandWrapper build() {
|
||||
return new CommandWrapper(null, null, null, null, null, actionName, entityName, null, null, href, json, null, null, null, null,
|
||||
null, null, null);
|
||||
null, null, null, null);
|
||||
}
|
||||
|
||||
public InteropWrapperBuilder withJson(final String json) {
|
||||
|
||||
+1
-1
@@ -784,7 +784,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString());
|
||||
final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand, this.fromApiJsonHelper, null, null,
|
||||
null, null, null, loanId, null, null, null, null, null, null, null);
|
||||
null, null, null, loanId, null, null, null, null, null, null, null, null);
|
||||
LoanOverdueDTO overdueDTO = applyChargeToOverdueLoanInstallment(loan, overdueInstallment.getChargeId(),
|
||||
overdueInstallment.getPeriodNumber(), command);
|
||||
loan = overdueDTO.getLoan();
|
||||
|
||||
+18
@@ -64,6 +64,10 @@ import org.apache.fineract.portfolio.fund.domain.FundRepository;
|
||||
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
|
||||
import org.apache.fineract.portfolio.group.domain.GroupRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseReadPlatformService;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePlatformService;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
@@ -81,6 +85,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanSchedu
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationTransitionValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
|
||||
@@ -456,4 +461,17 @@ public class LoanAccountConfiguration {
|
||||
public LoanRefundService loanRefundService(LoanRefundValidator loanRefundValidator) {
|
||||
return new LoanRefundService(loanRefundValidator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(InterestPauseReadPlatformService.class)
|
||||
public InterestPauseReadPlatformService interestPauseReadPlatformService(LoanTermVariationsRepository loanTermVariationsRepository) {
|
||||
return new InterestPauseReadPlatformServiceImpl(loanTermVariationsRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
|
||||
public InterestPauseWritePlatformService interestPauseWritePlatformService(LoanTermVariationsRepository loanTermVariationsRepository,
|
||||
LoanRepositoryWrapper loanRepositoryWrapper) {
|
||||
return new InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, loanRepositoryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ public class ShareAccountCommandsServiceImpl implements AccountsCommandsService
|
||||
public Object handleCommand(Long accountId, String command, String jsonBody) {
|
||||
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(jsonBody);
|
||||
final JsonCommand jsonCommand = JsonCommand.from(jsonBody, parsedCommand, this.fromApiJsonHelper, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null);
|
||||
null, null, null, null, null, null, null, null, null);
|
||||
if (ShareAccountApiConstants.APPROVE_COMMAND.equals(command)) {
|
||||
return approveShareAccount(accountId, jsonCommand);
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ public class ShareProductCommandsServiceImpl implements ProductCommandsService {
|
||||
public Object handleCommand(Long productId, String command, String jsonBody) {
|
||||
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(jsonBody);
|
||||
final JsonCommand jsonCommand = JsonCommand.from(jsonBody, parsedCommand, this.fromApiJsonHelper, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null);
|
||||
null, null, null, null, null, null, null, null, null);
|
||||
if (ShareProductApiConstants.PREIEW_DIVIDENDS_COMMAND_STRING.equals(command)) {
|
||||
return null;
|
||||
} else if (ShareProductApiConstants.POST_DIVIDENdS_COMMAND_STRING.equals(command)) {
|
||||
|
||||
@@ -179,4 +179,5 @@
|
||||
<include file="parts/0158_create_acc_gl_journal_entry_annual_summary.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0159_trial_balance_summary_with_asset_owner_year_end_summary.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0160_add_acc_product_mapping_product_id_index.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0161_add_loan_external_id_to_commands.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||
|
||||
<changeSet id="1" author="fineract">
|
||||
<addColumn tableName="m_portfolio_command_source">
|
||||
<column name="loan_external_id" type="VARCHAR(100)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+2
-2
@@ -41,8 +41,8 @@ public class CommandHandlerProviderStepDefinitions implements En {
|
||||
});
|
||||
|
||||
When("The user processes the command with ID {long}", (Long id) -> {
|
||||
this.result = commandHandler
|
||||
.processCommand(JsonCommand.fromExistingCommand(id, null, null, null, null, null, null, null, null, null, null, null));
|
||||
this.result = commandHandler.processCommand(
|
||||
JsonCommand.fromExistingCommand(id, null, null, null, null, null, null, null, null, null, null, null, null));
|
||||
});
|
||||
|
||||
Then("The command ID matches {long}", (Long id) -> {
|
||||
|
||||
+2
-2
@@ -101,7 +101,7 @@ public class CommandServiceStepDefinitions implements En {
|
||||
|
||||
public DummyCommand() {
|
||||
super(null, null, null, null, null, null, null, null, null, null, "{}", null, null, null, null, null, null,
|
||||
UUID.randomUUID().toString());
|
||||
UUID.randomUUID().toString(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,7 +124,7 @@ public class CommandServiceStepDefinitions implements En {
|
||||
JsonCommand command = JsonCommand.from(json, null, null, wrapper.getEntityName(), wrapper.getEntityId(),
|
||||
wrapper.getSubentityId(), wrapper.getGroupId(), wrapper.getClientId(), wrapper.getLoanId(), wrapper.getSavingsId(),
|
||||
wrapper.getTransactionId(), wrapper.getHref(), wrapper.getProductId(), wrapper.getCreditBureauId(),
|
||||
wrapper.getOrganisationCreditBureauId(), wrapper.getJobName());
|
||||
wrapper.getOrganisationCreditBureauId(), wrapper.getJobName(), wrapper.getLoanExternalId());
|
||||
|
||||
return this.processAndLogCommandService.executeCommand(wrapper, command, true);
|
||||
}
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ public class IdempotencyKeyResolverTest {
|
||||
public void testIPKResolveFromWrapper() {
|
||||
String idk = "idk";
|
||||
CommandWrapper wrapper = new CommandWrapper(null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, idk);
|
||||
null, null, null, idk, null);
|
||||
String resolvedIdk = underTest.resolve(wrapper);
|
||||
Assertions.assertEquals(idk, resolvedIdk);
|
||||
}
|
||||
|
||||
+1
-1
@@ -421,7 +421,7 @@ public class ThitsaWorksCreditBureauIntegrationWritePlatformServiceImplTest {
|
||||
command.put("creditBureauID", "1"); // Must match to the mocked config
|
||||
String json = mapper.writeValueAsString(command);
|
||||
return JsonCommand.from(json, JsonParser.parseString(json), fromJsonHelper, null, 1L, 2L, 3L, 4L, null, null, null, null, null,
|
||||
null, null, null);
|
||||
null, null, null, null);
|
||||
}
|
||||
|
||||
private void mockTokenGeneration() {
|
||||
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* 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 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.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.client.models.AdvancedPaymentData;
|
||||
import org.apache.fineract.client.models.PaymentAllocationOrder;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansRequest;
|
||||
import org.apache.fineract.client.models.PostLoansResponse;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Slf4j
|
||||
@ExtendWith({ LoanTestLifecycleExtension.class })
|
||||
public class LoanInterestPauseApiTest extends BaseLoanIntegrationTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LoanInterestPauseApiTest.class);
|
||||
|
||||
private static RequestSpecification REQUEST_SPEC;
|
||||
private static ResponseSpecification RESPONSE_SPEC;
|
||||
private static ResponseSpecification RESPONSE_SPEC_403;
|
||||
private static LoanTransactionHelper LOAN_TRANSACTIONAL_HELPER;
|
||||
private static LoanTransactionHelper LOAN_TRANSACTION_HELPER_403;
|
||||
private static AccountHelper accountHelper;
|
||||
private static PostClientsResponse client;
|
||||
private static Integer loanProductId;
|
||||
private static Long loanId;
|
||||
private static Long nonExistLoanId = 99999L;
|
||||
private static String externalId;
|
||||
private static String nonExistExternalId = "7c4fb86f-a778-4d02-b7a8-ec3ec98941fa";
|
||||
|
||||
@BeforeAll
|
||||
public static void setupTests() {
|
||||
Utils.initializeRESTAssured();
|
||||
REQUEST_SPEC = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||
REQUEST_SPEC.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||
RESPONSE_SPEC = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
RESPONSE_SPEC_403 = new ResponseSpecBuilder().expectStatusCode(403).build();
|
||||
LOAN_TRANSACTIONAL_HELPER = new LoanTransactionHelper(REQUEST_SPEC, RESPONSE_SPEC);
|
||||
LOAN_TRANSACTION_HELPER_403 = new LoanTransactionHelper(REQUEST_SPEC, RESPONSE_SPEC_403);
|
||||
accountHelper = new AccountHelper(REQUEST_SPEC, RESPONSE_SPEC);
|
||||
ClientHelper clientHelper = new ClientHelper(REQUEST_SPEC, RESPONSE_SPEC);
|
||||
client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||
|
||||
final Account assetAccount = accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = accountHelper.createLiabilityAccount();
|
||||
|
||||
externalId = UUID.randomUUID().toString();
|
||||
|
||||
loanProductId = createLoanProduct("500", "15", "4", true, "25", true, LoanScheduleType.PROGRESSIVE,
|
||||
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
|
||||
|
||||
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), loanProductId, BigDecimal.valueOf(500.0), 45,
|
||||
15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023", externalId);
|
||||
|
||||
loanId = loanResponse.getLoanId();
|
||||
|
||||
Assertions.assertNotNull(loanProductId, "Loan Product ID should not be null after creation");
|
||||
Assertions.assertNotNull(loanId, "Loan ID should not be null after creation");
|
||||
Assertions.assertNotNull(externalId, "External Loan ID should not be null after creation");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInterestPauseByLoanId_validRequest_shouldSucceed() {
|
||||
PostLoansLoanIdTransactionsResponse response = LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01", "2023-02-05",
|
||||
"yyyy-MM-dd", "en", loanId);
|
||||
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertNotNull(response.getResourceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInterestPauseByLoanId_endDateBeforeStartDate_shouldFail() {
|
||||
try {
|
||||
LOAN_TRANSACTION_HELPER_403.createInterestPauseByLoanId("2024-12-05", "2024-12-01", "yyyy-MM-dd", "en", loanId);
|
||||
} catch (Exception e) {
|
||||
String responseBody = e.getMessage();
|
||||
Assertions.assertNotNull(responseBody, "Response body should not be null");
|
||||
Assertions.assertTrue(responseBody.contains("interest.pause.end.date.before.start.date"),
|
||||
"Response should contain the validation error message for end date before start date");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInterestPauseByLoanId_startDateBeforeLoanStart_shouldFail() {
|
||||
try {
|
||||
LOAN_TRANSACTION_HELPER_403.createInterestPauseByLoanId("2022-12-01", "2024-12-05", "yyyy-MM-dd", "en", loanId);
|
||||
} catch (Exception e) {
|
||||
String responseBody = e.getMessage();
|
||||
Assertions.assertNotNull(responseBody, "Response body should not be null");
|
||||
Assertions.assertTrue(responseBody.contains("interest.pause.start.date.before.loan.start.date"),
|
||||
"Response should contain the validation error message for start date before loan start date");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInterestPauseByLoanId_endDateAfterLoanMaturity_shouldFail() {
|
||||
try {
|
||||
LOAN_TRANSACTION_HELPER_403.createInterestPauseByLoanId("2024-12-01", "2025-12-05", "yyyy-MM-dd", "en", loanId);
|
||||
} catch (Exception e) {
|
||||
String responseBody = e.getMessage();
|
||||
Assertions.assertNotNull(responseBody, "Response body should not be null");
|
||||
Assertions.assertTrue(responseBody.contains("interest.pause.end.date.after.loan.maturity.date"),
|
||||
"Response should contain the validation error message for end date after loan maturity date");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrieveInterestPausesByLoanId_noPauses_shouldReturnEmpty() {
|
||||
String response = LOAN_TRANSACTIONAL_HELPER.retrieveInterestPauseByLoanId(nonExistLoanId);
|
||||
|
||||
Assertions.assertNotNull(response, "Response should not be null");
|
||||
Assertions.assertFalse(response.contains("id"));
|
||||
Assertions.assertFalse(response.contains("startDate"));
|
||||
Assertions.assertFalse(response.contains("endDate"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrieveInterestPausesByLoanId_shouldReturnData() {
|
||||
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01", "2023-02-05", "yyyy-MM-dd", "en", loanId);
|
||||
|
||||
String response = LOAN_TRANSACTIONAL_HELPER.retrieveInterestPauseByLoanId(loanId);
|
||||
|
||||
Assertions.assertNotNull(response, "Response should not be null");
|
||||
Assertions.assertTrue(response.contains("2023-01-01"));
|
||||
Assertions.assertTrue(response.contains("2023-02-05"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInterestPauseByExternalLoanId_validRequest_shouldSucceed() {
|
||||
PostLoansLoanIdTransactionsResponse response = LOAN_TRANSACTIONAL_HELPER.createInterestPauseByExternalId("2023-01-01", "2023-02-05",
|
||||
"yyyy-MM-dd", "en", externalId);
|
||||
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertNotNull(response.getResourceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInterestPauseByExternalLoanId_endDateBeforeStartDate_shouldFail() {
|
||||
try {
|
||||
LOAN_TRANSACTION_HELPER_403.createInterestPauseByExternalId("2024-12-05", "2024-12-01", "yyyy-MM-dd", "en", externalId);
|
||||
} catch (Exception e) {
|
||||
String responseBody = e.getMessage();
|
||||
Assertions.assertNotNull(responseBody, "Response body should not be null");
|
||||
Assertions.assertTrue(responseBody.contains("interest.pause.end.date.before.start.date"),
|
||||
"Response should contain the validation error message for end date before start date");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInterestPauseByExternalLoanId_startDateBeforeLoanStart_shouldFail() {
|
||||
try {
|
||||
LOAN_TRANSACTION_HELPER_403.createInterestPauseByExternalId("2022-12-01", "2024-12-05", "yyyy-MM-dd", "en", externalId);
|
||||
} catch (Exception e) {
|
||||
String responseBody = e.getMessage();
|
||||
Assertions.assertNotNull(responseBody, "Response body should not be null");
|
||||
Assertions.assertTrue(responseBody.contains("interest.pause.start.date.before.loan.start.date"),
|
||||
"Response should contain the validation error message for start date before loan start date");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInterestPauseByExternalLoanId_endDateAfterLoanMaturity_shouldFail() {
|
||||
try {
|
||||
LOAN_TRANSACTION_HELPER_403.createInterestPauseByExternalId("2024-12-01", "2025-12-05", "yyyy-MM-dd", "en", externalId);
|
||||
} catch (Exception e) {
|
||||
String responseBody = e.getMessage();
|
||||
Assertions.assertNotNull(responseBody, "Response body should not be null");
|
||||
Assertions.assertTrue(responseBody.contains("interest.pause.end.date.after.loan.maturity.date"),
|
||||
"Response should contain the validation error message for end date after loan maturity date");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrieveInterestPausesByExternalLoanId_noPauses_shouldReturnEmpty() {
|
||||
String response = LOAN_TRANSACTIONAL_HELPER.retrieveInterestPauseByExternalId(nonExistExternalId);
|
||||
|
||||
Assertions.assertNotNull(response, "Response should not be null");
|
||||
Assertions.assertFalse(response.contains("id"));
|
||||
Assertions.assertFalse(response.contains("startDate"));
|
||||
Assertions.assertFalse(response.contains("endDate"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrieveInterestPausesByExternalLoanId_shouldReturnData() {
|
||||
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByExternalId("2023-01-01", "2023-02-05", "yyyy-MM-dd", "en", externalId);
|
||||
|
||||
String response = LOAN_TRANSACTIONAL_HELPER.retrieveInterestPauseByExternalId(externalId);
|
||||
|
||||
Assertions.assertNotNull(response, "Response should not be null");
|
||||
Assertions.assertTrue(response.contains("2023-01-01"));
|
||||
Assertions.assertTrue(response.contains("2023-02-05"));
|
||||
}
|
||||
|
||||
private static Integer createLoanProduct(final String principal, final String repaymentAfterEvery, final String numberOfRepayments,
|
||||
boolean downPaymentEnabled, String downPaymentPercentage, boolean autoPayForDownPayment, LoanScheduleType loanScheduleType,
|
||||
LoanScheduleProcessingType loanScheduleProcessingType, final Account... accounts) {
|
||||
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation();
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withMinPrincipal(principal).withPrincipal(principal)
|
||||
.withRepaymentTypeAsDays().withRepaymentAfterEvery(repaymentAfterEvery).withNumberOfRepayments(numberOfRepayments)
|
||||
.withEnableDownPayment(downPaymentEnabled, downPaymentPercentage, autoPayForDownPayment).withinterestRatePerPeriod("0")
|
||||
.withInterestRateFrequencyTypeAsMonths()
|
||||
.withRepaymentStrategy(AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)
|
||||
.withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRulePeriodicAccrual(accounts)
|
||||
.addAdvancedPaymentAllocation(defaultAllocation).withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
|
||||
.withInterestTypeAsDecliningBalance().withMultiDisburse().withDisallowExpectedDisbursements(true)
|
||||
.withLoanScheduleType(loanScheduleType).withLoanScheduleProcessingType(loanScheduleProcessingType).withDaysInMonth("30")
|
||||
.withDaysInYear("365").withMoratorium("0", "0").build(null);
|
||||
return LOAN_TRANSACTIONAL_HELPER.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private static AdvancedPaymentData createDefaultPaymentAllocation() {
|
||||
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
|
||||
advancedPaymentData.setTransactionType("DEFAULT");
|
||||
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
|
||||
|
||||
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
|
||||
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
|
||||
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
|
||||
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
|
||||
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
|
||||
|
||||
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
|
||||
return advancedPaymentData;
|
||||
}
|
||||
|
||||
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
|
||||
AtomicInteger integer = new AtomicInteger(1);
|
||||
return Arrays.stream(paymentAllocationTypes).map(pat -> {
|
||||
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
|
||||
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
|
||||
paymentAllocationOrder.setOrder(integer.getAndIncrement());
|
||||
return paymentAllocationOrder;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
|
||||
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
|
||||
final String expectedDisbursementDate, final String submittedOnDate, final String externalId) {
|
||||
return applyForLoanApplication(clientId, loanProductId, principal, loanTermFrequency, repaymentAfterEvery, numberOfRepayments,
|
||||
interestRate, expectedDisbursementDate, submittedOnDate, LoanScheduleProcessingType.HORIZONTAL, externalId);
|
||||
}
|
||||
|
||||
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
|
||||
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
|
||||
final String expectedDisbursementDate, final String submittedOnDate, LoanScheduleProcessingType loanScheduleProcessingType,
|
||||
final String externalId) {
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
return applyForLoanApplication(clientId, loanProductId, principal, loanTermFrequency, repaymentAfterEvery, numberOfRepayments,
|
||||
interestRate, expectedDisbursementDate, submittedOnDate,
|
||||
AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY, loanScheduleProcessingType.name(),
|
||||
externalId);
|
||||
}
|
||||
|
||||
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
|
||||
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
|
||||
final String expectedDisbursementDate, final String submittedOnDate, String transactionProcessorCode,
|
||||
String loanScheduleProcessingType, final String externalId) {
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
return LOAN_TRANSACTIONAL_HELPER.applyLoan(new PostLoansRequest().clientId(clientId).productId(loanProductId.longValue())
|
||||
.expectedDisbursementDate(expectedDisbursementDate).dateFormat(DATETIME_PATTERN)
|
||||
.transactionProcessingStrategyCode(transactionProcessorCode).locale("en").submittedOnDate(submittedOnDate)
|
||||
.amortizationType(1).interestRatePerPeriod(interestRate).interestCalculationPeriodType(1).interestType(0)
|
||||
.repaymentFrequencyType(0).repaymentEvery(repaymentAfterEvery).repaymentFrequencyType(0)
|
||||
.numberOfRepayments(numberOfRepayments).loanTermFrequency(loanTermFrequency).loanTermFrequencyType(0).principal(principal)
|
||||
.loanType("individual").loanScheduleProcessingType(loanScheduleProcessingType).externalId(externalId)
|
||||
.maxOutstandingLoanBalance(BigDecimal.valueOf(35000)));
|
||||
}
|
||||
}
|
||||
+56
@@ -116,6 +116,7 @@ public class LoanTransactionHelper extends IntegrationTest {
|
||||
private static final String WRITE_OFF_LOAN_COMMAND = "writeoff";
|
||||
private static final String WAIVE_INTEREST_COMMAND = "waiveinterest";
|
||||
private static final String MAKE_REPAYMENT_COMMAND = "repayment";
|
||||
private static final String INTEREST_PAUSE_COMMAND = "interestpause";
|
||||
private static final String UNDO = "undo";
|
||||
private static final String LOANCHARGE_REFUND_REPAYMENT_COMMAND = "chargeRefund";
|
||||
private static final String CREDIT_BALANCE_REFUND_COMMAND = "creditBalanceRefund";
|
||||
@@ -607,6 +608,34 @@ public class LoanTransactionHelper extends IntegrationTest {
|
||||
return ok(newFineract(user, pass).loanTransactions.executeLoanTransaction(loanId, request, "repayment"));
|
||||
}
|
||||
|
||||
public PostLoansLoanIdTransactionsResponse createInterestPauseByLoanId(final String startDate, final String endDate,
|
||||
final String dateFormat, final String locale, final Long loanID) {
|
||||
log.info("Creating interest pause for Loan {} from {} to {} with dateFormat {} and locale {}", loanID, startDate, endDate,
|
||||
dateFormat, locale);
|
||||
String body = getInterestPauseBodyAsJSON(startDate, endDate, dateFormat, locale);
|
||||
return postLoanTransaction(createInterestPause(INTEREST_PAUSE_COMMAND, loanID), body);
|
||||
}
|
||||
|
||||
public PostLoansLoanIdTransactionsResponse createInterestPauseByExternalId(final String startDate, final String endDate,
|
||||
final String dateFormat, final String locale, final String externalId) {
|
||||
log.info("Creating interest pause for Loan {} from {} to {} with dateFormat {} and locale {}", externalId, startDate, endDate,
|
||||
dateFormat, locale);
|
||||
String body = getInterestPauseBodyAsJSON(startDate, endDate, dateFormat, locale);
|
||||
return postLoanTransaction(createInterestPause(INTEREST_PAUSE_COMMAND, externalId), body);
|
||||
}
|
||||
|
||||
public String retrieveInterestPauseByLoanId(final Long loanID) {
|
||||
log.info("Retrieving interest pauses for Loan ID {}", loanID);
|
||||
String url = retrieveInterestPause(loanID);
|
||||
return Utils.performServerGet(requestSpec, responseSpec, url);
|
||||
}
|
||||
|
||||
public String retrieveInterestPauseByExternalId(final String externalId) {
|
||||
log.info("Retrieving interest pauses for External ID {}", externalId);
|
||||
String url = retrieveInterestPause(externalId);
|
||||
return Utils.performServerGet(requestSpec, responseSpec, url);
|
||||
}
|
||||
|
||||
public PostLoansLoanIdTransactionsResponse makeInterestPaymentWaiver(final Long loanId,
|
||||
final PostLoansLoanIdTransactionsRequest request) {
|
||||
return ok(fineract().loanTransactions.executeLoanTransaction(loanId, request, "interestPaymentWaiver"));
|
||||
@@ -1218,6 +1247,16 @@ public class LoanTransactionHelper extends IntegrationTest {
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
|
||||
private String getInterestPauseBodyAsJSON(final String startDate, final String endDate, final String dateFormat, final String locale) {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("startDate", startDate);
|
||||
map.put("endDate", endDate);
|
||||
map.put("dateFormat", dateFormat);
|
||||
map.put("locale", locale);
|
||||
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
|
||||
private String getAdjustTransactionJsonBody(String date, String amount) {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("transactionDate", date);
|
||||
@@ -1411,6 +1450,23 @@ public class LoanTransactionHelper extends IntegrationTest {
|
||||
return "/fineract-provider/api/v1/loans/" + loanID + "/transactions?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
|
||||
}
|
||||
|
||||
private String createInterestPause(final String command, final Long loanID) {
|
||||
return "/fineract-provider/api/v1/loans/" + loanID + "/interest-pauses?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
|
||||
}
|
||||
|
||||
private String createInterestPause(final String command, final String externalId) {
|
||||
return "/fineract-provider/api/v1/loans/external-id/" + externalId + "/interest-pauses?command=" + command + "&"
|
||||
+ Utils.TENANT_IDENTIFIER;
|
||||
}
|
||||
|
||||
private String retrieveInterestPause(final Long loanID) {
|
||||
return "/fineract-provider/api/v1/loans/" + loanID + "/interest-pauses?" + Utils.TENANT_IDENTIFIER;
|
||||
}
|
||||
|
||||
private String retrieveInterestPause(final String externalId) {
|
||||
return "/fineract-provider/api/v1/loans/external-id/" + externalId + "/interest-pauses?" + Utils.TENANT_IDENTIFIER;
|
||||
}
|
||||
|
||||
private String createInteroperationLoanTransactionURL(final String accountNo) {
|
||||
return "/fineract-provider/api/v1/interoperation/transactions/" + accountNo + "/loanrepayment";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user