FINERACT-2152: API update and delete interest pause
This commit is contained in:
committed by
Adam Saghy
parent
d2afd1aac7
commit
8663c054a0
@@ -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() {
|
||||
|
||||
+10
-2
@@ -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() {
|
||||
|
||||
+18
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -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());
|
||||
}
|
||||
|
||||
+40
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
+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.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.portfolio.interestpauses.service.InterestPauseWritePlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("deleteInterestPauseCommandHandler")
|
||||
@RequiredArgsConstructor
|
||||
public class DeleteInterestPauseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InterestPauseWritePlatformService interestPauseService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
final Long loanId = command.getLoanId();
|
||||
final Long termVariationId = command.getResourceId();
|
||||
|
||||
return interestPauseService.deleteInterestPause(loanId, termVariationId);
|
||||
}
|
||||
}
|
||||
+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.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.portfolio.interestpauses.service.InterestPauseWritePlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("updateInterestPauseCommandHandler")
|
||||
@RequiredArgsConstructor
|
||||
public class UpdateInterestPauseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InterestPauseWritePlatformService interestPauseService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
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");
|
||||
|
||||
return interestPauseService.updateInterestPause(loanId, termVariationId, startDate, endDate, dateFormat, locale);
|
||||
}
|
||||
}
|
||||
+50
@@ -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);
|
||||
}
|
||||
|
||||
+88
-3
@@ -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) {
|
||||
|
||||
+48
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
+51
@@ -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>
|
||||
+221
-105
@@ -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";
|
||||
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";
|
||||
|
||||
@BeforeAll
|
||||
public static void setupTests() {
|
||||
@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"));
|
||||
}
|
||||
|
||||
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 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());
|
||||
}
|
||||
|
||||
private static AdvancedPaymentData createDefaultPaymentAllocation() {
|
||||
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
|
||||
advancedPaymentData.setTransactionType("DEFAULT");
|
||||
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
|
||||
@Test
|
||||
public void testUpdateInterestPauseByLoanId_endDateBeforeStartDate_shouldFail() {
|
||||
PostLoansLoanIdTransactionsResponse createResponse = LOAN_TRANSACTIONAL_HELPER.createInterestPauseByLoanId("2023-01-01",
|
||||
"2023-01-12", "yyyy-MM-dd", "en", loanId);
|
||||
|
||||
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);
|
||||
Assertions.assertNotNull(createResponse);
|
||||
Assertions.assertNotNull(createResponse.getResourceId());
|
||||
|
||||
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
|
||||
return advancedPaymentData;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
@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 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);
|
||||
@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 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);
|
||||
@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");
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
@Test
|
||||
public void testDeleteInterestPauseByLoanId_invalidLoanId_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.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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* approve the loan application
|
||||
**/
|
||||
private void approveLoanApplication() {
|
||||
|
||||
if (loanId != null) {
|
||||
LOAN_TRANSACTIONAL_HELPER.approveLoan(dateString, loanId);
|
||||
LOG.info("Successfully approved loan (ID: {} )", loanId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -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";
|
||||
|
||||
+34
-4
@@ -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) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user