This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 8663c054a FINERACT-2152: API update and delete interest pause
8663c054a is described below
commit 8663c054a076ca52e49b8df3987f8da46c929e51
Author: Andrii Kulminskyi <[email protected]>
AuthorDate: Mon Dec 23 19:26:33 2024 +0200
FINERACT-2152: API update and delete interest pause
---
.../fineract/commands/domain/CommandSource.java | 3 +-
.../fineract/commands/domain/CommandWrapper.java | 12 +-
.../commands/service/CommandWrapperBuilder.java | 18 ++
.../SynchronousCommandProcessingService.java | 12 +-
.../api/LoanInterestPauseApiResource.java | 42 ++-
...java => CreateInterestPauseCommandHandler.java} | 12 +-
...java => DeleteInterestPauseCommandHandler.java} | 27 +-
...java => UpdateInterestPauseCommandHandler.java} | 23 +-
.../service/InterestPauseWritePlatformService.java | 50 ++++
.../InterestPauseWritePlatformServiceImpl.java | 91 +++++-
.../loanaccount/domain/LoanTermVariations.java | 48 +++
.../domain/LoanTermVariationsRepository.java | 9 +
.../starter/LoanAccountConfiguration.java | 4 +-
.../db/changelog/tenant/changelog-tenant.xml | 1 +
..._additional_audit_fields_to_term_variations.xml | 51 ++++
.../integrationtests/LoanInterestPauseApiTest.java | 328 ++++++++++++++-------
.../common/loans/LoanProductTestBuilder.java | 1 +
.../common/loans/LoanTransactionHelper.java | 38 ++-
18 files changed, 599 insertions(+), 171 deletions(-)
diff --git
a/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandSource.java
b/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandSource.java
index ad28f696c..e468e4a17 100644
---
a/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandSource.java
+++
b/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandSource.java
@@ -159,8 +159,7 @@ public class CommandSource extends
AbstractPersistableCustom<Long> {
.transactionId(command.getTransactionId()) //
.creditBureauId(command.getCreditBureauId()) //
.organisationCreditBureauId(command.getOrganisationCreditBureauId()) //
- .loanExternalId(command.getLoanExternalId()) //
- .build(); //
+ .loanExternalId(command.getLoanExternalId()).build(); //
}
public String getPermissionCode() {
diff --git
a/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandWrapper.java
b/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandWrapper.java
index 5b51f40de..fd73b43b9 100644
---
a/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandWrapper.java
+++
b/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandWrapper.java
@@ -251,8 +251,16 @@ public class CommandWrapper {
return this.entityName.equalsIgnoreCase("INTEREST_PAUSE");
}
- public boolean isInterestPauseExternalIdResource() {
- return this.entityName.equalsIgnoreCase("INTEREST_PAUSE") &&
this.href.contains("/external-id/");
+ public boolean isInterestPauseCreateResource() {
+ return this.entityName.equalsIgnoreCase("INTEREST_PAUSE") &&
"CREATE".equalsIgnoreCase(this.actionName);
+ }
+
+ public boolean isInterestPauseUpdateResource() {
+ return this.entityName.equalsIgnoreCase("INTEREST_PAUSE") &&
"UPDATE".equalsIgnoreCase(this.actionName);
+ }
+
+ public boolean isInterestPauseDeleteResource() {
+ return this.entityName.equalsIgnoreCase("INTEREST_PAUSE") &&
"DELETE".equalsIgnoreCase(this.actionName);
}
public Long commandId() {
diff --git
a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index 497862cbf..740fbbbc6 100644
---
a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++
b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -3732,4 +3732,22 @@ public class CommandWrapperBuilder {
this.href = "/v1/loans/external-id/" + loanExternalId +
"/interest-pauses";
return this;
}
+
+ public CommandWrapperBuilder deleteInterestPause(final long loanId, final
long variationId) {
+ this.actionName = "DELETE";
+ this.entityName = "INTEREST_PAUSE";
+ this.loanId = loanId;
+ this.entityId = variationId;
+ this.href = "/v1/loans/" + loanId + "/interest-pauses/" + variationId;
+ return this;
+ }
+
+ public CommandWrapperBuilder updateInterestPause(final long loanId, final
long variationId) {
+ this.actionName = "UPDATE";
+ this.entityName = "INTEREST_PAUSE";
+ this.loanId = loanId;
+ this.entityId = variationId;
+ this.href = "/v1/loans/" + loanId + "/interest-pauses/" + variationId;
+ return this;
+ }
}
diff --git
a/fineract-core/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java
b/fineract-core/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java
index 9b525d5cd..f021cb23c 100644
---
a/fineract-core/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java
+++
b/fineract-core/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java
@@ -249,8 +249,16 @@ public class SynchronousCommandProcessingService
implements CommandProcessingSer
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
- } else if (wrapper.isInterestPauseResource() ||
wrapper.isInterestPauseExternalIdResource()) {
- handler =
applicationContext.getBean("interestPauseCommandHandler",
NewCommandSourceHandler.class);
+ } else if (wrapper.isInterestPauseResource()) {
+ if (wrapper.isInterestPauseCreateResource()) {
+ handler =
applicationContext.getBean("createInterestPauseCommandHandler",
NewCommandSourceHandler.class);
+ } else if (wrapper.isInterestPauseUpdateResource()) {
+ handler =
applicationContext.getBean("updateInterestPauseCommandHandler",
NewCommandSourceHandler.class);
+ } else if (wrapper.isInterestPauseDeleteResource()) {
+ handler =
applicationContext.getBean("deleteInterestPauseCommandHandler",
NewCommandSourceHandler.class);
+ } else {
+ throw new UnsupportedCommandException(wrapper.commandName());
+ }
} else {
handler = commandHandlerProvider.getHandler(wrapper.entityName(),
wrapper.actionName());
}
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/api/LoanInterestPauseApiResource.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/api/LoanInterestPauseApiResource.java
index 1b08ad6d8..253727547 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/api/LoanInterestPauseApiResource.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/api/LoanInterestPauseApiResource.java
@@ -25,12 +25,15 @@ 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.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper;
@@ -50,6 +53,7 @@ import org.springframework.stereotype.Component;
public class LoanInterestPauseApiResource {
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
+ private static final String MODIFY_RESOURCE_NAME_FOR_PERMISSIONS = "UPDATE
LOAN";
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService;
@@ -64,7 +68,7 @@ public class LoanInterestPauseApiResource {
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);
+
this.context.authenticatedUser().validateHasReadPermission(MODIFY_RESOURCE_NAME_FOR_PERMISSIONS);
final CommandWrapper commandRequest = new
CommandWrapperBuilder().createInterestPause(loanId).withJson(request.toJson()).build();
@@ -81,7 +85,7 @@ public class LoanInterestPauseApiResource {
@PathParam("loanExternalId") @Parameter(description =
"loanExternalId") final String loanExternalId,
@RequestBody(required = true) final InterestPauseRequestDto
request) {
-
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+
this.context.authenticatedUser().validateHasReadPermission(MODIFY_RESOURCE_NAME_FOR_PERMISSIONS);
final CommandWrapper commandRequest = new
CommandWrapperBuilder().createInterestPauseByExternalId(loanExternalId)
.withJson(request.toJson()).build();
@@ -114,4 +118,38 @@ public class LoanInterestPauseApiResource {
return
this.interestPauseReadPlatformService.retrieveInterestPauses(loanExternalId);
}
+
+ @DELETE
+ @Path("/{loanId}/interest-pauses/{variationId}")
+ @Operation(summary = "Delete an interest pause period", description =
"Deletes a specific interest pause period by its variation ID.")
+ @ApiResponses({ @ApiResponse(responseCode = "204", description = "No
Content") })
+ public Response deleteInterestPause(@PathParam("loanId")
@Parameter(description = "loanId") final Long loanId,
+ @PathParam("variationId") @Parameter(description = "variationId")
final Long variationId) {
+
+
this.context.authenticatedUser().validateHasReadPermission(MODIFY_RESOURCE_NAME_FOR_PERMISSIONS);
+
+ final CommandWrapper commandRequest = new
CommandWrapperBuilder().deleteInterestPause(loanId, variationId).build();
+
+
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return Response.noContent().build();
+ }
+
+ @PUT
+ @Path("/{loanId}/interest-pauses/{variationId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Update an interest pause period", description =
"Updates a specific interest pause period by its variation ID.")
+ @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
+ public CommandProcessingResult updateInterestPause(@PathParam("loanId")
@Parameter(description = "loanId") final Long loanId,
+ @PathParam("variationId") @Parameter(description = "variationId")
final Long variationId,
+ @RequestBody(required = true) final InterestPauseRequestDto
request) {
+
+
this.context.authenticatedUser().validateHasReadPermission(MODIFY_RESOURCE_NAME_FOR_PERMISSIONS);
+
+ final CommandWrapper commandRequest = new
CommandWrapperBuilder().updateInterestPause(loanId, variationId)
+ .withJson(request.toJson()).build();
+
+ return
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ }
}
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/CreateInterestPauseCommandHandler.java
similarity index 85%
copy from
fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
copy to
fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/CreateInterestPauseCommandHandler.java
index 5747625dd..6246f278c 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/CreateInterestPauseCommandHandler.java
@@ -27,16 +27,14 @@ import
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import
org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePlatformService;
import org.springframework.stereotype.Component;
-@Component("interestPauseCommandHandler")
+@Component("createInterestPauseCommandHandler")
@RequiredArgsConstructor
-public class InterestPauseCommandHandler implements NewCommandSourceHandler {
+public class CreateInterestPauseCommandHandler 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");
@@ -44,15 +42,13 @@ public class InterestPauseCommandHandler implements
NewCommandSourceHandler {
if (command.getLoanId() != null) {
final Long loanId = command.getLoanId();
- result = interestPauseService.createInterestPause(loanId,
startDate, endDate, dateFormat, locale);
+ return 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);
+ return 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;
}
}
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/DeleteInterestPauseCommandHandler.java
similarity index 52%
copy from
fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
copy to
fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/DeleteInterestPauseCommandHandler.java
index 5747625dd..8068c8c5a 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/DeleteInterestPauseCommandHandler.java
@@ -22,37 +22,20 @@ 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")
+@Component("deleteInterestPauseCommandHandler")
@RequiredArgsConstructor
-public class InterestPauseCommandHandler implements NewCommandSourceHandler {
+public class DeleteInterestPauseCommandHandler implements
NewCommandSourceHandler {
private final InterestPauseWritePlatformService interestPauseService;
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
- CommandProcessingResult result;
+ final Long loanId = command.getLoanId();
+ final Long termVariationId = command.getResourceId();
- 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;
+ return interestPauseService.deleteInterestPause(loanId,
termVariationId);
}
}
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/UpdateInterestPauseCommandHandler.java
similarity index 63%
rename from
fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
rename to
fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/UpdateInterestPauseCommandHandler.java
index 5747625dd..04a9b2f03 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/InterestPauseCommandHandler.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/handler/UpdateInterestPauseCommandHandler.java
@@ -22,37 +22,24 @@ 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")
+@Component("updateInterestPauseCommandHandler")
@RequiredArgsConstructor
-public class InterestPauseCommandHandler implements NewCommandSourceHandler {
+public class UpdateInterestPauseCommandHandler implements
NewCommandSourceHandler {
private final InterestPauseWritePlatformService interestPauseService;
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
- CommandProcessingResult result;
-
+ final Long loanId = command.getLoanId();
+ final Long termVariationId = command.getResourceId();
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;
+ return interestPauseService.updateInterestPause(loanId,
termVariationId, startDate, endDate, dateFormat, locale);
}
}
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformService.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformService.java
index e3b8c397a..491a45171 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformService.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformService.java
@@ -23,6 +23,21 @@ import
org.apache.fineract.infrastructure.core.domain.ExternalId;
public interface InterestPauseWritePlatformService {
+ /**
+ * Create a new interest pause period for a loan identified by its
external ID.
+ *
+ * @param loanExternalId
+ * the external 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)
+ * @param dateFormat
+ * the format of the provided dates
+ * @param locale
+ * the locale used for date parsing
+ * @return the ID of the created loan term variation representing the
interest pause
+ */
CommandProcessingResult createInterestPause(ExternalId loanExternalId,
String startDate, String endDate, String dateFormat,
String locale);
@@ -35,7 +50,42 @@ public interface InterestPauseWritePlatformService {
* the start date of the interest pause period (inclusive)
* @param endDate
* the end date of the interest pause period (inclusive)
+ * @param dateFormat
+ * the format of the provided dates
+ * @param locale
+ * the locale used for date parsing
* @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);
+
+ /**
+ * Delete an existing interest pause period for a loan.
+ *
+ * @param loanId
+ * the ID of the loan
+ * @param variationId
+ * the ID of the loan term variation representing the interest
pause
+ * @return the result of the delete operation
+ */
+ CommandProcessingResult deleteInterestPause(Long loanId, Long variationId);
+
+ /**
+ * Update an existing interest pause period for a loan identified by its
internal ID.
+ *
+ * @param loanId
+ * the ID of the loan
+ * @param variationId
+ * the ID of the loan term variation representing the interest
pause to be updated
+ * @param startDateString
+ * the new start date of the interest pause period (inclusive)
as a string
+ * @param endDateString
+ * the new end date of the interest pause period (inclusive) as
a string
+ * @param dateFormat
+ * the format of the provided dates (e.g., "yyyy-MM-dd")
+ * @param locale
+ * the locale used for parsing the provided dates
+ * @return the updated loan term variation ID along with the updated fields
+ */
+ CommandProcessingResult updateInterestPause(Long loanId, Long variationId,
String startDateString, String endDateString,
+ String dateFormat, String locale);
}
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformServiceImpl.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformServiceImpl.java
index 272ba3f9f..095ec4024 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformServiceImpl.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformServiceImpl.java
@@ -18,12 +18,19 @@
*/
package org.apache.fineract.portfolio.interestpauses.service;
+import static
org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.ACTIVE;
+import static
org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType.INTEREST_PAUSE;
+import static
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
+
import java.time.LocalDate;
+import java.time.LocalDateTime;
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.Map;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import lombok.AllArgsConstructor;
@@ -34,11 +41,13 @@ 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.infrastructure.core.service.DateUtils;
+import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
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.apache.fineract.useradministration.domain.AppUser;
import org.springframework.transaction.annotation.Transactional;
@AllArgsConstructor
@@ -47,6 +56,7 @@ public class InterestPauseWritePlatformServiceImpl implements
InterestPauseWrite
private final LoanTermVariationsRepository loanTermVariationsRepository;
private final LoanRepositoryWrapper loanRepositoryWrapper;
+ private final PlatformSecurityContext context;
@Override
public CommandProcessingResult createInterestPause(ExternalId
loanExternalId, String startDateString, String endDateString,
@@ -68,14 +78,59 @@ public class InterestPauseWritePlatformServiceImpl
implements InterestPauseWrite
locale);
}
+ @Override
+ public CommandProcessingResult deleteInterestPause(Long loanId, Long
variationId) {
+ LoanTermVariations variation = loanTermVariationsRepository
+ .findByIdAndLoanIdAndTermType(variationId, loanId,
INTEREST_PAUSE.getValue())
+ .orElseThrow(() -> new
GeneralPlatformDomainRuleException("error.msg.variation.not.found",
+ "Variation not found for the given loan ID"));
+
+ loanTermVariationsRepository.delete(variation);
+
+ return new
CommandProcessingResultBuilder().withEntityId(variationId).build();
+ }
+
+ @Override
+ public CommandProcessingResult updateInterestPause(Long loanId, Long
variationId, String startDateString, String endDateString,
+ String dateFormat, String locale) {
+ Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
+
+ LocalDate startDate = parseDate(startDateString, dateFormat, locale);
+ LocalDate endDate = parseDate(endDateString, dateFormat, locale);
+
+ validateInterestPauseDates(loan, startDate, endDate, dateFormat,
locale);
+
+ LoanTermVariations variation = loanTermVariationsRepository
+ .findByIdAndLoanIdAndTermType(variationId, loanId,
INTEREST_PAUSE.getValue())
+ .orElseThrow(() -> new
GeneralPlatformDomainRuleException("error.msg.variation.not.found",
+ "Variation not found for the given loan ID"));
+
+ validateVariations(loan, variation);
+
+ variation.setTermApplicableFrom(startDate);
+ variation.setDateValue(endDate);
+
+ AppUser currentUser = context.authenticatedUser();
+ variation.setUpdatedBy(currentUser != null ? currentUser.getId() :
null);
+
variation.setUpdatedOnDate(LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant()));
+
+ LoanTermVariations updatedVariation =
loanTermVariationsRepository.save(variation);
+
+ return new
CommandProcessingResultBuilder().withEntityId(updatedVariation.getId())
+ .with(Map.of("startDate", startDate.toString(), "endDate",
endDate.toString())).build();
+ }
+
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 variation = new
LoanTermVariations(INTEREST_PAUSE.getValue(), startDate, null, endDate, false,
loan);
+
+ AppUser currentUser = context.authenticatedUser();
+ variation.setCreatedBy(currentUser != null ? currentUser.getId() :
null);
+
variation.setCreatedOnDate(LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant()));
LoanTermVariations savedVariation =
loanTermVariationsRepository.saveAndFlush(variation);
@@ -109,6 +164,36 @@ public class InterestPauseWritePlatformServiceImpl
implements InterestPauseWrite
.format("Interest pause end date (%s) must not be before
the interest pause start date (%s).", endDate, startDate),
endDate, startDate);
}
+
+ if (!Objects.equals(loan.getLoanStatus(), ACTIVE.getValue())) {
+ throw new GeneralPlatformDomainRuleException("loan.must.be.active",
+ "Operations on interest pauses are restricted to active
loans.");
+ }
+
+ if
(!PROGRESSIVE.equals(loan.getLoanRepaymentScheduleDetail().getLoanScheduleType()))
{
+ throw new
GeneralPlatformDomainRuleException("loan.must.be.progressive",
+ "Interest pause is only supported for progressive loans.");
+ }
+
+ if
(!loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
+ throw new
GeneralPlatformDomainRuleException("loan.must.have.recalculate.interest.enabled",
+ "Interest pause is only supported for loans with
recalculate interest enabled.");
+ }
+ }
+
+ private void validateVariations(Loan loan, LoanTermVariations variation) {
+ if (variation == null) {
+ throw new
GeneralPlatformDomainRuleException("interest.pause.not.found",
+ "The specified interest pause does not exist for the given
loan.");
+ }
+
+ List<LoanTermVariations> existingVariations =
loan.getLoanTermVariations();
+ for (LoanTermVariations existingVariation : existingVariations) {
+ if (!existingVariation.equals(variation) &&
variation.getTermApplicableFrom().isBefore(existingVariation.getDateValue())
+ &&
variation.getDateValue().isAfter(existingVariation.getTermApplicableFrom())) {
+ throw new
GeneralPlatformDomainRuleException("interest.pause.overlapping", "Overlapping
interest pauses are not allowed.");
+ }
+ }
}
private LocalDate parseDate(String date, String dateFormat, String locale)
{
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java
index d5f41828d..891dde0e2 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java
@@ -27,6 +27,7 @@ import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
@@ -61,6 +62,18 @@ public class LoanTermVariations extends
AbstractPersistableCustom<Long> {
@Column(name = "is_active", nullable = false)
private Boolean isActive;
+ @Column(name = "created_by")
+ private Long createdBy;
+
+ @Column(name = "created_on_date")
+ private LocalDateTime createdOnDate;
+
+ @Column(name = "updated_by")
+ private Long updatedBy;
+
+ @Column(name = "updated_on_date")
+ private LocalDateTime updatedOnDate;
+
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private LoanTermVariations parent;
@@ -139,6 +152,10 @@ public class LoanTermVariations extends
AbstractPersistableCustom<Long> {
return this.dateValue;
}
+ public void setDateValue(LocalDate dateValue) {
+ this.dateValue = dateValue;
+ }
+
public void setTermApplicableFrom(LocalDate termApplicableFrom) {
this.termApplicableFrom = termApplicableFrom;
}
@@ -167,4 +184,35 @@ public class LoanTermVariations extends
AbstractPersistableCustom<Long> {
this.isActive = false;
}
+ public Long getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(Long createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public LocalDateTime getCreatedOnDate() {
+ return createdOnDate;
+ }
+
+ public void setCreatedOnDate(LocalDateTime createdOnDate) {
+ this.createdOnDate = createdOnDate;
+ }
+
+ public Long getUpdatedBy() {
+ return updatedBy;
+ }
+
+ public void setUpdatedBy(Long updatedBy) {
+ this.updatedBy = updatedBy;
+ }
+
+ public LocalDateTime getUpdatedOnDate() {
+ return updatedOnDate;
+ }
+
+ public void setUpdatedOnDate(LocalDateTime updatedOnDate) {
+ this.updatedOnDate = updatedOnDate;
+ }
}
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanTermVariationsRepository.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanTermVariationsRepository.java
index 0bf7209ff..57993fe05 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanTermVariationsRepository.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanTermVariationsRepository.java
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain;
import java.util.List;
+import java.util.Optional;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
@@ -60,4 +61,12 @@ public interface LoanTermVariationsRepository
""")
List<LoanTermVariationsData>
findLoanTermVariationsByExternalLoanIdAndTermType(@Param("loanExternalId")
ExternalId loanExternalId,
@Param("termType") int termType);
+
+ @Query("""
+ select ltv
+ from LoanTermVariations ltv
+ where ltv.id = :variationId and ltv.loan.id = :loanId and
ltv.termType = :termType
+ """)
+ Optional<LoanTermVariations>
findByIdAndLoanIdAndTermType(@Param("variationId") long variationId,
@Param("loanId") long loanId,
+ @Param("termType") int termType);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
index b7b9a955d..a1a1fddbb 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
@@ -471,7 +471,7 @@ public class LoanAccountConfiguration {
@Bean
@ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
public InterestPauseWritePlatformService
interestPauseWritePlatformService(LoanTermVariationsRepository
loanTermVariationsRepository,
- LoanRepositoryWrapper loanRepositoryWrapper) {
- return new
InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository,
loanRepositoryWrapper);
+ LoanRepositoryWrapper loanRepositoryWrapper,
PlatformSecurityContext context) {
+ return new
InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository,
loanRepositoryWrapper, context);
}
}
diff --git
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index 673a92f1f..b058f2a2a 100644
---
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -180,4 +180,5 @@
<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" />
+ <include
file="parts/0162_add_additional_audit_fields_to_term_variations.xml"
relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0162_add_additional_audit_fields_to_term_variations.xml
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0162_add_additional_audit_fields_to_term_variations.xml
new file mode 100644
index 000000000..fbb1ef2bf
--- /dev/null
+++
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0162_add_additional_audit_fields_to_term_variations.xml
@@ -0,0 +1,51 @@
+<?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_loan_term_variations">
+ <column name="created_by" type="BIGINT">
+ <constraints nullable="true"/>
+ </column>
+ </addColumn>
+
+ <addColumn tableName="m_loan_term_variations">
+ <column name="created_on_date" type="TIMESTAMP">
+ <constraints nullable="true"/>
+ </column>
+ </addColumn>
+
+ <addColumn tableName="m_loan_term_variations">
+ <column name="updated_by" type="BIGINT">
+ <constraints nullable="true"/>
+ </column>
+ </addColumn>
+
+ <addColumn tableName="m_loan_term_variations">
+ <column name="updated_on_date" type="TIMESTAMP">
+ <constraints nullable="true"/>
+ </column>
+ </addColumn>
+ </changeSet>
+</databaseChangeLog>
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
index 347b72f23..d4f5d0dc0 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
@@ -24,38 +24,30 @@ 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.PostLoansLoanIdRequest;
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.LoanApplicationTestBuilder;
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.BeforeEach;
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 })
+@ExtendWith(LoanTestLifecycleExtension.class)
public class LoanInterestPauseApiTest extends BaseLoanIntegrationTest {
private static final Logger LOG =
LoggerFactory.getLogger(LoanInterestPauseApiTest.class);
@@ -63,52 +55,56 @@ public class LoanInterestPauseApiTest extends
BaseLoanIntegrationTest {
private static RequestSpecification REQUEST_SPEC;
private static ResponseSpecification RESPONSE_SPEC;
private static ResponseSpecification RESPONSE_SPEC_403;
+ private static ResponseSpecification RESPONSE_SPEC_204;
private static LoanTransactionHelper LOAN_TRANSACTIONAL_HELPER;
+ private static LoanTransactionHelper LOAN_TRANSACTIONAL_HELPER_204;
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 AccountHelper ACCOUNT_HELPER;
+ private static final Integer nonExistLoanId = 99999;
private static String externalId;
- private static String nonExistExternalId =
"7c4fb86f-a778-4d02-b7a8-ec3ec98941fa";
-
- @BeforeAll
- public static void setupTests() {
+ private static final String nonExistExternalId =
"7c4fb86f-a778-4d02-b7a8-ec3ec98941fa";
+ private Integer clientId;
+ private Integer loanProductId;
+ private Integer loanId;
+ private final String loanPrincipalAmount = "10000.00";
+ private final String numberOfRepayments = "12";
+ private final String interestRatePerPeriod = "18";
+ private final String dateString = "01 January 2023";
+
+ @BeforeEach
+ public void initialize() {
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();
+ RESPONSE_SPEC_204 = new
ResponseSpecBuilder().expectStatusCode(204).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();
+ LOAN_TRANSACTIONAL_HELPER_204 = new
LoanTransactionHelper(REQUEST_SPEC, RESPONSE_SPEC_204);
+ ACCOUNT_HELPER = new AccountHelper(REQUEST_SPEC, RESPONSE_SPEC);
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();
+ createRequiredEntities();
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");
}
+ /**
+ * Creates the client, loan product, and loan entities
+ **/
+ private void createRequiredEntities() {
+ this.createClientEntity();
+ this.createLoanProductEntity();
+ this.createLoanEntity();
+ }
+
@Test
public void testCreateInterestPauseByLoanId_validRequest_shouldSucceed() {
- PostLoansLoanIdTransactionsResponse response =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
"2023-02-05",
+ PostLoansLoanIdTransactionsResponse response =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
"2023-01-12",
"yyyy-MM-dd", "en", loanId);
Assertions.assertNotNull(response);
@@ -118,7 +114,7 @@ public class LoanInterestPauseApiTest extends
BaseLoanIntegrationTest {
@Test
public void
testCreateInterestPauseByLoanId_endDateBeforeStartDate_shouldFail() {
try {
-
LOAN_TRANSACTION_HELPER_403.createInterestPauseByLoanId("2024-12-05",
"2024-12-01", "yyyy-MM-dd", "en", loanId);
+
LOAN_TRANSACTION_HELPER_403.createInterestPauseByLoanId("2024-12-05",
"2023-01-12", "yyyy-MM-dd", "en", loanId);
} catch (Exception e) {
String responseBody = e.getMessage();
Assertions.assertNotNull(responseBody, "Response body should not
be null");
@@ -130,7 +126,7 @@ public class LoanInterestPauseApiTest extends
BaseLoanIntegrationTest {
@Test
public void
testCreateInterestPauseByLoanId_startDateBeforeLoanStart_shouldFail() {
try {
-
LOAN_TRANSACTION_HELPER_403.createInterestPauseByLoanId("2022-12-01",
"2024-12-05", "yyyy-MM-dd", "en", loanId);
+
LOAN_TRANSACTION_HELPER_403.createInterestPauseByLoanId("2022-12-01",
"2023-01-12", "yyyy-MM-dd", "en", loanId);
} catch (Exception e) {
String responseBody = e.getMessage();
Assertions.assertNotNull(responseBody, "Response body should not
be null");
@@ -163,18 +159,18 @@ public class LoanInterestPauseApiTest extends
BaseLoanIntegrationTest {
@Test
public void testRetrieveInterestPausesByLoanId_shouldReturnData() {
- LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
"2023-02-05", "yyyy-MM-dd", "en", loanId);
+ LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
"2023-01-12", "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"));
+ Assertions.assertTrue(response.contains("2023-01-12"));
}
@Test
public void
testCreateInterestPauseByExternalLoanId_validRequest_shouldSucceed() {
- PostLoansLoanIdTransactionsResponse response =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByExternalId("2023-01-01",
"2023-02-05",
+ PostLoansLoanIdTransactionsResponse response =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByExternalId("2023-01-01",
"2023-01-12",
"yyyy-MM-dd", "en", externalId);
Assertions.assertNotNull(response);
@@ -184,7 +180,7 @@ public class LoanInterestPauseApiTest extends
BaseLoanIntegrationTest {
@Test
public void
testCreateInterestPauseByExternalLoanId_endDateBeforeStartDate_shouldFail() {
try {
-
LOAN_TRANSACTION_HELPER_403.createInterestPauseByExternalId("2024-12-05",
"2024-12-01", "yyyy-MM-dd", "en", externalId);
+
LOAN_TRANSACTION_HELPER_403.createInterestPauseByExternalId("2023-01-01",
"2022-01-12", "yyyy-MM-dd", "en", externalId);
} catch (Exception e) {
String responseBody = e.getMessage();
Assertions.assertNotNull(responseBody, "Response body should not
be null");
@@ -229,87 +225,207 @@ public class LoanInterestPauseApiTest extends
BaseLoanIntegrationTest {
@Test
public void testRetrieveInterestPausesByExternalLoanId_shouldReturnData() {
-
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByExternalId("2023-01-01",
"2023-02-05", "yyyy-MM-dd", "en", externalId);
+
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByExternalId("2023-01-01",
"2023-01-12", "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"));
+ Assertions.assertTrue(response.contains("2023-01-12"));
+ }
+
+ @Test
+ public void testUpdateInterestPauseByLoanId_validRequest_shouldSucceed() {
+ PostLoansLoanIdTransactionsResponse createResponse =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
+ "2023-01-12", "yyyy-MM-dd", "en", loanId);
+
+ Assertions.assertNotNull(createResponse);
+ Assertions.assertNotNull(createResponse.getResourceId());
+
+ Long variationId = createResponse.getResourceId();
+
+ PostLoansLoanIdTransactionsResponse updateResponse =
LOAN_TRANSACTIONAL_HELPER.updateInterestPauseByLoanId(variationId,
+ "2023-01-01", "2023-01-12", "yyyy-MM-dd", "en", loanId);
+
+ Assertions.assertNotNull(updateResponse);
+ Assertions.assertNotNull(updateResponse.getResourceId());
+ Assertions.assertEquals(variationId, updateResponse.getResourceId());
+ }
+
+ @Test
+ public void
testUpdateInterestPauseByLoanId_endDateBeforeStartDate_shouldFail() {
+ PostLoansLoanIdTransactionsResponse createResponse =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
+ "2023-01-12", "yyyy-MM-dd", "en", loanId);
+
+ Assertions.assertNotNull(createResponse);
+ Assertions.assertNotNull(createResponse.getResourceId());
+
+ Long variationId = createResponse.getResourceId();
+
+ try {
+
LOAN_TRANSACTION_HELPER_403.updateInterestPauseByLoanId(variationId,
"2023-03-01", "2023-01-12", "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
testUpdateInterestPauseByLoanId_startDateBeforeLoanStart_shouldFail() {
+ PostLoansLoanIdTransactionsResponse createResponse =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
+ "2023-01-12", "yyyy-MM-dd", "en", loanId);
+
+ Assertions.assertNotNull(createResponse);
+ Assertions.assertNotNull(createResponse.getResourceId());
+
+ Long variationId = createResponse.getResourceId();
+
+ try {
+
LOAN_TRANSACTION_HELPER_403.updateInterestPauseByLoanId(variationId,
"2022-12-01", "2023-01-12", "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");
+ }
}
- 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);
+ @Test
+ public void testDeleteInterestPauseByLoanId_validRequest_shouldSucceed() {
+ PostLoansLoanIdTransactionsResponse createResponse =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
+ "2023-01-12", "yyyy-MM-dd", "en", loanId);
+
+ Assertions.assertNotNull(createResponse, "Create response should not
be null");
+ Assertions.assertNotNull(createResponse.getResourceId(), "Resource ID
should not be null");
+
+ Long variationId = createResponse.getResourceId();
+
+ try {
+
LOAN_TRANSACTIONAL_HELPER_204.deleteInterestPauseByLoanId(variationId, loanId);
+ } catch (Exception e) {
+ Assertions.fail("Delete operation failed: " + e.getMessage());
+ }
+
+ String response =
LOAN_TRANSACTIONAL_HELPER.retrieveInterestPauseByLoanId(loanId);
+ Assertions.assertFalse(response.contains(String.valueOf(variationId)),
"Response should not contain the deleted variation ID");
}
- private static AdvancedPaymentData createDefaultPaymentAllocation() {
- AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
- advancedPaymentData.setTransactionType("DEFAULT");
-
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
+ @Test
+ public void
testDeleteInterestPauseByLoanId_nonExistentVariation_shouldFail() {
+ try {
+ LOAN_TRANSACTION_HELPER_403.deleteInterestPauseByLoanId(99999L,
loanId);
+ } catch (Exception e) {
+ String responseBody = e.getMessage();
+ Assertions.assertNotNull(responseBody, "Response body should not
be null");
+
Assertions.assertTrue(responseBody.contains("error.msg.variation.not.found"),
+ "Response should contain the validation error message for
variation not found");
+ }
+ }
- 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);
+ @Test
+ public void testDeleteInterestPauseByLoanId_invalidLoanId_shouldFail() {
+ PostLoansLoanIdTransactionsResponse createResponse =
LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
+ "2023-01-12", "yyyy-MM-dd", "en", loanId);
- advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
- return advancedPaymentData;
+ Assertions.assertNotNull(createResponse);
+ Assertions.assertNotNull(createResponse.getResourceId());
+
+ Long variationId = createResponse.getResourceId();
+
+ try {
+
LOAN_TRANSACTION_HELPER_403.deleteInterestPauseByLoanId(variationId,
nonExistLoanId);
+ } catch (Exception e) {
+ String responseBody = e.getMessage();
+ Assertions.assertNotNull(responseBody, "Response body should not
be null");
+
Assertions.assertTrue(responseBody.contains("error.msg.variation.not.found"),
+ "Response should contain the validation error message for
variation not found");
+ }
+ }
+
+ /**
+ * create a new client
+ **/
+ private void createClientEntity() {
+ this.clientId = ClientHelper.createClient(REQUEST_SPEC, RESPONSE_SPEC);
+
+ ClientHelper.verifyClientCreatedOnServer(REQUEST_SPEC, RESPONSE_SPEC,
clientId);
}
- 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());
+ /**
+ * create a new loan product
+ **/
+ private void createLoanProductEntity() {
+ LOG.info("---------------------------------CREATING LOAN
PRODUCT------------------------------------------");
+
+ final String interestRecalculationCompoundingMethod =
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE;
+ final String rescheduleStrategyMethod =
LoanProductTestBuilder.RECALCULATION_STRATEGY_ADJUST_LAST_UNPAID_PERIOD;
+ final String preCloseInterestCalculationStrategy =
LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE;
+
+ final Account assetAccount = ACCOUNT_HELPER.createAssetAccount();
+ final Account incomeAccount = ACCOUNT_HELPER.createIncomeAccount();
+ final Account expenseAccount = ACCOUNT_HELPER.createExpenseAccount();
+ final Account overpaymentAccount =
ACCOUNT_HELPER.createLiabilityAccount();
+
+ String futureInstallmentAllocationRule = "NEXT_INSTALLMENT";
+ AdvancedPaymentData defaultAllocation =
createDefaultPaymentAllocation(futureInstallmentAllocationRule);
+ String loanProductJSON = new
LoanProductTestBuilder().withPrincipal(loanPrincipalAmount).withNumberOfRepayments(numberOfRepayments)
+
.withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod(interestRatePerPeriod)
+
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
+ .withAccountingRulePeriodicAccrual(new Account[] {
assetAccount, incomeAccount, expenseAccount, overpaymentAccount })
+
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).addAdvancedPaymentAllocation(defaultAllocation)
+
.withLoanScheduleType(LoanScheduleType.PROGRESSIVE).withLoanScheduleProcessingType(LoanScheduleProcessingType.HORIZONTAL)
+
.withMultiDisburse().withDisallowExpectedDisbursements(true).withInterestRecalculationDetails(
+ interestRecalculationCompoundingMethod,
rescheduleStrategyMethod, preCloseInterestCalculationStrategy)
+ .build();
+
+ loanProductId =
LOAN_TRANSACTIONAL_HELPER.getLoanProductId(loanProductJSON);
+ LOG.info("Successfully created loan product (ID:{}) ", loanProductId);
}
- 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);
+ /**
+ * submit a new loan application, approve and disburse the loan
+ **/
+ private void createLoanEntity() {
+ LOG.info("---------------------------------NEW LOAN
APPLICATION------------------------------------------");
+
+ String loanApplicationJSON = new
LoanApplicationTestBuilder().withPrincipal(loanPrincipalAmount)
+
.withLoanTermFrequency(numberOfRepayments).withLoanTermFrequencyAsDays().withNumberOfRepayments(numberOfRepayments)
+
.withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsDays().withInterestRatePerPeriod(interestRatePerPeriod)
+
.withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()
+
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate(dateString)
+
.withSubmittedOnDate(dateString).withLoanType("individual").withExternalId(externalId)
+
.withRepaymentStrategy("advanced-payment-allocation-strategy").build(clientId.toString(),
loanProductId.toString(), null);
+
+ loanId = LOAN_TRANSACTIONAL_HELPER.getLoanId(loanApplicationJSON);
+
+ LOG.info("Sucessfully created loan (ID: {} )", loanId);
+
+ approveLoanApplication();
+ disburseLoan();
}
- 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);
+ /**
+ * approve the loan application
+ **/
+ private void approveLoanApplication() {
+
+ if (loanId != null) {
+ LOAN_TRANSACTIONAL_HELPER.approveLoan(dateString, loanId);
+ LOG.info("Successfully approved loan (ID: {} )", loanId);
+ }
}
- 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)));
+ /**
+ * disburse the newly created loan
+ **/
+ private void disburseLoan() {
+
+ if (loanId != null) {
+ LOAN_TRANSACTIONAL_HELPER.disburseLoan(externalId, new
PostLoansLoanIdRequest().actualDisbursementDate(dateString)
+ .transactionAmount(new
BigDecimal(loanPrincipalAmount)).locale("en").dateFormat("dd MMMM yyyy"));
+ LOG.info("Successfully disbursed loan (ID: {} )", loanId);
+ }
}
}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
index 40116b9ae..507d789fe 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
@@ -66,6 +66,7 @@ public class LoanProductTestBuilder {
public static final String
RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS = "1";
public static final String
RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS = "2";
public static final String RECALCULATION_STRATEGY_REDUCE_EMI_AMOUN = "3";
+ public static final String
RECALCULATION_STRATEGY_ADJUST_LAST_UNPAID_PERIOD = "4";
public static final String RECALCULATION_COMPOUNDING_METHOD_NONE = "0";
public static final String RECALCULATION_COMPOUNDING_METHOD_INTEREST = "1";
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 8785ec626..52e222d65 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -609,7 +609,7 @@ public class LoanTransactionHelper extends IntegrationTest {
}
public PostLoansLoanIdTransactionsResponse
createInterestPauseByLoanId(final String startDate, final String endDate,
- final String dateFormat, final String locale, final Long loanID) {
+ final String dateFormat, final String locale, final Integer
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);
@@ -624,7 +624,20 @@ public class LoanTransactionHelper extends IntegrationTest
{
return postLoanTransaction(createInterestPause(INTEREST_PAUSE_COMMAND,
externalId), body);
}
- public String retrieveInterestPauseByLoanId(final Long loanID) {
+ public PostLoansLoanIdTransactionsResponse
updateInterestPauseByLoanId(final Long termVariationId, final String startDate,
+ final String endDate, final String dateFormat, final String
locale, final Integer loanID) {
+ log.info("Updating interest pause for Loan {} with Term Variation ID
{}: startDate={} endDate={} dateFormat={} locale={}", loanID,
+ termVariationId, startDate, endDate, dateFormat, locale);
+ String body = getInterestPauseBodyAsJSON(startDate, endDate,
dateFormat, locale);
+ return putLoanTransaction(updateInterestPause(termVariationId,
loanID), body);
+ }
+
+ public void deleteInterestPauseByLoanId(final Long termVariationId, final
Integer loanID) {
+ log.info("Deleting interest pause for Loan ID {} with Term Variation
ID {}", loanID, termVariationId);
+ deleteLoanTransaction(deleteInterestPause(termVariationId, loanID));
+ }
+
+ public String retrieveInterestPauseByLoanId(final Integer loanID) {
log.info("Retrieving interest pauses for Loan ID {}", loanID);
String url = retrieveInterestPause(loanID);
return Utils.performServerGet(requestSpec, responseSpec, url);
@@ -1450,7 +1463,7 @@ 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) {
+ private String createInterestPause(final String command, final Integer
loanID) {
return "/fineract-provider/api/v1/loans/" + loanID +
"/interest-pauses?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
}
@@ -1459,7 +1472,7 @@ public class LoanTransactionHelper extends
IntegrationTest {
+ Utils.TENANT_IDENTIFIER;
}
- private String retrieveInterestPause(final Long loanID) {
+ private String retrieveInterestPause(final Integer loanID) {
return "/fineract-provider/api/v1/loans/" + loanID +
"/interest-pauses?" + Utils.TENANT_IDENTIFIER;
}
@@ -1467,6 +1480,14 @@ public class LoanTransactionHelper extends
IntegrationTest {
return "/fineract-provider/api/v1/loans/external-id/" + externalId +
"/interest-pauses?" + Utils.TENANT_IDENTIFIER;
}
+ private String updateInterestPause(final Long termVariationId, final
Integer loanID) {
+ return "/fineract-provider/api/v1/loans/" + loanID +
"/interest-pauses/" + termVariationId + "?" + Utils.TENANT_IDENTIFIER;
+ }
+
+ private String deleteInterestPause(final Long termVariationId, final
Integer loanID) {
+ return "/fineract-provider/api/v1/loans/" + loanID +
"/interest-pauses/" + termVariationId + "?" + Utils.TENANT_IDENTIFIER;
+ }
+
private String createInteroperationLoanTransactionURL(final String
accountNo) {
return "/fineract-provider/api/v1/interoperation/transactions/" +
accountNo + "/loanrepayment";
}
@@ -1513,6 +1534,15 @@ public class LoanTransactionHelper extends
IntegrationTest {
return GSON.fromJson(response,
PostLoansLoanIdTransactionsResponse.class);
}
+ private PostLoansLoanIdTransactionsResponse putLoanTransaction(final
String putURLForLoanTransaction, final String jsonToBeSent) {
+ final String response = Utils.performServerPut(this.requestSpec,
this.responseSpec, putURLForLoanTransaction, jsonToBeSent);
+ return GSON.fromJson(response,
PostLoansLoanIdTransactionsResponse.class);
+ }
+
+ private void deleteLoanTransaction(final String
deleteURLForLoanTransaction) {
+ Utils.performServerDelete(this.requestSpec, this.responseSpec,
deleteURLForLoanTransaction, null);
+ }
+
private Object performLoanTransaction(final String
postURLForLoanTransaction, final String jsonToBeSent,
ResponseSpecification responseValidationError) {