FINERACT-1932: Fineract modularization - fineract-rates

This commit is contained in:
Jose Alberto Hernandez
2024-05-17 15:12:02 +02:00
committed by Adam Saghy
parent 8b2c9da804
commit abc7cec2ec
30 changed files with 293 additions and 0 deletions
@@ -1,133 +0,0 @@
/**
* 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.floatingrates.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
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.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateData;
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
import org.springframework.stereotype.Component;
@Path("/v1/floatingrates")
@Component
@Tag(name = "Floating Rates", description = "It lets you create, list, retrieve and upload the floating rates")
@RequiredArgsConstructor
public class FloatingRatesApiResource {
private static final String RESOURCE_NAME = "FLOATINGRATE";
private static final Set<String> LIST_FLOATING_RATES_PARAMETERS = new HashSet<>(
Arrays.asList("id", "name", "isBaseLendingRate", "isActive", "createdby", "createdOn", "modifiedBy", "modifiedOn"));
private static final Set<String> INDIVIDUAL_FLOATING_RATES_PARAMETERS = new HashSet<>(Arrays.asList("id", "name", "isBaseLendingRate",
"isActive", "createdBy", "createdOn", "modifiedBy", "modifiedOn", "ratePeriods"));
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<FloatingRateData> toApiJsonSerializer;
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a new Floating Rate", description = "Creates a new Floating Rate\n" + "Mandatory Fields: name\n"
+ "Optional Fields: isBaseLendingRate, isActive, ratePeriods")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = FloatingRatesApiResourceSwagger.PostFloatingRatesRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FloatingRatesApiResourceSwagger.PostFloatingRatesResponse.class))) })
public String createFloatingRate(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createFloatingRate().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Floating Rates", description = "Lists Floating Rates")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = FloatingRatesApiResourceSwagger.GetFloatingRatesResponse.class)))) })
public String retrieveAll(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME);
final List<FloatingRateData> floatingRates = this.floatingRatesReadPlatformService.retrieveAll();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, floatingRates, FloatingRatesApiResource.LIST_FLOATING_RATES_PARAMETERS);
}
@GET
@Path("{floatingRateId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Floating Rate", description = "Retrieves Floating Rate")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FloatingRatesApiResourceSwagger.GetFloatingRatesFloatingRateIdResponse.class))) })
public String retrieveOne(@PathParam("floatingRateId") @Parameter(description = "floatingRateId") final Long floatingRateId,
@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME);
final FloatingRateData floatingRates = this.floatingRatesReadPlatformService.retrieveOne(floatingRateId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, floatingRates, FloatingRatesApiResource.INDIVIDUAL_FLOATING_RATES_PARAMETERS);
}
@PUT
@Path("{floatingRateId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update Floating Rate", description = "Updates new Floating Rate. Rate Periods in the past cannot be modified. All the future rateperiods would be replaced with the new ratePeriods data sent.")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = FloatingRatesApiResourceSwagger.PutFloatingRatesFloatingRateIdRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FloatingRatesApiResourceSwagger.PutFloatingRatesFloatingRateIdResponse.class))) })
public String updateFloatingRate(@PathParam("floatingRateId") @Parameter(description = "floatingRateId") final Long floatingRateId,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateFloatingRate(floatingRateId).withJson(apiRequestBodyAsJson)
.build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
}
@@ -1,169 +0,0 @@
/**
* 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.floatingrates.api;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Set;
/**
* Created by Chirag Gupta on 12/17/17.
*/
final class FloatingRatesApiResourceSwagger {
private FloatingRatesApiResourceSwagger() {}
@Schema(description = "PostFloatingRatesRequest")
public static final class PostFloatingRatesRequest {
private PostFloatingRatesRequest() {}
static final class PostFloatingRatesRatePeriods {
private PostFloatingRatesRatePeriods() {}
@Schema(example = "19 November 2015")
public String fromDate;
@Schema(example = "10")
public Double interestRate;
@Schema(example = "en")
public String locale;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
}
@Schema(example = "Floating Rate 1")
public String name;
@Schema(example = "true")
public Boolean isBaseLendingRate;
@Schema(example = "true")
public Boolean isActive;
public Set<PostFloatingRatesRatePeriods> ratePeriods;
}
@Schema(description = "PostFloatingRatesResponse")
public static final class PostFloatingRatesResponse {
private PostFloatingRatesResponse() {}
@Schema(example = "1")
public Long resourceId;
}
@Schema(description = "GetFloatingRatesResponse")
public static final class GetFloatingRatesResponse {
private GetFloatingRatesResponse() {}
@Schema(example = "1")
public Long id;
@Schema(example = "Floating Rate 1")
public String name;
@Schema(example = "true")
public Boolean isBaseLendingRate;
@Schema(example = "true")
public Boolean isActive;
@Schema(example = "mifos")
public String createdBy;
@Schema(example = "Nov 18, 2015")
public String createdOn;
@Schema(example = "mifos")
public String modifiedBy;
@Schema(example = "Nov 18, 2015")
public String modifiedOn;
}
@Schema(description = "GetFloatingRatesFloatingRateIdResponse")
public static final class GetFloatingRatesFloatingRateIdResponse {
private GetFloatingRatesFloatingRateIdResponse() {}
static final class GetFloatingRatesRatePeriods {
private GetFloatingRatesRatePeriods() {}
@Schema(example = "1")
public Long id;
@Schema(example = "Dec 15, 2015")
public String fromDate;
@Schema(example = "11")
public Double interestRate;
@Schema(example = "false")
public Boolean isDifferentialToBaseLendingRate;
@Schema(example = "true")
public Boolean isActive;
@Schema(example = "mifos")
public String createdBy;
@Schema(example = "Nov 18, 2015")
public String createdOn;
@Schema(example = "mifos")
public String modifiedBy;
@Schema(example = "Nov 18, 2015")
public String modifiedOn;
}
@Schema(example = "1")
public Long id;
@Schema(example = "Floating Rate 1")
public String name;
@Schema(example = "true")
public Boolean isBaseLendingRate;
@Schema(example = "true")
public Boolean isActive;
@Schema(example = "mifos")
public String createdBy;
@Schema(example = "Nov 18, 2015")
public String createdOn;
@Schema(example = "mifos")
public String modifiedBy;
@Schema(example = "Nov 18, 2015")
public String modifiedOn;
public Set<GetFloatingRatesRatePeriods> ratePeriods;
}
@Schema(description = "PutFloatingRatesFloatingRateIdRequest")
public static final class PutFloatingRatesFloatingRateIdRequest {
private PutFloatingRatesFloatingRateIdRequest() {}
@Schema(example = "Floating Rate 1")
public String name;
@Schema(example = "true")
public Boolean isBaseLendingRate;
@Schema(example = "true")
public Boolean isActive;
public Set<PostFloatingRatesRequest.PostFloatingRatesRatePeriods> ratePeriods;
}
@Schema(description = "PutFloatingRatesFloatingRateIdResponse")
public static final class PutFloatingRatesFloatingRateIdResponse {
private PutFloatingRatesFloatingRateIdResponse() {}
static final class PutFloatingRatesChanges {
private PutFloatingRatesChanges() {}
public Set<PostFloatingRatesRequest.PostFloatingRatesRatePeriods> ratePeriods;
}
@Schema(example = "1")
public Long resourceId;
public PutFloatingRatesChanges changes;
}
}
@@ -1,141 +0,0 @@
/**
* 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.floatingrates.data;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.List;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public class FloatingRateData implements Comparable<FloatingRateData>, Serializable {
private final Long id;
private final String name;
private final boolean isBaseLendingRate;
private final boolean isActive;
private final String createdBy;
private final OffsetDateTime createdOn;
private final String modifiedBy;
private final OffsetDateTime modifiedOn;
private final List<FloatingRatePeriodData> ratePeriods;
@SuppressWarnings("unused")
private final List<EnumOptionData> interestRateFrequencyTypeOptions;
public FloatingRateData(Long id, String name, boolean isBaseLendingRate, boolean isActive, String createdBy, OffsetDateTime createdOn,
String modifiedBy, OffsetDateTime modifiedOn, List<FloatingRatePeriodData> ratePeriods,
List<EnumOptionData> interestRateFrequencyTypeOptions) {
this.id = id;
this.name = name;
this.isBaseLendingRate = isBaseLendingRate;
this.isActive = isActive;
this.createdBy = createdBy;
this.createdOn = createdOn;
this.modifiedBy = modifiedBy;
this.modifiedOn = modifiedOn;
this.ratePeriods = ratePeriods;
this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions;
}
public Long getId() {
return this.id;
}
public String getName() {
return this.name;
}
public boolean isBaseLendingRate() {
return this.isBaseLendingRate;
}
public boolean isActive() {
return this.isActive;
}
public String getCreatedBy() {
return this.createdBy;
}
public OffsetDateTime getCreatedOn() {
return this.createdOn;
}
public String getModifiedBy() {
return this.modifiedBy;
}
public OffsetDateTime getModifiedOn() {
return this.modifiedOn;
}
public List<FloatingRatePeriodData> getRatePeriods() {
return this.ratePeriods;
}
@Override
public int compareTo(final FloatingRateData obj) {
if (obj == null) {
return -1;
}
return new CompareToBuilder() //
.append(this.id, obj.id) //
.append(this.name, obj.name) //
.append(this.isBaseLendingRate, obj.isBaseLendingRate) //
.append(this.isActive, obj.isActive) //
.toComparison();
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof FloatingRateData)) {
return false;
}
final FloatingRateData rhs = (FloatingRateData) obj;
return new EqualsBuilder() //
.append(this.id, rhs.id) //
.append(this.name, rhs.name) //
.append(this.isBaseLendingRate, rhs.isBaseLendingRate) //
.append(this.isActive, rhs.isActive) //
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37) //
.append(this.id) //
.append(this.name) //
.append(this.isBaseLendingRate) //
.append(this.isActive) //
.toHashCode();
}
public static FloatingRateData toTemplate(List<EnumOptionData> interestRateFrequencyTypeOptions) {
// TODO Auto-generated method stub
return new FloatingRateData(null, null, false, true, null, null, null, null, null, interestRateFrequencyTypeOptions);
}
}
@@ -1,69 +0,0 @@
/**
* 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.floatingrates.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Getter;
@Getter
public class InterestRatePeriodData {
private LocalDate fromDate;
private final BigDecimal interestRate;
private final boolean isDifferentialToBLR;
private final LocalDate blrFromDate;
private final BigDecimal blrInterestRate;
private BigDecimal loanDifferentialInterestRate;
private BigDecimal loanProductDifferentialInterestRate;
private BigDecimal effectiveInterestRate;
public InterestRatePeriodData(LocalDate fromDate, BigDecimal interestRate, boolean isDifferentialToBLR, LocalDate blrFromDate,
BigDecimal blrInterestRate) {
this.fromDate = fromDate;
this.interestRate = interestRate;
this.isDifferentialToBLR = isDifferentialToBLR;
this.blrFromDate = blrFromDate;
this.blrInterestRate = blrInterestRate;
}
public void setFromDate(LocalDate fromDate) {
this.fromDate = fromDate;
}
public boolean isDifferentialToBLR() {
return this.isDifferentialToBLR;
}
public boolean isIsDifferentialToBLR() {
return this.isDifferentialToBLR;
}
public void setLoanDifferentialInterestRate(BigDecimal loanDifferentialInterestRate) {
this.loanDifferentialInterestRate = loanDifferentialInterestRate;
}
public void setLoanProductDifferentialInterestRate(BigDecimal loanProductDifferentialInterestRate) {
this.loanProductDifferentialInterestRate = loanProductDifferentialInterestRate;
}
public void setEffectiveInterestRate(BigDecimal effectiveInterestRate) {
this.effectiveInterestRate = effectiveInterestRate;
}
}
@@ -1,36 +0,0 @@
/**
* 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.floatingrates.domain;
import java.util.Collection;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
public interface FloatingRateRepository extends JpaRepository<FloatingRate, Long>, JpaSpecificationExecutor<FloatingRate> {
@Query("select floatingRate from FloatingRate floatingRate where floatingRate.isBaseLendingRate = true and floatingRate.isActive = true")
FloatingRate retrieveBaseLendingRate();
@Query("select floatingRate from FloatingRate floatingRate " + " inner join floatingRate.floatingRatePeriods as periods"
+ " where floatingRate.isActive = true " + " and periods.isActive = true "
+ " and periods.isDifferentialToBaseLendingRate = true")
Collection<FloatingRate> retrieveFloatingRatesLinkedToBLR();
}
@@ -1,50 +0,0 @@
/**
* 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.floatingrates.domain;
import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class FloatingRateRepositoryWrapper {
private final FloatingRateRepository floatingRateRepository;
@Autowired
public FloatingRateRepositoryWrapper(final FloatingRateRepository floatingRateRepository) {
this.floatingRateRepository = floatingRateRepository;
}
public FloatingRate retrieveBaseLendingRate() {
return this.floatingRateRepository.retrieveBaseLendingRate();
}
public FloatingRate findOneWithNotFoundDetection(final Long id) {
return this.floatingRateRepository.findById(id).orElseThrow(() -> new FloatingRateNotFoundException(id));
}
public void save(FloatingRate newFloatingRate) {
this.floatingRateRepository.save(newFloatingRate);
}
public void saveAndFlush(FloatingRate floatingRateForUpdate) {
this.floatingRateRepository.saveAndFlush(floatingRateForUpdate);
}
}
@@ -1,41 +0,0 @@
/**
* 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.floatingrates.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
public class FloatingRateNotFoundException extends AbstractPlatformResourceNotFoundException {
public FloatingRateNotFoundException(final Long id) {
super("error.msg.floatingrate.id.invalid", "Floating Rate with identifier " + id + " does not exist", id);
}
public FloatingRateNotFoundException(final String globalisationMessageCode) {
super(globalisationMessageCode, "Floating Rate does not exist");
}
public FloatingRateNotFoundException(String globalisationMessageCode, EmptyResultDataAccessException e) {
super(globalisationMessageCode, "Floating Rate does not exist", e);
}
public FloatingRateNotFoundException(Long id, EmptyResultDataAccessException e) {
super("error.msg.floatingrate.id.invalid", "Floating Rate with identifier " + id + " does not exist", id, e);
}
}
@@ -1,46 +0,0 @@
/**
* 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.floatingrates.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.floatingrates.service.FloatingRateWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "FLOATINGRATE", action = "CREATE")
public class CreateFloatingRateCommandHandler implements NewCommandSourceHandler {
private final FloatingRateWritePlatformService writePlatformService;
@Autowired
public CreateFloatingRateCommandHandler(final FloatingRateWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.createFloatingRate(command);
}
}
@@ -1,46 +0,0 @@
/**
* 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.floatingrates.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.floatingrates.service.FloatingRateWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "FLOATINGRATE", action = "UPDATE")
public class UpdateFloatingRateCommandHandler implements NewCommandSourceHandler {
private final FloatingRateWritePlatformService writePlatformService;
@Autowired
public UpdateFloatingRateCommandHandler(final FloatingRateWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.updateFloatingRate(command);
}
}
@@ -1,248 +0,0 @@
/**
* 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.floatingrates.serialization;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class FloatingRateDataValidator {
public static final String NAME = "name";
public static final String IS_BASE_LENDING_RATE = "isBaseLendingRate";
public static final String IS_ACTIVE = "isActive";
public static final String RATE_PERIODS = "ratePeriods";
public static final String FROM_DATE = "fromDate";
public static final String INTEREST_RATE = "interestRate";
public static final String IS_DIFFERENTIAL_TO_BASE_LENDING_RATE = "isDifferentialToBaseLendingRate";
public static final String LOCALE = "locale";
public static final String DATE_FORMAT = "dateFormat";
public static final String FLOATINGRATE = "floatingrate";
private static final Set<String> SUPPORTED_PARAMETERS_FOR_FLOATING_RATES = new HashSet<>(
Arrays.asList(NAME, IS_BASE_LENDING_RATE, IS_ACTIVE, RATE_PERIODS));
private static final Set<String> SUPPORTED_PARAMETERS_FOR_FLOATING_RATE_PERIODS = new HashSet<>(
Arrays.asList(FROM_DATE, INTEREST_RATE, IS_DIFFERENTIAL_TO_BASE_LENDING_RATE, LOCALE, DATE_FORMAT));
private final FromJsonHelper fromApiJsonHelper;
private final FloatingRateRepository floatingRateRepository;
@Autowired
public FloatingRateDataValidator(final FromJsonHelper fromApiJsonHelper, final FloatingRateRepository floatingRateRepository) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.floatingRateRepository = floatingRateRepository;
}
public void validateForCreate(String json) {
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_PARAMETERS_FOR_FLOATING_RATES);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(FLOATINGRATE);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String name = this.fromApiJsonHelper.extractStringNamed(NAME, element);
baseDataValidator.reset().parameter(NAME).value(name).notBlank().notExceedingLengthOf(200);
Boolean isBaseLendingRate = null;
if (this.fromApiJsonHelper.parameterExists(IS_BASE_LENDING_RATE, element)) {
isBaseLendingRate = this.fromApiJsonHelper.extractBooleanNamed(IS_BASE_LENDING_RATE, element);
baseDataValidator.reset().parameter(IS_BASE_LENDING_RATE).value(isBaseLendingRate).notNull();
if (isBaseLendingRate == null) {
baseDataValidator.reset().parameter(IS_BASE_LENDING_RATE).trueOrFalseRequired(false);
} else if (isBaseLendingRate) {
FloatingRate baseLendingRate = this.floatingRateRepository.retrieveBaseLendingRate();
if (baseLendingRate != null) {
baseDataValidator.reset().parameter(IS_BASE_LENDING_RATE).value(isBaseLendingRate)
.failWithCode("baselendingrate.duplicate", "Base Lending Rate already exists");
}
}
}
if (this.fromApiJsonHelper.parameterExists(IS_ACTIVE, element)) {
final Boolean isActive = this.fromApiJsonHelper.extractBooleanNamed(IS_ACTIVE, element);
if (isActive == null) {
baseDataValidator.reset().parameter(IS_ACTIVE).trueOrFalseRequired(false);
}
}
if (isBaseLendingRate == null) {
isBaseLendingRate = false;
}
validateRatePeriods(baseDataValidator, element, isBaseLendingRate, false);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void validateRatePeriods(DataValidatorBuilder baseDataValidator, JsonElement element, boolean isBaseLendingRate,
boolean isBLRModifiedAsNonBLR) {
if (this.fromApiJsonHelper.parameterExists(RATE_PERIODS, element)) {
final JsonArray ratePeriods = this.fromApiJsonHelper.extractJsonArrayNamed(RATE_PERIODS, element);
baseDataValidator.reset().parameter(RATE_PERIODS).value(ratePeriods).notBlank().jsonArrayNotEmpty();
if (ratePeriods != null) {
List<LocalDate> fromDates = new ArrayList<>();
for (int i = 0; i < ratePeriods.size(); i++) {
final JsonElement ratePeriod = ratePeriods.get(i);
this.fromApiJsonHelper.checkForUnsupportedParameters(ratePeriod.getAsJsonObject(),
SUPPORTED_PARAMETERS_FOR_FLOATING_RATE_PERIODS);
final LocalDate fromDate = this.fromApiJsonHelper.extractLocalDateNamed(FROM_DATE, ratePeriod);
baseDataValidator.reset().parameter(FROM_DATE).parameterAtIndexArray(FROM_DATE, i + 1).value(fromDate).notBlank()
.validateDateAfter(DateUtils.getBusinessLocalDate().plusDays(1));
if (fromDate != null) {
fromDates.add(fromDate);
}
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(INTEREST_RATE,
ratePeriod);
baseDataValidator.reset().parameter(INTEREST_RATE).parameterAtIndexArray(INTEREST_RATE, i + 1)
.value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
if (this.fromApiJsonHelper.parameterExists(IS_DIFFERENTIAL_TO_BASE_LENDING_RATE, ratePeriod)) {
final Boolean isDifferentialToBaseLendingRate = this.fromApiJsonHelper
.extractBooleanNamed(IS_DIFFERENTIAL_TO_BASE_LENDING_RATE, ratePeriod);
if (isDifferentialToBaseLendingRate == null) {
baseDataValidator.reset().parameter(IS_DIFFERENTIAL_TO_BASE_LENDING_RATE)
.parameterAtIndexArray(IS_DIFFERENTIAL_TO_BASE_LENDING_RATE, i + 1).trueOrFalseRequired(false);
} else if (isDifferentialToBaseLendingRate) {
FloatingRate baseLendingRate = this.floatingRateRepository.retrieveBaseLendingRate();
if (baseLendingRate == null || isBLRModifiedAsNonBLR) {
baseDataValidator.reset().parameter(IS_DIFFERENTIAL_TO_BASE_LENDING_RATE)
.parameterAtIndexArray(IS_DIFFERENTIAL_TO_BASE_LENDING_RATE, i + 1)
.value(isDifferentialToBaseLendingRate)
.failWithCode("no.baselending.rate.defined", "Base Lending Rate doesn't exists");
}
if (isBaseLendingRate) {
baseDataValidator.reset().parameter(IS_DIFFERENTIAL_TO_BASE_LENDING_RATE)
.parameterAtIndexArray(IS_DIFFERENTIAL_TO_BASE_LENDING_RATE, i + 1)
.value(isDifferentialToBaseLendingRate).failWithCode("cannot.be.true.for.baselendingrate",
"isDifferentialToBaseLendingRate cannot be true for floating rate marked as Base Lending Rate.");
}
}
}
}
Set<LocalDate> uniqueFromDates = new HashSet<>(fromDates);
if (fromDates.size() != uniqueFromDates.size()) {
baseDataValidator.reset().parameter(FROM_DATE).failWithCode("multiple.same.date",
"More than one entry in ratePeriods have same fromDate.");
}
}
}
}
public void validateForUpdate(String json, FloatingRate floatingRateForUpdate) {
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_PARAMETERS_FOR_FLOATING_RATES);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(FLOATINGRATE);
final JsonElement element = this.fromApiJsonHelper.parse(json);
if (this.fromApiJsonHelper.parameterExists(NAME, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(NAME, element);
baseDataValidator.reset().parameter(NAME).value(name).notBlank().notExceedingLengthOf(200);
}
Boolean isBaseLendingRate = null;
Boolean isBLRModifiedAsNonBLR = false;
FloatingRate baseLendingRate = this.floatingRateRepository.retrieveBaseLendingRate();
if (this.fromApiJsonHelper.parameterExists(IS_BASE_LENDING_RATE, element)) {
isBaseLendingRate = this.fromApiJsonHelper.extractBooleanNamed(IS_BASE_LENDING_RATE, element);
baseDataValidator.reset().parameter(IS_BASE_LENDING_RATE).value(isBaseLendingRate).notNull();
if (isBaseLendingRate == null) {
baseDataValidator.reset().parameter(IS_BASE_LENDING_RATE).trueOrFalseRequired(false);
} else if (isBaseLendingRate && baseLendingRate != null && !baseLendingRate.getId().equals(floatingRateForUpdate.getId())) { // NOSONAR
baseDataValidator.reset().parameter(IS_BASE_LENDING_RATE).value(isBaseLendingRate).failWithCode("baselendingrate.duplicate",
"Base Lending Rate already exists");
}
}
Boolean isActive = null;
if (this.fromApiJsonHelper.parameterExists(IS_ACTIVE, element)) {
isActive = this.fromApiJsonHelper.extractBooleanNamed(IS_ACTIVE, element);
if (isActive == null) {
baseDataValidator.reset().parameter(IS_ACTIVE).trueOrFalseRequired(false);
}
}
if (isBaseLendingRate == null) {
isBaseLendingRate = floatingRateForUpdate.isBaseLendingRate();
}
if (isActive == null) {
isActive = floatingRateForUpdate.isActive();
}
if (baseLendingRate != null && baseLendingRate.getId().equals(floatingRateForUpdate.getId()) && (!isBaseLendingRate || !isActive)) { // NOSONAR
isBLRModifiedAsNonBLR = true;
}
if (isBLRModifiedAsNonBLR) {
Collection<FloatingRate> floatingRates = this.floatingRateRepository.retrieveFloatingRatesLinkedToBLR();
if (floatingRates != null && !floatingRates.isEmpty()) {
baseDataValidator.reset().parameter(IS_BASE_LENDING_RATE).value(isBaseLendingRate).failWithCode(
"cannot.be.marked.non.baselendingrate",
"There are floating rates linked to this Base Lending Rate, cannot be marked as non-Base Lending Rate.");
}
}
validateRatePeriods(baseDataValidator, element, isBaseLendingRate, isBLRModifiedAsNonBLR);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
}
}
@@ -1,29 +0,0 @@
/**
* 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.floatingrates.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface FloatingRateWritePlatformService {
CommandProcessingResult createFloatingRate(JsonCommand command);
CommandProcessingResult updateFloatingRate(JsonCommand command);
}
@@ -1,107 +0,0 @@
/**
* 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.floatingrates.service;
import jakarta.persistence.PersistenceException;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepositoryWrapper;
import org.apache.fineract.portfolio.floatingrates.serialization.FloatingRateDataValidator;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Slf4j
public class FloatingRateWritePlatformServiceImpl implements FloatingRateWritePlatformService {
private final FloatingRateDataValidator fromApiJsonDeserializer;
private final FloatingRateRepositoryWrapper floatingRateRepository;
@Transactional
@Override
public CommandProcessingResult createFloatingRate(final JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForCreate(command.json());
final FloatingRate newFloatingRate = FloatingRate.createNew(command);
this.floatingRateRepository.saveAndFlush(newFloatingRate);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(newFloatingRate.getId()) //
.build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
return CommandProcessingResult.empty();
} catch (final PersistenceException dve) {
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
handleDataIntegrityIssues(command, throwable, dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult updateFloatingRate(final JsonCommand command) {
try {
final FloatingRate floatingRateForUpdate = this.floatingRateRepository.findOneWithNotFoundDetection(command.entityId());
this.fromApiJsonDeserializer.validateForUpdate(command.json(), floatingRateForUpdate);
final Map<String, Object> changes = floatingRateForUpdate.update(command);
if (!changes.isEmpty()) {
this.floatingRateRepository.save(floatingRateForUpdate);
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(command.entityId()) //
.with(changes) //
.build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
return CommandProcessingResult.empty();
} catch (final PersistenceException dve) {
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
handleDataIntegrityIssues(command, throwable, dve);
return CommandProcessingResult.empty();
}
}
private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dve) {
if (realCause.getMessage().contains("unq_name")) {
final String name = command.stringValueOfParameterNamed("name");
throw new PlatformDataIntegrityException("error.msg.floatingrates.duplicate.name",
"Floating Rate with name `" + name + "` already exists", "name", name);
}
if (realCause.getMessage().contains("unq_rate_period")) {
throw new PlatformDataIntegrityException("error.msg.floatingrates.duplicate.active.fromdate",
"Attempt to add multiple floating rate periods with same fromdate", "fromdate", "");
}
log.error("Error occured.", dve);
throw ErrorHandler.getMappable(dve, "error.msg.floatingrates.unknown.data.integrity.issue",
"Unknown data integrity issue with resource: " + realCause.getMessage());
}
}
@@ -1,39 +0,0 @@
/**
* 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.floatingrates.service;
import java.util.List;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateData;
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
public interface FloatingRatesReadPlatformService {
List<FloatingRateData> retrieveAll();
List<FloatingRateData> retrieveLookupActive();
FloatingRateData retrieveOne(Long floatingRateId);
List<FloatingRateData> retrieveAllActive();
FloatingRateData retrieveBaseLendingRate();
List<InterestRatePeriodData> retrieveInterestRatePeriods(Long productId);
}
@@ -1,239 +0,0 @@
/**
* 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.floatingrates.service;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateData;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@RequiredArgsConstructor
public class FloatingRatesReadPlatformServiceImpl implements FloatingRatesReadPlatformService {
private final JdbcTemplate jdbcTemplate;
@Override
public List<FloatingRateData> retrieveAll() {
FloatingRateRowMapper rateMapper = new FloatingRateRowMapper(false);
final String sql = "select " + rateMapper.schema();
return this.jdbcTemplate.query(sql, rateMapper); // NOSONAR
}
@Override
public List<FloatingRateData> retrieveAllActive() {
FloatingRateRowMapper rateMapper = new FloatingRateRowMapper(false);
final String sql = "select " + rateMapper.schema() + " where rate.is_active = true ";
return this.jdbcTemplate.query(sql, rateMapper);// NOSONAR
}
@Override
public List<FloatingRateData> retrieveLookupActive() {
FloatingRateLookupMapper rateMapper = new FloatingRateLookupMapper();
final String sql = "select " + rateMapper.schema() + " where rate.is_active = true ";
return this.jdbcTemplate.query(sql, rateMapper); // NOSONAR
}
@Override
public FloatingRateData retrieveOne(final Long floatingRateId) {
try {
FloatingRateRowMapper rateMapper = new FloatingRateRowMapper(true);
final String sql = "select " + rateMapper.schema() + " where rate.id = ?";
return this.jdbcTemplate.queryForObject(sql, rateMapper, new Object[] { floatingRateId }); // NOSONAR
} catch (final EmptyResultDataAccessException e) {
throw new FloatingRateNotFoundException(floatingRateId, e);
}
}
@Override
public List<InterestRatePeriodData> retrieveInterestRatePeriods(final Long productId) {
try {
FloatingInterestRatePeriodRowMapper mapper = new FloatingInterestRatePeriodRowMapper();
return this.jdbcTemplate.query(mapper.schema(), mapper, new Object[] { productId }); // NOSONAR
} catch (final EmptyResultDataAccessException e) {
throw new FloatingRateNotFoundException("error.msg.floatingrate.not.found.for.product", e);
}
}
@Override
public FloatingRateData retrieveBaseLendingRate() {
try {
FloatingRateRowMapper rateMapper = new FloatingRateRowMapper(true);
final String sql = "select " + rateMapper.schema() + " where rate.is_base_lending_rate = true and rate.is_active = true";
return this.jdbcTemplate.queryForObject(sql, rateMapper); // NOSONAR
} catch (final EmptyResultDataAccessException e) {
throw new FloatingRateNotFoundException("error.msg.floatingrate.base.lending.rate.not.found", e);
}
}
private final class FloatingRateRowMapper implements RowMapper<FloatingRateData> {
private final boolean addRatePeriods;
private final StringBuilder sqlQuery = new StringBuilder().append("rate.id as id, ").append("rate.name as name, ")
.append("rate.is_base_lending_rate as isBaseLendingRate, ").append("rate.is_active as isActive, ")
.append("crappu.username as createdBy, ").append("rate.created_date as createdOn, ")
.append("rate.created_on_utc as createdOnUTC, ").append("moappu.username as modifiedBy, ")
.append("rate.lastmodified_date as modifiedOn, ").append("rate.last_modified_on_utc as modifiedOnUTC ")
.append("FROM m_floating_rates as rate ").append("LEFT JOIN m_appuser as crappu on rate.created_by = crappu.id ")
.append("LEFT JOIN m_appuser as moappu on rate.last_modified_by = moappu.id ");
FloatingRateRowMapper(final boolean addRatePeriods) {
this.addRatePeriods = addRatePeriods;
}
@Override
public FloatingRateData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
final boolean isBaseLendingRate = rs.getBoolean("isBaseLendingRate");
final boolean isActive = rs.getBoolean("isActive");
final String createdBy = rs.getString("createdBy");
final OffsetDateTime createdOnLocal = JdbcSupport.getOffsetDateTime(rs, "createdOn");
final OffsetDateTime createdOnUtc = JdbcSupport.getOffsetDateTime(rs, "createdOnUTC");
final String modifiedBy = rs.getString("modifiedBy");
final OffsetDateTime modifiedOnLocal = JdbcSupport.getOffsetDateTime(rs, "modifiedOn");
final OffsetDateTime modifiedOnUtc = JdbcSupport.getOffsetDateTime(rs, "modifiedOnUTC");
List<FloatingRatePeriodData> ratePeriods = null;
final OffsetDateTime createdOn = createdOnUtc != null ? createdOnUtc : createdOnLocal;
final OffsetDateTime modifiedOn = modifiedOnUtc != null ? modifiedOnUtc : modifiedOnLocal;
if (addRatePeriods) {
FloatingRatePeriodRowMapper ratePeriodMapper = new FloatingRatePeriodRowMapper();
final String sql = "select " + ratePeriodMapper.schema()
+ " where period.is_active = true and period.floating_rates_id = ? " + " order by period.from_date desc ";
ratePeriods = jdbcTemplate.query(sql, ratePeriodMapper, id); // NOSONAR
}
return new FloatingRateData(id, name, isBaseLendingRate, isActive, createdBy, createdOn, modifiedBy, modifiedOn, ratePeriods,
null);
}
public String schema() {
return sqlQuery.toString();
}
}
private static final class FloatingRatePeriodRowMapper implements RowMapper<FloatingRatePeriodData> {
private final StringBuilder sqlQuery = new StringBuilder().append("period.id as id, ").append("period.from_date as fromDate, ")
.append("period.interest_rate as interestRate, ")
.append("period.is_differential_to_base_lending_rate as isDifferentialToBaseLendingRate, ")
.append("period.is_active as isActive, ").append("crappu.username as createdBy, ")
.append("period.created_date as createdOn, ").append("period.created_on_utc as createdOnUTC, ")
.append("moappu.username as modifiedBy, ").append("period.lastmodified_date as modifiedOn, ")
.append("period.last_modified_on_utc as modifiedOnUTC ").append("FROM m_floating_rates_periods as period ")
.append("LEFT JOIN m_appuser as crappu on period.created_by = crappu.id ")
.append("LEFT JOIN m_appuser as moappu on period.last_modified_by = moappu.id ");
@Override
public FloatingRatePeriodData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate");
final BigDecimal interestRate = rs.getBigDecimal("interestRate");
final boolean isDifferentialToBaseLendingRate = rs.getBoolean("isDifferentialToBaseLendingRate");
final boolean isActive = rs.getBoolean("isActive");
final String createdBy = rs.getString("createdBy");
final OffsetDateTime createdOnLocal = JdbcSupport.getOffsetDateTime(rs, "createdOn");
final OffsetDateTime createdOnUtc = JdbcSupport.getOffsetDateTime(rs, "createdOnUTC");
final String modifiedBy = rs.getString("modifiedBy");
final OffsetDateTime modifiedOnLocal = JdbcSupport.getOffsetDateTime(rs, "modifiedOn");
final OffsetDateTime modifiedOnUtc = JdbcSupport.getOffsetDateTime(rs, "modifiedOnUTC");
final OffsetDateTime createdOn = createdOnUtc != null ? createdOnUtc : createdOnLocal;
final OffsetDateTime modifiedOn = modifiedOnUtc != null ? modifiedOnUtc : modifiedOnLocal;
return new FloatingRatePeriodData(id, fromDate, interestRate, isDifferentialToBaseLendingRate, isActive, createdBy, createdOn,
modifiedBy, modifiedOn);
}
public String schema() {
return sqlQuery.toString();
}
}
private static final class FloatingRateLookupMapper implements RowMapper<FloatingRateData> {
private final StringBuilder sqlQuery = new StringBuilder().append("rate.id as id, ").append("rate.name as name, ")
.append("rate.is_base_lending_rate as isBaseLendingRate ").append("FROM m_floating_rates as rate ");
@Override
public FloatingRateData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
final boolean isBaseLendingRate = rs.getBoolean("isBaseLendingRate");
return new FloatingRateData(id, name, isBaseLendingRate, true, null, null, null, null, null, null);
}
public String schema() {
return sqlQuery.toString();
}
}
private static final class FloatingInterestRatePeriodRowMapper implements RowMapper<InterestRatePeriodData> {
private final StringBuilder sqlQuery = new StringBuilder().append("select ")
.append(" linkedrateperiods.from_date as linkedrateperiods_from_date, ")
.append(" linkedrateperiods.interest_rate as linkedrateperiods_interest_rate, ")
.append(" linkedrateperiods.is_differential_to_base_lending_rate as linkedrateperiods_is_differential_to_base_lending_rate, ")
.append(" baserate.from_date as baserate_from_date, ").append(" baserate.interest_rate as baserate_interest_rate ")
.append(" from m_product_loan as lp ")
.append(" join m_product_loan_floating_rates as plfr on lp.id = plfr.loan_product_id ")
.append(" join m_floating_rates as linkedrate on linkedrate.id = plfr.floating_rates_id ")
.append("left join m_floating_rates_periods as linkedrateperiods on (linkedrate.id = linkedrateperiods.floating_rates_id and linkedrateperiods.is_active = true) ")
.append("left join ( ").append(" select blr.name, ").append(" blr.is_base_lending_rate, ")
.append(" blr.is_active, ").append(" blrperiods.from_date, ").append(" blrperiods.interest_rate ")
.append(" from m_floating_rates as blr ")
.append(" left join m_floating_rates_periods as blrperiods on (blr.id = blrperiods.floating_rates_id and blrperiods.is_active = true) ")
.append(" where blr.is_base_lending_rate = true and blr.is_active = true ")
.append(") as baserate on (linkedrateperiods.is_differential_to_base_lending_rate = true and linkedrate.is_base_lending_rate = false) ")
.append("where (baserate.from_date is null ").append(" or baserate.from_date = (select MAX(b.from_date) ")
.append(" from (select blr.name, ").append(" blr.is_base_lending_rate, ")
.append(" blr.is_active, ").append(" blrperiods.from_date, ")
.append(" blrperiods.interest_rate ").append(" from m_floating_rates as blr ")
.append(" left join m_floating_rates_periods as blrperiods on (blr.id = blrperiods.floating_rates_id and blrperiods.is_active = true) ")
.append(" where blr.is_base_lending_rate = true and blr.is_active = true ").append(" ) as b ")
.append(" where b.from_date <= linkedrateperiods.from_date)) ").append("and lp.id = ? ")
.append("order by linkedratePeriods_from_date desc ");
@Override
public InterestRatePeriodData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Date fromDate = rs.getDate("linkedrateperiods_from_date");
final BigDecimal interestRate = rs.getBigDecimal("linkedrateperiods_interest_rate");
final boolean isDifferentialToBLR = rs.getBoolean("linkedrateperiods_is_differential_to_base_lending_rate");
final Date blrFromDate = rs.getDate("baserate_from_date");
final BigDecimal blrInterestRate = rs.getBigDecimal("baserate_interest_rate");
final LocalDate fromLocalDate = fromDate != null ? fromDate.toLocalDate() : null;
final LocalDate blrFromLocalDate = blrFromDate != null ? blrFromDate.toLocalDate() : null;
return new InterestRatePeriodData(fromLocalDate, interestRate, isDifferentialToBLR, blrFromLocalDate, blrInterestRate);
}
public String schema() {
return sqlQuery.toString();
}
}
}
@@ -1,47 +0,0 @@
/**
* 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.floatingrates.starter;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepositoryWrapper;
import org.apache.fineract.portfolio.floatingrates.serialization.FloatingRateDataValidator;
import org.apache.fineract.portfolio.floatingrates.service.FloatingRateWritePlatformService;
import org.apache.fineract.portfolio.floatingrates.service.FloatingRateWritePlatformServiceImpl;
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class FloatingRatesConfiguration {
@Bean
@ConditionalOnMissingBean(FloatingRatesReadPlatformService.class)
public FloatingRatesReadPlatformService floatingRatesReadPlatformService(JdbcTemplate jdbcTemplate) {
return new FloatingRatesReadPlatformServiceImpl(jdbcTemplate);
}
@Bean
@ConditionalOnMissingBean(FloatingRateWritePlatformService.class)
public FloatingRateWritePlatformService floatingRateWritePlatformService(FloatingRateDataValidator fromApiJsonDeserializer,
FloatingRateRepositoryWrapper floatingRateRepository) {
return new FloatingRateWritePlatformServiceImpl(fromApiJsonDeserializer, floatingRateRepository);
}
}