FINERACT-2152: API Create and retrieve interest pause
This commit is contained in:
committed by
Adam Saghy
parent
e94e6c421a
commit
c2ec015e64
+117
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.interestpauses.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseRequestDto;
|
||||
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseResponseDto;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/loans")
|
||||
@Component
|
||||
@Tag(name = "Loan Interest Pause", description = "APIs for managing interest pause periods on loans.")
|
||||
@RequiredArgsConstructor
|
||||
public class LoanInterestPauseApiResource {
|
||||
|
||||
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final InterestPauseReadPlatformService interestPauseReadPlatformService;
|
||||
|
||||
@POST
|
||||
@Path("/{loanId}/interest-pauses")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Create a new interest pause period for a loan", description = "Allows users to define a period during which no interest will be accrued for a specific loan.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public CommandProcessingResult createInterestPause(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
|
||||
@RequestBody(required = true) final InterestPauseRequestDto request) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createInterestPause(loanId).withJson(request.toJson()).build();
|
||||
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/external-id/{loanExternalId}/interest-pauses")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Create a new interest pause for a loan using external ID", description = "Allows users to define a period during which no interest will be accrued for a specific loan using the external loan ID.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public CommandProcessingResult createInterestPauseByExternalId(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId") final String loanExternalId,
|
||||
@RequestBody(required = true) final InterestPauseRequestDto request) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createInterestPauseByExternalId(loanExternalId)
|
||||
.withJson(request.toJson()).build();
|
||||
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{loanId}/interest-pauses")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve all interest pause periods for a loan", description = "Fetches a list of all active interest pause periods for a specific loan.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public List<InterestPauseResponseDto> retrieveInterestPauses(
|
||||
@PathParam("loanId") @Parameter(description = "loanId") final Long loanId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
return this.interestPauseReadPlatformService.retrieveInterestPauses(loanId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/external-id/{loanExternalId}/interest-pauses")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve all interest pause periods for a loan using external ID", description = "Fetches a list of all active interest pause periods for a specific loan using the external loan ID.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public List<InterestPauseResponseDto> retrieveInterestPausesByExternalId(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId") final String loanExternalId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
return this.interestPauseReadPlatformService.retrieveInterestPauses(loanExternalId);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.interestpauses.data;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "Request DTO for creating an interest pause")
|
||||
public class InterestPauseRequestDto {
|
||||
|
||||
@Schema(example = "2024-01-01", description = "Start date of the interest pause period")
|
||||
private String startDate;
|
||||
|
||||
@Schema(example = "2024-01-11", description = "End date of the interest pause period")
|
||||
private String endDate;
|
||||
|
||||
@Schema(example = "yyyy-MM-dd", description = "Format of the dates provided")
|
||||
private String dateFormat;
|
||||
|
||||
@Schema(example = "en", description = "Locale to interpret the date format")
|
||||
private String locale;
|
||||
|
||||
public String toJson() {
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException("Error serializing request to JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.interestpauses.data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "Response DTO for Interest Pause periods")
|
||||
public class InterestPauseResponseDto {
|
||||
|
||||
@Schema(example = "1", description = "ID of the loan term variation")
|
||||
private Long id;
|
||||
|
||||
@Schema(example = "2024-01-01", description = "Start date of the interest pause period")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(example = "2024-01-11", description = "End date of the interest pause period")
|
||||
private LocalDate endDate;
|
||||
|
||||
@Schema(example = "yyyy-MM-dd", description = "Date format used to interpret start and end dates")
|
||||
private String dateFormat;
|
||||
|
||||
@Schema(example = "en", description = "Locale used for date formatting")
|
||||
private String locale;
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.interestpauses.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("interestPauseCommandHandler")
|
||||
@RequiredArgsConstructor
|
||||
public class InterestPauseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InterestPauseWritePlatformService interestPauseService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
CommandProcessingResult result;
|
||||
|
||||
final String startDate = command.stringValueOfParameterNamed("startDate");
|
||||
final String endDate = command.stringValueOfParameterNamed("endDate");
|
||||
final String dateFormat = command.stringValueOfParameterNamed("dateFormat");
|
||||
final String locale = command.stringValueOfParameterNamed("locale");
|
||||
|
||||
if (command.getLoanId() != null) {
|
||||
final Long loanId = command.getLoanId();
|
||||
result = interestPauseService.createInterestPause(loanId, startDate, endDate, dateFormat, locale);
|
||||
} else if (command.getLoanExternalId() != null) {
|
||||
final ExternalId loanExternalId = command.getLoanExternalId();
|
||||
result = interestPauseService.createInterestPause(loanExternalId, startDate, endDate, dateFormat, locale);
|
||||
} else {
|
||||
throw new PlatformApiDataValidationException("validation.msg.missing.loan.id.or.external.id",
|
||||
"Either loanId or loanExternalId must be provided.", "loanId");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.interestpauses.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseResponseDto;
|
||||
|
||||
public interface InterestPauseReadPlatformService {
|
||||
|
||||
/**
|
||||
* Retrieve all interest pause periods for a valid loan.
|
||||
*
|
||||
* @param loanId
|
||||
* @return List of InterestPauseData
|
||||
*/
|
||||
List<InterestPauseResponseDto> retrieveInterestPauses(Long loanId);
|
||||
|
||||
/**
|
||||
* Retrieve all interest pause periods for a loan using external ID.
|
||||
*
|
||||
* @param loanExternalId
|
||||
* @return List of InterestPauseData
|
||||
*/
|
||||
List<InterestPauseResponseDto> retrieveInterestPauses(String loanExternalId);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.interestpauses.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseResponseDto;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class InterestPauseReadPlatformServiceImpl implements InterestPauseReadPlatformService {
|
||||
|
||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||
|
||||
@Override
|
||||
public List<InterestPauseResponseDto> retrieveInterestPauses(Long loanId) {
|
||||
List<LoanTermVariationsData> variations = this.loanTermVariationsRepository.findLoanTermVariationsByLoanIdAndTermType(loanId,
|
||||
LoanTermVariationType.INTEREST_PAUSE.getValue());
|
||||
|
||||
return mapToInterestPauseResponse(variations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InterestPauseResponseDto> retrieveInterestPauses(String loanExternalId) {
|
||||
List<LoanTermVariationsData> variations = this.loanTermVariationsRepository.findLoanTermVariationsByExternalLoanIdAndTermType(
|
||||
new ExternalId(loanExternalId), LoanTermVariationType.INTEREST_PAUSE.getValue());
|
||||
|
||||
return mapToInterestPauseResponse(variations);
|
||||
}
|
||||
|
||||
private List<InterestPauseResponseDto> mapToInterestPauseResponse(List<LoanTermVariationsData> variations) {
|
||||
return variations.stream()
|
||||
.map(variation -> new InterestPauseResponseDto(variation.getId(), variation.getTermVariationApplicableFrom(),
|
||||
variation.getDateValue(), DateUtils.DEFAULT_DATE_FORMAT, Locale.getDefault().toString()))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.interestpauses.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
|
||||
public interface InterestPauseWritePlatformService {
|
||||
|
||||
CommandProcessingResult createInterestPause(ExternalId loanExternalId, String startDate, String endDate, String dateFormat,
|
||||
String locale);
|
||||
|
||||
/**
|
||||
* Create a new interest pause period for a loan identified by its internal ID.
|
||||
*
|
||||
* @param loanId
|
||||
* the ID of the loan
|
||||
* @param startDate
|
||||
* the start date of the interest pause period (inclusive)
|
||||
* @param endDate
|
||||
* the end date of the interest pause period (inclusive)
|
||||
* @return the ID of the created loan term variation representing the interest pause
|
||||
*/
|
||||
CommandProcessingResult createInterestPause(Long loanId, String startDate, String endDate, String dateFormat, String locale);
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.interestpauses.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Transactional
|
||||
public class InterestPauseWritePlatformServiceImpl implements InterestPauseWritePlatformService {
|
||||
|
||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createInterestPause(ExternalId loanExternalId, String startDateString, String endDateString,
|
||||
String dateFormat, String locale) {
|
||||
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
|
||||
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
|
||||
|
||||
return processInterestPause(() -> loanRepositoryWrapper.findOneWithNotFoundDetection(loanExternalId), startDate, endDate,
|
||||
dateFormat, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createInterestPause(Long loanId, String startDateString, String endDateString, String dateFormat,
|
||||
String locale) {
|
||||
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
|
||||
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
|
||||
|
||||
return processInterestPause(() -> loanRepositoryWrapper.findOneWithNotFoundDetection(loanId), startDate, endDate, dateFormat,
|
||||
locale);
|
||||
}
|
||||
|
||||
private CommandProcessingResult processInterestPause(Supplier<Loan> loanSupplier, LocalDate startDate, LocalDate endDate,
|
||||
String dateFormat, String locale) {
|
||||
final Loan loan = loanSupplier.get();
|
||||
|
||||
validateInterestPauseDates(loan, startDate, endDate, dateFormat, locale);
|
||||
|
||||
LoanTermVariations variation = new LoanTermVariations(LoanTermVariationType.INTEREST_PAUSE.getValue(), startDate, null, endDate,
|
||||
false, loan);
|
||||
|
||||
LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
|
||||
|
||||
return new CommandProcessingResultBuilder().withEntityId(savedVariation.getId()).build();
|
||||
}
|
||||
|
||||
private void validateInterestPauseDates(Loan loan, LocalDate startDate, LocalDate endDate, String dateFormat, String locale) {
|
||||
|
||||
validateOrThrow(baseDataValidator -> {
|
||||
baseDataValidator.reset().parameter("startDate").value(startDate).notBlank();
|
||||
baseDataValidator.reset().parameter("endDate").value(endDate).notBlank();
|
||||
baseDataValidator.reset().parameter("dateFormat").value(dateFormat).notBlank();
|
||||
baseDataValidator.reset().parameter("locale").value(locale).notBlank();
|
||||
});
|
||||
|
||||
if (startDate.isBefore(loan.getSubmittedOnDate())) {
|
||||
throw new GeneralPlatformDomainRuleException("interest.pause.start.date.before.loan.start.date",
|
||||
String.format("Interest pause start date (%s) cannot be earlier than loan start date (%s).", startDate,
|
||||
loan.getSubmittedOnDate()),
|
||||
startDate, loan.getSubmittedOnDate());
|
||||
}
|
||||
|
||||
if (endDate.isAfter(loan.getMaturityDate())) {
|
||||
throw new GeneralPlatformDomainRuleException("interest.pause.end.date.after.loan.maturity.date", String
|
||||
.format("Interest pause end date (%s) cannot be later than loan maturity date (%s).", endDate, loan.getMaturityDate()),
|
||||
endDate, loan.getMaturityDate());
|
||||
}
|
||||
|
||||
if (endDate.isBefore(startDate)) {
|
||||
throw new GeneralPlatformDomainRuleException("interest.pause.end.date.before.start.date", String
|
||||
.format("Interest pause end date (%s) must not be before the interest pause start date (%s).", endDate, startDate),
|
||||
endDate, startDate);
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDate parseDate(String date, String dateFormat, String locale) {
|
||||
try {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat, Locale.forLanguageTag(locale));
|
||||
return LocalDate.parse(date, formatter);
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.invalid.date.format",
|
||||
String.format("Invalid date format. Provided: %s, Expected format: %s, Locale: %s", date, dateFormat, locale),
|
||||
e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateOrThrow(Consumer<DataValidatorBuilder> baseDataValidator) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors).resource("InterestPause");
|
||||
|
||||
baseDataValidator.accept(dataValidatorBuilder);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -113,6 +113,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
+ "and (:futureCharges = true or ls.fromDate < :tillDate or (ls.installmentNumber = (select min(lsi.installmentNumber) from LoanRepaymentScheduleInstallment lsi where lsi.loan.id = l.id and lsi.isDownPayment = false) and ls.fromDate = :tillDate))))";
|
||||
String FIND_LOANS_FOR_ADD_ACCRUAL = LOANS_FOR_ACCRUAL + "and (:futureCharges = true or ls.dueDate <= :tillDate)))";
|
||||
|
||||
String FIND_LOAN_BY_EXTERNAL_ID = "SELECT loan FROM Loan loan WHERE loan.externalId = :externalId";
|
||||
|
||||
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
|
||||
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") LocalDate disbursementDate, @Param("groupId") Long groupId,
|
||||
@Param("loanType") Integer loanType);
|
||||
@@ -247,4 +249,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
@Query(FIND_LOANS_FOR_ADD_ACCRUAL)
|
||||
List<Loan> findLoansForAddAccrual(@Param("accountingType") Integer accountingType, @Param("tillDate") LocalDate tillDate,
|
||||
@Param("futureCharges") boolean futureCharges);
|
||||
|
||||
@Query(FIND_LOAN_BY_EXTERNAL_ID)
|
||||
Optional<Loan> findByExternalId(@Param("externalId") ExternalId externalId);
|
||||
}
|
||||
|
||||
+14
@@ -49,6 +49,20 @@ public class LoanRepositoryWrapper {
|
||||
return this.findOneWithNotFoundDetection(id, false);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Loan findOneWithNotFoundDetection(final ExternalId externalId) {
|
||||
return this.findOneWithNotFoundDetection(externalId, false);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Loan findOneWithNotFoundDetection(final ExternalId externalId, boolean loadLazyCollections) {
|
||||
final Loan loan = this.repository.findByExternalId(externalId).orElseThrow(() -> new LoanNotFoundException(externalId));
|
||||
if (loadLazyCollections) {
|
||||
loan.initializeLazyCollections();
|
||||
}
|
||||
return loan;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Loan findOneWithNotFoundDetection(final Long id, boolean loadLazyCollections) {
|
||||
final Loan loan = this.repository.findById(id).orElseThrow(() -> new LoanNotFoundException(id));
|
||||
|
||||
+5
-1
@@ -30,7 +30,8 @@ public enum LoanTermVariationType {
|
||||
GRACE_ON_INTEREST(7, "loanTermType.graceOnInterest"), //
|
||||
GRACE_ON_PRINCIPAL(8, "loanTermType.graceOnPrincipal"), //
|
||||
EXTEND_REPAYMENT_PERIOD(9, "loanTermType.extendRepaymentPeriod"), //
|
||||
INTEREST_RATE_FROM_INSTALLMENT(10, "loanTermType.interestRateFromInstallment"); //
|
||||
INTEREST_RATE_FROM_INSTALLMENT(10, "loanTermType.interestRateFromInstallment"), //
|
||||
INTEREST_PAUSE(11, "loanTermType.interestPause"); //
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -82,6 +83,9 @@ public enum LoanTermVariationType {
|
||||
case 10:
|
||||
enumeration = LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT;
|
||||
break;
|
||||
case 11:
|
||||
enumeration = LoanTermVariationType.INTEREST_PAUSE;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
|
||||
+11
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
@@ -49,4 +50,14 @@ public interface LoanTermVariationsRepository
|
||||
""")
|
||||
List<LoanTermVariationsData> findLoanTermVariationsByLoanIdAndTermType(@Param("loanId") long loanId, @Param("termType") int termType);
|
||||
|
||||
@Query("""
|
||||
select new org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData(
|
||||
ltv.id, ltv.termType, ltv.termApplicableFrom, ltv.decimalValue, ltv.dateValue, ltv.isSpecificToInstallment
|
||||
)
|
||||
from LoanTermVariations ltv
|
||||
where ltv.loan.externalId = :loanExternalId and ltv.termType = :termType
|
||||
order by ltv.termApplicableFrom
|
||||
""")
|
||||
List<LoanTermVariationsData> findLoanTermVariationsByExternalLoanIdAndTermType(@Param("loanExternalId") ExternalId loanExternalId,
|
||||
@Param("termType") int termType);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user