update RD Prodcut code
This commit is contained in:
+79
@@ -292,6 +292,44 @@ public final class DepositsApiConstants {
|
||||
recurringDepositRequestData.addAll(DEPOSIT_TERM_REQUEST_DATA_PARAMETERS);
|
||||
recurringDepositRequestData.addAll(RECURRING_DETAILS_REQUEST_DATA_PARAMETERS);
|
||||
recurringDepositRequestData.add(SavingsApiConstants.minBalanceForInterestCalculationParamName);
|
||||
// Custom CBS fields — Details step
|
||||
recurringDepositRequestData.add("accountType");
|
||||
recurringDepositRequestData.add("customerSegment");
|
||||
recurringDepositRequestData.add("productCode");
|
||||
recurringDepositRequestData.add("effectiveTo");
|
||||
recurringDepositRequestData.add("endDate");
|
||||
recurringDepositRequestData.add("introducerRequired");
|
||||
recurringDepositRequestData.add("shareLinkingEnabled");
|
||||
// Custom CBS fields — Currency step
|
||||
recurringDepositRequestData.add("dormancyAlert");
|
||||
recurringDepositRequestData.add("balanceSwapOption");
|
||||
recurringDepositRequestData.add("msme");
|
||||
recurringDepositRequestData.add("nominationFacility");
|
||||
recurringDepositRequestData.add("autoDebitFacility");
|
||||
// Custom CBS fields — Terms step
|
||||
recurringDepositRequestData.add("effectiveDate");
|
||||
recurringDepositRequestData.add("interestCalculateSelector");
|
||||
recurringDepositRequestData.add("averagePeriod");
|
||||
recurringDepositRequestData.add("autoPost");
|
||||
recurringDepositRequestData.add("frequencyFixDay");
|
||||
recurringDepositRequestData.add("lastInterestDate");
|
||||
// Custom CBS fields — Settings step
|
||||
recurringDepositRequestData.add("loanFacility");
|
||||
recurringDepositRequestData.add("prematureWithdrawalOption");
|
||||
recurringDepositRequestData.add("tenureFlexibility");
|
||||
recurringDepositRequestData.add("lienMarked");
|
||||
recurringDepositRequestData.add("withdrawalFeeForTransfers");
|
||||
recurringDepositRequestData.add("enableLockinPeriod");
|
||||
recurringDepositRequestData.add("maturityInstructions");
|
||||
recurringDepositRequestData.add("creditMaturityProceeds");
|
||||
recurringDepositRequestData.add("penaltyGracePeriodMissedInstallments");
|
||||
recurringDepositRequestData.add("panelRequestApplicable");
|
||||
recurringDepositRequestData.add("noPenaltyNomineeHeirClosure");
|
||||
recurringDepositRequestData.add("deceasedSettlement");
|
||||
recurringDepositRequestData.add("allowDebitTxnAfterDueDate");
|
||||
recurringDepositRequestData.add("accountClosedZeroBalanceInDayClosing");
|
||||
// Custom CBS fields — Interest rate slabs (sent inline but ignored server-side; saved separately)
|
||||
recurringDepositRequestData.add("rdSlabs");
|
||||
return recurringDepositRequestData;
|
||||
}
|
||||
|
||||
@@ -302,6 +340,47 @@ public final class DepositsApiConstants {
|
||||
recurringDepositRequestData.addAll(DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS);
|
||||
recurringDepositRequestData.addAll(RECURRING_DETAILS_RESPONSE_DATA_PARAMETERS);
|
||||
recurringDepositRequestData.add(SavingsApiConstants.minBalanceForInterestCalculationParamName);
|
||||
// CBS extended fields — Options (for template=true)
|
||||
recurringDepositRequestData.add("interestCalculationTypeOption");
|
||||
recurringDepositRequestData.add("periodFrequencyOptions");
|
||||
recurringDepositRequestData.add("yesNoOptions");
|
||||
recurringDepositRequestData.add("accountTypeOptions");
|
||||
recurringDepositRequestData.add("customerSegmentOptions");
|
||||
// CBS extended fields — Details step
|
||||
recurringDepositRequestData.add("accountType");
|
||||
recurringDepositRequestData.add("customerSegment");
|
||||
recurringDepositRequestData.add("productCode");
|
||||
recurringDepositRequestData.add("effectiveDate");
|
||||
recurringDepositRequestData.add("effectiveTo");
|
||||
recurringDepositRequestData.add("endDate");
|
||||
recurringDepositRequestData.add("introducerRequired");
|
||||
recurringDepositRequestData.add("shareLinkingEnabled");
|
||||
recurringDepositRequestData.add("nominationFacility");
|
||||
recurringDepositRequestData.add("dormancyAlert");
|
||||
// CBS extended fields — Terms step
|
||||
recurringDepositRequestData.add("interestCalculateSelector");
|
||||
recurringDepositRequestData.add("averagePeriod");
|
||||
recurringDepositRequestData.add("autoPost");
|
||||
recurringDepositRequestData.add("frequencyFixDay");
|
||||
recurringDepositRequestData.add("lastInterestDate");
|
||||
// CBS extended fields — Settings step
|
||||
recurringDepositRequestData.add("enableLockinPeriod");
|
||||
recurringDepositRequestData.add("balanceFrequency");
|
||||
recurringDepositRequestData.add("accountClosedZeroBalanceInDayClosing");
|
||||
recurringDepositRequestData.add("balanceSwapOption");
|
||||
recurringDepositRequestData.add("msme");
|
||||
recurringDepositRequestData.add("autoDebitFacility");
|
||||
recurringDepositRequestData.add("loanFacility");
|
||||
recurringDepositRequestData.add("prematureWithdrawalOption");
|
||||
recurringDepositRequestData.add("tenureFlexibility");
|
||||
recurringDepositRequestData.add("lienMarked");
|
||||
recurringDepositRequestData.add("maturityInstructions");
|
||||
recurringDepositRequestData.add("creditMaturityProceeds");
|
||||
recurringDepositRequestData.add("penaltyGracePeriodMissedInstallments");
|
||||
recurringDepositRequestData.add("panelRequestApplicable");
|
||||
recurringDepositRequestData.add("noPenaltyNomineeHeirClosure");
|
||||
recurringDepositRequestData.add("deceasedSettlement");
|
||||
recurringDepositRequestData.add("allowDebitTxnAfterDueDate");
|
||||
return recurringDepositRequestData;
|
||||
}
|
||||
|
||||
|
||||
+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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.api;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Request payload for saving a new RD interest rate version.
|
||||
* JSON structure:
|
||||
* {
|
||||
* "versionLabel": "Rate Version 2025-Q2",
|
||||
* "remarks": "Revised rates",
|
||||
* "slabs": [
|
||||
* { "srNo": 1, "description": "15 Days to 29 Days", "periodTypeEnum": 1,
|
||||
* "fromPeriod": 15, "toPeriod": 29, "annualInterestRate": 3.00 },
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class RdInterestRateSaveRequest {
|
||||
|
||||
private String versionLabel;
|
||||
private String remarks;
|
||||
private List<SlabRequest> slabs;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public static class SlabRequest {
|
||||
private Integer srNo;
|
||||
private String description;
|
||||
private Integer periodTypeEnum;
|
||||
private Integer fromPeriod;
|
||||
private Integer toPeriod;
|
||||
private BigDecimal annualInterestRate;
|
||||
}
|
||||
}
|
||||
+15
-4
@@ -79,6 +79,11 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
|
||||
@@ -117,6 +122,10 @@ public class RecurringDepositProductsApiResource {
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(LocalDate.class,
|
||||
(JsonSerializer<LocalDate>) (src, typeOfSrc, ctx) -> new JsonPrimitive(src.toString()))
|
||||
.create();
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -354,19 +363,21 @@ public class RecurringDepositProductsApiResource {
|
||||
private String generateNextProductCode(final String tableName, final String prefix) {
|
||||
try {
|
||||
String maxCode = jdbcTemplate.queryForObject(
|
||||
"SELECT MAX(product_code) FROM " + tableName + " WHERE product_code LIKE ? AND deposit_type_enum = 3",
|
||||
"SELECT MAX(product_code) FROM " + tableName + " WHERE product_code LIKE ? AND deposit_type_enum = 300",
|
||||
String.class, prefix + "%");
|
||||
if (maxCode == null || maxCode.isBlank()) {
|
||||
return prefix + "00";
|
||||
return prefix + "001";
|
||||
}
|
||||
String numStr = maxCode.substring(prefix.length());
|
||||
int num = Integer.parseInt(numStr);
|
||||
return prefix + String.format("%02d", num + 1);
|
||||
return prefix + String.format("%03d", num + 1);
|
||||
} catch (Exception e) {
|
||||
return prefix + "00";
|
||||
return prefix + "001";
|
||||
}
|
||||
}
|
||||
|
||||
// ── RD Interest Rate Chart endpoints ────────────────────────────────────
|
||||
|
||||
@DELETE
|
||||
@Path("{productId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
import org.apache.fineract.portfolio.savings.service.RdBaseInterestRateReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/recurringdepositproducts/baseinterestrates")
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RecurringDepositProductsBaseInterestRatesApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final RdBaseInterestRateReadPlatformService rdBaseInterestRateReadPlatformService;
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(LocalDate.class,
|
||||
(JsonSerializer<LocalDate>) (src, typeOfSrc, ctx) -> new JsonPrimitive(src.toString()))
|
||||
.create();
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Get all active RD base interest rate charts", description = "Returns the current CBS base interest-rate version with slabs.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public String retrieveAllActiveInterestRates() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final List<RdInterestRateEffectiveData> data = this.rdBaseInterestRateReadPlatformService.retrieveAllActiveInterestRates();
|
||||
return gson.toJson(data);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("history")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Get all RD base interest rate charts", description = "Returns the full CBS base chart history.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public String retrieveHistory() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final List<RdInterestRateEffectiveData> data = this.rdBaseInterestRateReadPlatformService.retrieveAllInterestRates();
|
||||
return gson.toJson(data);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save new RD base interest rate version", description = "Creates a new CBS base chart version and deactivates the previous active version.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public String saveInterestRates(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final RdInterestRateSaveRequest request = gson.fromJson(apiRequestBodyAsJson, RdInterestRateSaveRequest.class);
|
||||
final Long createdById = this.context.authenticatedUser().getId();
|
||||
final RdInterestRateEffectiveData saved = this.rdBaseInterestRateReadPlatformService.saveInterestRates(request, createdById);
|
||||
return gson.toJson(saved);
|
||||
}
|
||||
}
|
||||
+23
-8
@@ -170,13 +170,21 @@ public class DepositProductDataValidator {
|
||||
|
||||
validatePreClosureDetailForCreate(element, baseDataValidator);
|
||||
|
||||
validateDepositTermDeatilForCreate(element, baseDataValidator);
|
||||
// When custom rdSlabs are provided, minDepositTerm / charts / depositAmount are not required
|
||||
final boolean hasRdSlabs = this.fromApiJsonHelper.parameterExists("rdSlabs", element)
|
||||
&& this.fromApiJsonHelper.extractJsonArrayNamed("rdSlabs", element) != null
|
||||
&& this.fromApiJsonHelper.extractJsonArrayNamed("rdSlabs", element).size() > 0;
|
||||
|
||||
if (!hasRdSlabs) {
|
||||
validateDepositTermDeatilForCreate(element, baseDataValidator);
|
||||
}
|
||||
|
||||
validateRecurringDetailForCreate(element, baseDataValidator);
|
||||
|
||||
validateChartsData(element, baseDataValidator);
|
||||
|
||||
validateDepositAmountForCreate(element, baseDataValidator);
|
||||
if (!hasRdSlabs) {
|
||||
validateChartsData(element, baseDataValidator);
|
||||
validateDepositAmountForCreate(element, baseDataValidator);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
@@ -198,13 +206,20 @@ public class DepositProductDataValidator {
|
||||
|
||||
validatePreClosureDetailForUpdate(element, baseDataValidator);
|
||||
|
||||
validateDepositTermDetailForUpdate(element, baseDataValidator);
|
||||
final boolean hasRdSlabsForUpdate = this.fromApiJsonHelper.parameterExists("rdSlabs", element)
|
||||
&& this.fromApiJsonHelper.extractJsonArrayNamed("rdSlabs", element) != null
|
||||
&& this.fromApiJsonHelper.extractJsonArrayNamed("rdSlabs", element).size() > 0;
|
||||
|
||||
if (!hasRdSlabsForUpdate) {
|
||||
validateDepositTermDetailForUpdate(element, baseDataValidator);
|
||||
}
|
||||
|
||||
validateRecurringDepositUpdate(element, baseDataValidator);
|
||||
|
||||
validateChartsData(element, baseDataValidator);
|
||||
|
||||
validateDepositAmountForUpdate(element, baseDataValidator);
|
||||
if (!hasRdSlabsForUpdate) {
|
||||
validateChartsData(element, baseDataValidator);
|
||||
validateDepositAmountForUpdate(element, baseDataValidator);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class RdInterestRateEffectiveData {
|
||||
|
||||
private Long id;
|
||||
private Long productId;
|
||||
private String versionLabel;
|
||||
private LocalDate effectiveFromDate;
|
||||
private LocalDate effectiveToDate;
|
||||
private Boolean isActive;
|
||||
private String remarks;
|
||||
private List<RdInterestRateSlabData> slabs;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class RdInterestRateSlabData {
|
||||
|
||||
private Long id;
|
||||
private Integer srNo;
|
||||
private String description;
|
||||
private Integer periodTypeEnum;
|
||||
private String periodTypeLabel;
|
||||
private Integer fromPeriod;
|
||||
private Integer toPeriod;
|
||||
private BigDecimal annualInterestRate;
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_rd_base_interest_rate_effective")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdBaseInterestRateEffective {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "version_label")
|
||||
private String versionLabel;
|
||||
|
||||
@Column(name = "effective_from_date")
|
||||
private LocalDate effectiveFromDate;
|
||||
|
||||
@Column(name = "effective_to_date")
|
||||
private LocalDate effectiveToDate;
|
||||
|
||||
@Column(name = "is_active")
|
||||
private Boolean isActive;
|
||||
|
||||
@Column(name = "remarks")
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "created_by_id")
|
||||
private Long createdById;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDate createdDate;
|
||||
|
||||
public static RdBaseInterestRateEffective create(String versionLabel, LocalDate effectiveFromDate, String remarks, Long createdById) {
|
||||
RdBaseInterestRateEffective e = new RdBaseInterestRateEffective();
|
||||
e.versionLabel = versionLabel;
|
||||
e.effectiveFromDate = effectiveFromDate;
|
||||
e.isActive = true;
|
||||
e.remarks = remarks;
|
||||
e.createdById = createdById;
|
||||
e.createdDate = LocalDate.now();
|
||||
return e;
|
||||
}
|
||||
|
||||
public void deactivate(LocalDate toDate) {
|
||||
this.isActive = false;
|
||||
this.effectiveToDate = toDate;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RdBaseInterestRateEffectiveRepository extends JpaRepository<RdBaseInterestRateEffective, Long> {
|
||||
|
||||
List<RdBaseInterestRateEffective> findByIsActiveTrueOrderByEffectiveFromDateDesc();
|
||||
|
||||
List<RdBaseInterestRateEffective> findByOrderByEffectiveFromDateDesc();
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_rd_base_interest_rate_slab_history")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdBaseInterestRateSlabHistory {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "effective_id")
|
||||
private Long effectiveId;
|
||||
|
||||
@Column(name = "sr_no")
|
||||
private Integer srNo;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "period_type_enum")
|
||||
private Integer periodTypeEnum;
|
||||
|
||||
@Column(name = "from_period")
|
||||
private Integer fromPeriod;
|
||||
|
||||
@Column(name = "to_period")
|
||||
private Integer toPeriod;
|
||||
|
||||
@Column(name = "annual_interest_rate")
|
||||
private BigDecimal annualInterestRate;
|
||||
|
||||
public static RdBaseInterestRateSlabHistory create(Long effectiveId, Integer srNo,
|
||||
String description, Integer periodTypeEnum, Integer fromPeriod, Integer toPeriod, BigDecimal annualInterestRate) {
|
||||
RdBaseInterestRateSlabHistory s = new RdBaseInterestRateSlabHistory();
|
||||
s.effectiveId = effectiveId;
|
||||
s.srNo = srNo;
|
||||
s.description = description;
|
||||
s.periodTypeEnum = periodTypeEnum != null ? periodTypeEnum : 1;
|
||||
s.fromPeriod = fromPeriod;
|
||||
s.toPeriod = toPeriod;
|
||||
s.annualInterestRate = annualInterestRate;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RdBaseInterestRateSlabHistoryRepository extends JpaRepository<RdBaseInterestRateSlabHistory, Long> {
|
||||
|
||||
List<RdBaseInterestRateSlabHistory> findByEffectiveIdOrderBySrNo(Long effectiveId);
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Maps to: m_rd_interest_rate_effective
|
||||
* Stores one "version" of the bank's RD interest rate chart for a product.
|
||||
* When a new version is saved the old one is deactivated (is_active=0) but kept for history.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_rd_interest_rate_effective")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdInterestRateEffective {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "product_id")
|
||||
private Long productId;
|
||||
|
||||
@Column(name = "version_label")
|
||||
private String versionLabel;
|
||||
|
||||
@Column(name = "effective_from_date")
|
||||
private LocalDate effectiveFromDate;
|
||||
|
||||
@Column(name = "effective_to_date")
|
||||
private LocalDate effectiveToDate;
|
||||
|
||||
@Column(name = "is_active")
|
||||
private Boolean isActive;
|
||||
|
||||
@Column(name = "remarks")
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "created_by_id")
|
||||
private Long createdById;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDate createdDate;
|
||||
|
||||
public static RdInterestRateEffective create(Long productId, String versionLabel,
|
||||
LocalDate effectiveFromDate, String remarks, Long createdById) {
|
||||
RdInterestRateEffective e = new RdInterestRateEffective();
|
||||
e.productId = productId;
|
||||
e.versionLabel = versionLabel;
|
||||
e.effectiveFromDate = effectiveFromDate;
|
||||
e.isActive = true;
|
||||
e.remarks = remarks;
|
||||
e.createdById = createdById;
|
||||
e.createdDate = LocalDate.now();
|
||||
return e;
|
||||
}
|
||||
|
||||
public void deactivate(LocalDate toDate) {
|
||||
this.isActive = false;
|
||||
this.effectiveToDate = toDate;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RdInterestRateEffectiveRepository extends JpaRepository<RdInterestRateEffective, Long> {
|
||||
|
||||
List<RdInterestRateEffective> findByProductIdOrderByEffectiveFromDateDesc(Long productId);
|
||||
|
||||
List<RdInterestRateEffective> findByProductIdAndIsActiveTrueOrderByEffectiveFromDateDesc(Long productId);
|
||||
|
||||
List<RdInterestRateEffective> findByIsActiveTrueOrderByEffectiveFromDateDesc();
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Maps to: m_rd_interest_rate_slab_history
|
||||
* One row per slab per version. All historical slabs are preserved.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_rd_interest_rate_slab_history")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdInterestRateSlabHistory {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "effective_id")
|
||||
private Long effectiveId;
|
||||
|
||||
@Column(name = "sr_no")
|
||||
private Integer srNo;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "period_type_enum")
|
||||
private Integer periodTypeEnum;
|
||||
|
||||
@Column(name = "from_period")
|
||||
private Integer fromPeriod;
|
||||
|
||||
@Column(name = "to_period")
|
||||
private Integer toPeriod;
|
||||
|
||||
@Column(name = "annual_interest_rate")
|
||||
private BigDecimal annualInterestRate;
|
||||
|
||||
public static RdInterestRateSlabHistory create(Long effectiveId, Integer srNo,
|
||||
String description, Integer periodTypeEnum,
|
||||
Integer fromPeriod, Integer toPeriod, BigDecimal annualInterestRate) {
|
||||
RdInterestRateSlabHistory s = new RdInterestRateSlabHistory();
|
||||
s.effectiveId = effectiveId;
|
||||
s.srNo = srNo;
|
||||
s.description = description;
|
||||
s.periodTypeEnum = periodTypeEnum != null ? periodTypeEnum : 1;
|
||||
s.fromPeriod = fromPeriod;
|
||||
s.toPeriod = toPeriod;
|
||||
s.annualInterestRate = annualInterestRate;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RdInterestRateSlabHistoryRepository extends JpaRepository<RdInterestRateSlabHistory, Long> {
|
||||
|
||||
List<RdInterestRateSlabHistory> findByEffectiveIdOrderBySrNo(Long effectiveId);
|
||||
}
|
||||
+71
-2
@@ -301,9 +301,26 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId, ");
|
||||
sqlBuilder.append("sp.account_type as accountType, sp.customer_segment as customerSegment, ");
|
||||
sqlBuilder.append("sp.product_code as productCode, ");
|
||||
sqlBuilder.append("sp.effective_date as effectiveDate, sp.effective_to as effectiveTo, sp.end_date as endDate, ");
|
||||
sqlBuilder.append("sp.introducer_required as introducerRequired, sp.share_linking_enabled as shareLinkingEnabled, ");
|
||||
sqlBuilder.append("sp.nomination_facility as nominationFacility, sp.dormancy_alert as dormancyAlert, ");
|
||||
sqlBuilder.append("sp.interest_calculate_selector as interestCalculateSelector, ");
|
||||
sqlBuilder.append("sp.average_period as averagePeriod, ");
|
||||
sqlBuilder.append("sp.auto_post as autoPost, sp.frequency_fix_day as frequencyFixDay ");
|
||||
sqlBuilder.append("sp.auto_post as autoPost, sp.frequency_fix_day as frequencyFixDay, ");
|
||||
sqlBuilder.append("sp.last_interest_date as lastInterestDate, sp.enable_lockin_period as enableLockinPeriod, ");
|
||||
sqlBuilder.append("sp.balance_frequency as balanceFrequency, ");
|
||||
sqlBuilder.append("sp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
||||
sqlBuilder.append("sp.balance_swap_option as balanceSwapOption, sp.msme as msme, ");
|
||||
sqlBuilder.append("sp.auto_debit_facility as autoDebitFacility, sp.loan_facility as loanFacility, ");
|
||||
sqlBuilder.append("sp.premature_withdrawal_option as prematureWithdrawalOption, sp.tenure_flexibility as tenureFlexibility, ");
|
||||
sqlBuilder.append("sp.lien_marked as lienMarked, sp.maturity_instructions as maturityInstructions, ");
|
||||
sqlBuilder.append("sp.credit_maturity_proceeds as creditMaturityProceeds, ");
|
||||
sqlBuilder.append("sp.penalty_grace_period_missed_installments as penaltyGracePeriodMissedInstallments, ");
|
||||
sqlBuilder.append("sp.panel_request_applicable as panelRequestApplicable, ");
|
||||
sqlBuilder.append("sp.no_penalty_nominee_heir_closure as noPenaltyNomineeHeirClosure, ");
|
||||
sqlBuilder.append("sp.deceased_settlement as deceasedSettlement, ");
|
||||
sqlBuilder.append("sp.allow_debit_txn_after_due_date as allowDebitTxnAfterDueDate, ");
|
||||
sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers ");
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id ");
|
||||
sqlBuilder.append("left join m_deposit_product_recurring_detail dprd on sp.id=dprd.savings_product_id ");
|
||||
@@ -350,10 +367,36 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
final String accountType = rs.getString("accountType");
|
||||
final String customerSegment = rs.getString("customerSegment");
|
||||
final String productCode = rs.getString("productCode");
|
||||
final java.time.LocalDate effectiveDate = JdbcSupport.getLocalDate(rs, "effectiveDate");
|
||||
final java.time.LocalDate effectiveTo = JdbcSupport.getLocalDate(rs, "effectiveTo");
|
||||
final java.time.LocalDate endDate = JdbcSupport.getLocalDate(rs, "endDate");
|
||||
final boolean introducerRequired = rs.getBoolean("introducerRequired");
|
||||
final boolean shareLinkingEnabled = rs.getBoolean("shareLinkingEnabled");
|
||||
final boolean nominationFacility = rs.getBoolean("nominationFacility");
|
||||
final boolean dormancyAlert = rs.getBoolean("dormancyAlert");
|
||||
final Integer interestCalculateSelector = JdbcSupport.getInteger(rs, "interestCalculateSelector");
|
||||
final String averagePeriod = rs.getString("averagePeriod");
|
||||
final Boolean autoPost = rs.getBoolean("autoPost");
|
||||
final boolean autoPost = rs.getBoolean("autoPost");
|
||||
final Integer frequencyFixDay = JdbcSupport.getInteger(rs, "frequencyFixDay");
|
||||
final java.time.LocalDate lastInterestDate = JdbcSupport.getLocalDate(rs, "lastInterestDate");
|
||||
final boolean enableLockinPeriod = rs.getBoolean("enableLockinPeriod");
|
||||
final String balanceFrequency = rs.getString("balanceFrequency");
|
||||
final boolean accountClosedZeroBalanceInDayClosing = rs.getBoolean("accountClosedZeroBalanceInDayClosing");
|
||||
final boolean balanceSwapOption = rs.getBoolean("balanceSwapOption");
|
||||
final boolean msme = rs.getBoolean("msme");
|
||||
final boolean autoDebitFacility = rs.getBoolean("autoDebitFacility");
|
||||
final boolean loanFacility = rs.getBoolean("loanFacility");
|
||||
final boolean prematureWithdrawalOption = rs.getBoolean("prematureWithdrawalOption");
|
||||
final boolean tenureFlexibility = rs.getBoolean("tenureFlexibility");
|
||||
final boolean lienMarked = rs.getBoolean("lienMarked");
|
||||
final boolean maturityInstructions = rs.getBoolean("maturityInstructions");
|
||||
final boolean creditMaturityProceeds = rs.getBoolean("creditMaturityProceeds");
|
||||
final boolean penaltyGracePeriodMissedInstallments = rs.getBoolean("penaltyGracePeriodMissedInstallments");
|
||||
final boolean panelRequestApplicable = rs.getBoolean("panelRequestApplicable");
|
||||
final boolean noPenaltyNomineeHeirClosure = rs.getBoolean("noPenaltyNomineeHeirClosure");
|
||||
final boolean deceasedSettlement = rs.getBoolean("deceasedSettlement");
|
||||
final boolean allowDebitTxnAfterDueDate = rs.getBoolean("allowDebitTxnAfterDueDate");
|
||||
final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
|
||||
|
||||
RecurringDepositProductData result = RecurringDepositProductData.instance(depositProductData,
|
||||
preClosurePenalApplicable, preClosurePenalInterest,
|
||||
@@ -363,10 +406,36 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
result.setAccountType(accountType);
|
||||
result.setCustomerSegment(customerSegment);
|
||||
result.setProductCode(productCode);
|
||||
result.setEffectiveDate(effectiveDate);
|
||||
result.setEffectiveTo(effectiveTo);
|
||||
result.setEndDate(endDate);
|
||||
result.setIntroducerRequired(introducerRequired);
|
||||
result.setShareLinkingEnabled(shareLinkingEnabled);
|
||||
result.setNominationFacility(nominationFacility);
|
||||
result.setDormancyAlert(dormancyAlert);
|
||||
result.setInterestCalculateSelector(interestCalculateSelector);
|
||||
result.setAveragePeriod(averagePeriod);
|
||||
result.setAutoPost(autoPost);
|
||||
result.setFrequencyFixDay(frequencyFixDay);
|
||||
result.setLastInterestDate(lastInterestDate);
|
||||
result.setEnableLockinPeriod(enableLockinPeriod);
|
||||
result.setBalanceFrequency(balanceFrequency);
|
||||
result.setAccountClosedZeroBalanceInDayClosing(accountClosedZeroBalanceInDayClosing);
|
||||
result.setBalanceSwapOption(balanceSwapOption);
|
||||
result.setMsme(msme);
|
||||
result.setAutoDebitFacility(autoDebitFacility);
|
||||
result.setLoanFacility(loanFacility);
|
||||
result.setPrematureWithdrawalOption(prematureWithdrawalOption);
|
||||
result.setTenureFlexibility(tenureFlexibility);
|
||||
result.setLienMarked(lienMarked);
|
||||
result.setMaturityInstructions(maturityInstructions);
|
||||
result.setCreditMaturityProceeds(creditMaturityProceeds);
|
||||
result.setPenaltyGracePeriodMissedInstallments(penaltyGracePeriodMissedInstallments);
|
||||
result.setPanelRequestApplicable(panelRequestApplicable);
|
||||
result.setNoPenaltyNomineeHeirClosure(noPenaltyNomineeHeirClosure);
|
||||
result.setDeceasedSettlement(deceasedSettlement);
|
||||
result.setAllowDebitTxnAfterDueDate(allowDebitTxnAfterDueDate);
|
||||
result.setWithdrawalFeeForTransfers(withdrawalFeeForTransfers);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.savings.api.RdInterestRateSaveRequest;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
|
||||
public interface RdBaseInterestRateReadPlatformService {
|
||||
|
||||
List<RdInterestRateEffectiveData> retrieveAllInterestRates();
|
||||
|
||||
List<RdInterestRateEffectiveData> retrieveAllActiveInterestRates();
|
||||
|
||||
RdInterestRateEffectiveData saveInterestRates(RdInterestRateSaveRequest request, Long createdById);
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.portfolio.savings.api.RdInterestRateSaveRequest;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateSlabData;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffective;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistory;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class RdBaseInterestRateReadPlatformServiceImpl implements RdBaseInterestRateReadPlatformService {
|
||||
|
||||
private final RdBaseInterestRateEffectiveRepository effectiveRepository;
|
||||
private final RdBaseInterestRateSlabHistoryRepository slabRepository;
|
||||
|
||||
private static final String[] PERIOD_LABELS = { "", "Days", "Months", "Years" };
|
||||
|
||||
@Override
|
||||
public List<RdInterestRateEffectiveData> retrieveAllInterestRates() {
|
||||
return effectiveRepository.findByOrderByEffectiveFromDateDesc().stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RdInterestRateEffectiveData> retrieveAllActiveInterestRates() {
|
||||
return effectiveRepository.findByIsActiveTrueOrderByEffectiveFromDateDesc().stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public RdInterestRateEffectiveData saveInterestRates(RdInterestRateSaveRequest request, Long createdById) {
|
||||
validateRequest(request);
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> normalizedSlabs = normalizeSlabs(request.getSlabs());
|
||||
|
||||
final LocalDate today = LocalDate.now();
|
||||
final List<RdBaseInterestRateEffective> activeVersions = effectiveRepository.findByIsActiveTrueOrderByEffectiveFromDateDesc();
|
||||
for (RdBaseInterestRateEffective version : activeVersions) {
|
||||
version.deactivate(today);
|
||||
}
|
||||
if (!activeVersions.isEmpty()) {
|
||||
effectiveRepository.saveAll(activeVersions);
|
||||
}
|
||||
|
||||
final String label = request.getVersionLabel() != null && !request.getVersionLabel().isBlank()
|
||||
? request.getVersionLabel()
|
||||
: "CBS Base Rate Version " + LocalDate.now();
|
||||
|
||||
RdBaseInterestRateEffective newVersion = RdBaseInterestRateEffective.create(label, LocalDate.now(), request.getRemarks(), createdById);
|
||||
newVersion = effectiveRepository.saveAndFlush(newVersion);
|
||||
|
||||
final Long versionId = newVersion.getId();
|
||||
final AtomicInteger srCounter = new AtomicInteger(1);
|
||||
for (RdInterestRateSaveRequest.SlabRequest slabRequest : normalizedSlabs) {
|
||||
final int sr = slabRequest.getSrNo() != null ? slabRequest.getSrNo() : srCounter.getAndIncrement();
|
||||
final RdBaseInterestRateSlabHistory slab = RdBaseInterestRateSlabHistory.create(versionId, sr, slabRequest.getDescription(),
|
||||
slabRequest.getPeriodTypeEnum(), slabRequest.getFromPeriod(), slabRequest.getToPeriod(),
|
||||
slabRequest.getAnnualInterestRate());
|
||||
slabRepository.save(slab);
|
||||
}
|
||||
|
||||
return toData(newVersion);
|
||||
}
|
||||
|
||||
private void validateRequest(final RdInterestRateSaveRequest request) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder validator = new DataValidatorBuilder(errors).resource("RD_BASE_INTEREST_RATE_VERSION");
|
||||
|
||||
if (request == null) {
|
||||
validator.failWithCodeNoParameterAddedToErrorCode("request.cannot.be.null");
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.getSlabs() == null || request.getSlabs().isEmpty()) {
|
||||
validator.parameter("slabs").failWithCode("cannot.be.blank");
|
||||
}
|
||||
|
||||
if (request.getVersionLabel() != null && request.getVersionLabel().length() > 100) {
|
||||
validator.parameter("versionLabel").value(request.getVersionLabel()).failWithCode("exceeds.max.length");
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
private List<RdInterestRateSaveRequest.SlabRequest> normalizeSlabs(final List<RdInterestRateSaveRequest.SlabRequest> slabs) {
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> sorted = new ArrayList<>(slabs);
|
||||
sorted.sort(Comparator.comparing((RdInterestRateSaveRequest.SlabRequest slab) -> slab.getSrNo() == null ? Integer.MAX_VALUE : slab.getSrNo())
|
||||
.thenComparing(slab -> slab.getFromPeriod() == null ? Integer.MAX_VALUE : slab.getFromPeriod()));
|
||||
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> normalized = new ArrayList<>(sorted.size());
|
||||
Integer expectedFrom = 0;
|
||||
for (int i = 0; i < sorted.size(); i++) {
|
||||
final RdInterestRateSaveRequest.SlabRequest source = sorted.get(i);
|
||||
final RdInterestRateSaveRequest.SlabRequest target = new RdInterestRateSaveRequest.SlabRequest();
|
||||
|
||||
final Integer fromPeriod = source.getFromPeriod();
|
||||
if (fromPeriod == null || !fromPeriod.equals(expectedFrom)) {
|
||||
throwValidationError("fromPeriod", "must.start.from.previous.range.end");
|
||||
}
|
||||
target.setFromPeriod(fromPeriod);
|
||||
|
||||
final Integer toPeriod = source.getToPeriod();
|
||||
if (i < sorted.size() - 1) {
|
||||
if (toPeriod == null) {
|
||||
throwValidationError("toPeriod", "cannot.be.blank.for.intermediate.slab");
|
||||
}
|
||||
if (toPeriod < fromPeriod) {
|
||||
throwValidationError("toPeriod", "cannot.be.less.than.fromPeriod");
|
||||
}
|
||||
expectedFrom = toPeriod + 1;
|
||||
target.setToPeriod(toPeriod);
|
||||
} else {
|
||||
target.setToPeriod(null);
|
||||
}
|
||||
|
||||
target.setSrNo(source.getSrNo() == null ? i + 1 : source.getSrNo());
|
||||
target.setDescription(source.getDescription());
|
||||
target.setPeriodTypeEnum(source.getPeriodTypeEnum() == null ? 1 : source.getPeriodTypeEnum());
|
||||
target.setAnnualInterestRate(source.getAnnualInterestRate());
|
||||
|
||||
if (target.getAnnualInterestRate() == null) {
|
||||
throwValidationError("annualInterestRate", "cannot.be.blank");
|
||||
}
|
||||
if (target.getAnnualInterestRate().signum() < 0) {
|
||||
throwValidationError("annualInterestRate", "must.be.non.negative");
|
||||
}
|
||||
|
||||
normalized.add(target);
|
||||
}
|
||||
|
||||
if (!normalized.isEmpty() && normalized.get(0).getFromPeriod() != 0) {
|
||||
throwValidationError("fromPeriod", "must.start.from.zero");
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void throwValidationError(final String parameterName, final String code) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
new DataValidatorBuilder(errors).resource("RD_BASE_INTEREST_RATE_VERSION").parameter(parameterName).failWithCode(code);
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
private RdInterestRateEffectiveData toData(RdBaseInterestRateEffective e) {
|
||||
List<RdInterestRateSlabData> slabs = slabRepository.findByEffectiveIdOrderBySrNo(e.getId()).stream()
|
||||
.map(s -> RdInterestRateSlabData.builder()
|
||||
.id(s.getId())
|
||||
.srNo(s.getSrNo())
|
||||
.description(s.getDescription())
|
||||
.periodTypeEnum(s.getPeriodTypeEnum())
|
||||
.periodTypeLabel(periodLabel(s.getPeriodTypeEnum()))
|
||||
.fromPeriod(s.getFromPeriod())
|
||||
.toPeriod(s.getToPeriod())
|
||||
.annualInterestRate(s.getAnnualInterestRate())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return RdInterestRateEffectiveData.builder()
|
||||
.id(e.getId())
|
||||
.productId(null)
|
||||
.versionLabel(e.getVersionLabel())
|
||||
.effectiveFromDate(e.getEffectiveFromDate())
|
||||
.effectiveToDate(e.getEffectiveToDate())
|
||||
.isActive(e.getIsActive())
|
||||
.remarks(e.getRemarks())
|
||||
.slabs(slabs)
|
||||
.build();
|
||||
}
|
||||
|
||||
private String periodLabel(Integer code) {
|
||||
if (code == null || code < 1 || code >= PERIOD_LABELS.length) return "Days";
|
||||
return PERIOD_LABELS[code];
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.savings.api.RdInterestRateSaveRequest;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
|
||||
public interface RdInterestRateReadPlatformService {
|
||||
|
||||
/** All versions (with slabs) for a specific product, newest first. */
|
||||
List<RdInterestRateEffectiveData> retrieveInterestRates(Long productId);
|
||||
|
||||
/** All active versions across all RD products – used as reference on the create-product page. */
|
||||
List<RdInterestRateEffectiveData> retrieveAllActiveInterestRates();
|
||||
|
||||
/**
|
||||
* Saves a NEW version of the rate chart for the given product.
|
||||
* Deactivates the previous active version first (immutable history).
|
||||
* Returns the newly created version's data.
|
||||
*/
|
||||
RdInterestRateEffectiveData saveInterestRates(Long productId, RdInterestRateSaveRequest request, Long createdById);
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.portfolio.savings.api.RdInterestRateSaveRequest;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateSlabData;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdInterestRateEffective;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdInterestRateEffectiveRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistory;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistoryRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class RdInterestRateReadPlatformServiceImpl implements RdInterestRateReadPlatformService {
|
||||
|
||||
private final RdInterestRateEffectiveRepository effectiveRepository;
|
||||
private final RdInterestRateSlabHistoryRepository slabRepository;
|
||||
|
||||
private static final String[] PERIOD_LABELS = { "", "Days", "Months", "Years" };
|
||||
|
||||
@Override
|
||||
public List<RdInterestRateEffectiveData> retrieveInterestRates(Long productId) {
|
||||
return effectiveRepository.findByProductIdOrderByEffectiveFromDateDesc(productId)
|
||||
.stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RdInterestRateEffectiveData> retrieveAllActiveInterestRates() {
|
||||
return effectiveRepository.findByIsActiveTrueOrderByEffectiveFromDateDesc()
|
||||
.stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public RdInterestRateEffectiveData saveInterestRates(Long productId,
|
||||
RdInterestRateSaveRequest request, Long createdById) {
|
||||
|
||||
validateRequest(request);
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> normalizedSlabs = normalizeSlabs(request.getSlabs());
|
||||
|
||||
// 1. Deactivate all current active versions for this product, preserving end dates for audit/history.
|
||||
final LocalDate today = LocalDate.now();
|
||||
final List<RdInterestRateEffective> activeVersions = effectiveRepository
|
||||
.findByProductIdAndIsActiveTrueOrderByEffectiveFromDateDesc(productId);
|
||||
for (RdInterestRateEffective version : activeVersions) {
|
||||
version.deactivate(today);
|
||||
}
|
||||
if (!activeVersions.isEmpty()) {
|
||||
effectiveRepository.saveAll(activeVersions);
|
||||
}
|
||||
|
||||
// 2. Create the new version header
|
||||
String label = request.getVersionLabel() != null && !request.getVersionLabel().isBlank()
|
||||
? request.getVersionLabel()
|
||||
: "Rate Version " + LocalDate.now();
|
||||
|
||||
RdInterestRateEffective newVersion = RdInterestRateEffective.create(
|
||||
productId, label, LocalDate.now(), request.getRemarks(), createdById);
|
||||
newVersion = effectiveRepository.saveAndFlush(newVersion);
|
||||
|
||||
// 3. Insert slabs for the new version
|
||||
final Long versionId = newVersion.getId();
|
||||
AtomicInteger srCounter = new AtomicInteger(1);
|
||||
for (RdInterestRateSaveRequest.SlabRequest s : normalizedSlabs) {
|
||||
int sr = s.getSrNo() != null ? s.getSrNo() : srCounter.getAndIncrement();
|
||||
RdInterestRateSlabHistory slab = RdInterestRateSlabHistory.create(
|
||||
versionId, sr, s.getDescription(),
|
||||
s.getPeriodTypeEnum(), s.getFromPeriod(), s.getToPeriod(),
|
||||
s.getAnnualInterestRate());
|
||||
slabRepository.save(slab);
|
||||
}
|
||||
|
||||
return toData(newVersion);
|
||||
}
|
||||
|
||||
private void validateRequest(final RdInterestRateSaveRequest request) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder validator = new DataValidatorBuilder(errors).resource("RD_INTEREST_RATE_VERSION");
|
||||
|
||||
if (request == null) {
|
||||
validator.failWithCodeNoParameterAddedToErrorCode("request.cannot.be.null");
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.getSlabs() == null || request.getSlabs().isEmpty()) {
|
||||
validator.parameter("slabs").failWithCode("cannot.be.blank");
|
||||
}
|
||||
|
||||
if (request.getVersionLabel() != null && request.getVersionLabel().length() > 100) {
|
||||
validator.parameter("versionLabel").value(request.getVersionLabel()).failWithCode("exceeds.max.length");
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
private List<RdInterestRateSaveRequest.SlabRequest> normalizeSlabs(final List<RdInterestRateSaveRequest.SlabRequest> slabs) {
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> sorted = new ArrayList<>(slabs);
|
||||
sorted.sort(Comparator.comparing((RdInterestRateSaveRequest.SlabRequest slab) -> slab.getSrNo() == null ? Integer.MAX_VALUE : slab.getSrNo())
|
||||
.thenComparing(slab -> slab.getFromPeriod() == null ? Integer.MAX_VALUE : slab.getFromPeriod()));
|
||||
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> normalized = new ArrayList<>(sorted.size());
|
||||
Integer expectedFrom = 0;
|
||||
for (int i = 0; i < sorted.size(); i++) {
|
||||
final RdInterestRateSaveRequest.SlabRequest source = sorted.get(i);
|
||||
final RdInterestRateSaveRequest.SlabRequest target = new RdInterestRateSaveRequest.SlabRequest();
|
||||
|
||||
final Integer fromPeriod = source.getFromPeriod();
|
||||
if (fromPeriod == null || !fromPeriod.equals(expectedFrom)) {
|
||||
throwValidationError("fromPeriod", "must.start.from.previous.range.end");
|
||||
}
|
||||
target.setFromPeriod(fromPeriod);
|
||||
|
||||
final Integer toPeriod = source.getToPeriod();
|
||||
if (i < sorted.size() - 1) {
|
||||
if (toPeriod == null) {
|
||||
throwValidationError("toPeriod", "cannot.be.blank.for.intermediate.slab");
|
||||
}
|
||||
if (toPeriod < fromPeriod) {
|
||||
throwValidationError("toPeriod", "cannot.be.less.than.fromPeriod");
|
||||
}
|
||||
expectedFrom = toPeriod + 1;
|
||||
target.setToPeriod(toPeriod);
|
||||
} else {
|
||||
target.setToPeriod(null);
|
||||
}
|
||||
|
||||
target.setSrNo(source.getSrNo() == null ? i + 1 : source.getSrNo());
|
||||
target.setDescription(source.getDescription());
|
||||
target.setPeriodTypeEnum(source.getPeriodTypeEnum() == null ? 1 : source.getPeriodTypeEnum());
|
||||
target.setAnnualInterestRate(source.getAnnualInterestRate());
|
||||
|
||||
if (target.getAnnualInterestRate() == null) {
|
||||
throwValidationError("annualInterestRate", "cannot.be.blank");
|
||||
}
|
||||
if (target.getAnnualInterestRate().signum() < 0) {
|
||||
throwValidationError("annualInterestRate", "must.be.non.negative");
|
||||
}
|
||||
|
||||
normalized.add(target);
|
||||
}
|
||||
|
||||
if (!normalized.isEmpty() && normalized.get(0).getFromPeriod() != 0) {
|
||||
throwValidationError("fromPeriod", "must.start.from.zero");
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void throwValidationError(final String parameterName, final String code) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
new DataValidatorBuilder(errors).resource("RD_INTEREST_RATE_VERSION").parameter(parameterName).failWithCode(code);
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
// ── helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
private RdInterestRateEffectiveData toData(RdInterestRateEffective e) {
|
||||
List<RdInterestRateSlabData> slabs = slabRepository
|
||||
.findByEffectiveIdOrderBySrNo(e.getId())
|
||||
.stream()
|
||||
.map(s -> RdInterestRateSlabData.builder()
|
||||
.id(s.getId())
|
||||
.srNo(s.getSrNo())
|
||||
.description(s.getDescription())
|
||||
.periodTypeEnum(s.getPeriodTypeEnum())
|
||||
.periodTypeLabel(periodLabel(s.getPeriodTypeEnum()))
|
||||
.fromPeriod(s.getFromPeriod())
|
||||
.toPeriod(s.getToPeriod())
|
||||
.annualInterestRate(s.getAnnualInterestRate())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return RdInterestRateEffectiveData.builder()
|
||||
.id(e.getId())
|
||||
.productId(e.getProductId())
|
||||
.versionLabel(e.getVersionLabel())
|
||||
.effectiveFromDate(e.getEffectiveFromDate())
|
||||
.effectiveToDate(e.getEffectiveToDate())
|
||||
.isActive(e.getIsActive())
|
||||
.remarks(e.getRemarks())
|
||||
.slabs(slabs)
|
||||
.build();
|
||||
}
|
||||
|
||||
private String periodLabel(Integer code) {
|
||||
if (code == null || code < 1 || code >= PERIOD_LABELS.length) return "Days";
|
||||
return PERIOD_LABELS[code];
|
||||
}
|
||||
}
|
||||
+12
@@ -144,6 +144,10 @@ import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformS
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformServiceJpaRepositoryImpl;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository;
|
||||
import org.apache.fineract.portfolio.savings.service.RdBaseInterestRateReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.RdBaseInterestRateReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsSchedularInterestPoster;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsSchedularInterestPosterTask;
|
||||
import org.apache.fineract.portfolio.savings.service.search.SavingsAccountTransactionSearchService;
|
||||
@@ -159,6 +163,14 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@Configuration
|
||||
public class SavingsConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RdBaseInterestRateReadPlatformService.class)
|
||||
public RdBaseInterestRateReadPlatformService rdBaseInterestRateReadPlatformService(
|
||||
RdBaseInterestRateEffectiveRepository effectiveRepository,
|
||||
RdBaseInterestRateSlabHistoryRepository slabRepository) {
|
||||
return new RdBaseInterestRateReadPlatformServiceImpl(effectiveRepository, slabRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SavingsAccountTransactionSearchService.class)
|
||||
public SavingsAccountTransactionSearchService savingsAccountTransactionSearchService(PlatformSecurityContext context,
|
||||
|
||||
@@ -271,4 +271,9 @@
|
||||
<include file="parts/0251_add_savings_product_enhanced_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0255_create_current_product_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0256_add_recurring_deposit_product_codes.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0257_create_rd_interest_rate_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0267_seed_rd_interest_rate_data.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0270_add_rd_product_extra_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0271_create_rd_base_interest_rate_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0272_seed_rd_base_interest_rate_data.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!--
|
||||
m_rd_interest_rate_effective — one row per rate-version per product.
|
||||
m_rd_interest_rate_slab_history — slabs for every version (immutable once written).
|
||||
|
||||
Period Type Enum: 1 = Days, 2 = Months, 3 = Years
|
||||
-->
|
||||
|
||||
<changeSet id="0257a_create_rd_interest_rate_effective" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_rd_interest_rate_effective"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_rd_interest_rate_effective">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="product_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="version_label" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_from_date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_to_date" type="DATE"/>
|
||||
<column name="is_active" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="remarks" type="VARCHAR(500)"/>
|
||||
<column name="created_by_id" type="BIGINT"/>
|
||||
<column name="created_date" type="DATE"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_rd_interest_rate_effective"
|
||||
baseColumnNames="product_id"
|
||||
referencedTableName="m_savings_product"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_rd_rate_effective_product"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0257b_create_rd_interest_rate_slab_history" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_rd_interest_rate_slab_history"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_rd_interest_rate_slab_history">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="sr_no" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="description" type="VARCHAR(200)"/>
|
||||
<column name="period_type_enum" type="INT" defaultValueNumeric="1">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="from_period" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="to_period" type="INT"/>
|
||||
<column name="annual_interest_rate" type="DECIMAL(19,6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_date" type="DATE"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_rd_interest_rate_slab_history"
|
||||
baseColumnNames="effective_id"
|
||||
referencedTableName="m_rd_interest_rate_effective"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_rd_rate_slab_effective"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!--
|
||||
Seed RD Interest Rate Data (runs only if both tables are empty)
|
||||
Sr Particular Rate
|
||||
1 0 Days to 29 Days 3.00%
|
||||
2 30 Days to 45 Days 3.00%
|
||||
3 46 Days to 90 Days 3.00%
|
||||
4 91 Days to 180 Days 4.50%
|
||||
5 181 Days to below 1 Year 5.50%
|
||||
6 1 Year to Below 2 Year 7.00%
|
||||
7 444 Days 7.00%
|
||||
8 400 Days 7.00%
|
||||
9 500 Days 7.00%
|
||||
10 2 Year to Below 3 Year 7.00%
|
||||
11 3 Year to Below 5 Year 7.25%
|
||||
12 5 Year & Above 6.50%
|
||||
-->
|
||||
|
||||
<changeSet id="0267_insert_rd_rate_version" author="fineract">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_rd_interest_rate_effective</sqlCheck>
|
||||
</preConditions>
|
||||
<sql>
|
||||
INSERT INTO m_rd_interest_rate_effective
|
||||
(product_id, version_label, effective_from_date, effective_to_date, is_active, remarks, created_by_id, created_date)
|
||||
SELECT id, 'Initial Rate Version', '2025-01-01', NULL, TRUE,
|
||||
'Standard interest rate card published for RD products', 1, CURRENT_DATE
|
||||
FROM m_savings_product
|
||||
WHERE deposit_type_enum = 300
|
||||
ORDER BY id LIMIT 1;
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0268_insert_rd_rate_slabs" author="fineract">
|
||||
<validCheckSum>9:e35a0696046940f370f7cfb55c20740b</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_rd_interest_rate_slab_history</sqlCheck>
|
||||
</preConditions>
|
||||
<sql>
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 1, '0 Days to 29 Days', 1, 0, 29, 3.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 2, '30 Days to 45 Days', 1, 30, 45, 3.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 3, '46 Days to 90 Days', 1, 46, 90, 3.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 4, '91 Days to 180 Days', 1, 91, 180, 4.500000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 5, '181 Days to below 1 Year', 1, 181, 364, 5.500000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 6, '1 Year to Below 2 Year', 1, 365, 729, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 7, '444 Days', 1, 444, 444, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 8, '400 Days', 1, 400, 400, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 9, '500 Days', 1, 500, 500, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 10, '2 Year to Below 3 Year', 1, 730, 1094, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 11, '3 Year to Below 5 Year', 1, 1095, 1824, 7.250000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 12, '5 Year & Above', 1, 1825, NULL, 6.500000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!-- balance_frequency — may already exist from a prior manual migration -->
|
||||
<changeSet id="0270a_add_balance_frequency" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_savings_product" columnName="balance_frequency"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_savings_product">
|
||||
<column name="balance_frequency" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- account_closed_zero_balance_in_day_closing — may already exist -->
|
||||
<changeSet id="0270b_add_account_closed_zero_balance" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_savings_product" columnName="account_closed_zero_balance_in_day_closing"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_savings_product">
|
||||
<column name="account_closed_zero_balance_in_day_closing" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- 14 new RD-specific boolean flags -->
|
||||
<changeSet id="0270c_add_rd_product_flags" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_savings_product" columnName="balance_swap_option"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_savings_product">
|
||||
<column name="balance_swap_option" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="msme" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="auto_debit_facility" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="loan_facility" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="premature_withdrawal_option" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="tenure_flexibility" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="lien_marked" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="maturity_instructions" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="credit_maturity_proceeds" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="penalty_grace_period_missed_installments" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="panel_request_applicable" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="no_penalty_nominee_heir_closure" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="deceased_settlement" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="allow_debit_txn_after_due_date" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<changeSet id="0271a_create_rd_base_interest_rate_effective" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_rd_base_interest_rate_effective"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_rd_base_interest_rate_effective">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="version_label" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_from_date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_to_date" type="DATE"/>
|
||||
<column name="is_active" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="remarks" type="VARCHAR(500)"/>
|
||||
<column name="created_by_id" type="BIGINT"/>
|
||||
<column name="created_date" type="DATE"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0271b_create_rd_base_interest_rate_slab_history" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_rd_base_interest_rate_slab_history"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_rd_base_interest_rate_slab_history">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="sr_no" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="description" type="VARCHAR(200)"/>
|
||||
<column name="period_type_enum" type="INT" defaultValueNumeric="1">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="from_period" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="to_period" type="INT"/>
|
||||
<column name="annual_interest_rate" type="DECIMAL(19,6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_date" type="DATE"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_rd_base_interest_rate_slab_history"
|
||||
baseColumnNames="effective_id"
|
||||
referencedTableName="m_rd_base_interest_rate_effective"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_rd_base_rate_slab_effective"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<changeSet id="0272_insert_rd_base_rate_version" author="fineract">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_rd_base_interest_rate_effective</sqlCheck>
|
||||
</preConditions>
|
||||
<sql>
|
||||
INSERT INTO m_rd_base_interest_rate_effective
|
||||
(version_label, effective_from_date, effective_to_date, is_active, remarks, created_by_id, created_date)
|
||||
VALUES
|
||||
('CBS Base Rate Version', '2025-01-01', NULL, TRUE, 'Central RD base chart', 1, CURRENT_DATE);
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 1, '15 Days to 29 Days', 1, 15, 29, 3.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 2, '30 Days to 45 Days', 1, 30, 45, 3.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 3, '46 Days to 90 Days', 1, 46, 90, 3.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 4, '91 Days to 180 Days', 1, 91, 180, 4.500000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 5, '181 Days to below 1 Year', 1, 181, 364, 5.500000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 6, '1 Year to Below 2 Year', 1, 365, 729, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 7, '444 Days', 1, 444, 444, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 8, '400 Days', 1, 400, 400, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 9, '500 Days', 1, 500, 500, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 10, '2 Year to Below 3 Year', 1, 730, 1094, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 11, '3 Year to Below 5 Year', 1, 1095, 1824, 7.250000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 12, '5 Year & Above', 1, 1825, NULL, 6.500000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
</sql>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public final class RdInterestRateEffectiveData {
|
||||
|
||||
private final Long id;
|
||||
private final Long productId;
|
||||
private final String versionLabel;
|
||||
private final LocalDate effectiveFromDate;
|
||||
private final LocalDate effectiveToDate;
|
||||
private final Boolean isActive;
|
||||
private final String remarks;
|
||||
private final List<RdInterestRateSlabData> slabs;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public final class RdInterestRateSlabData {
|
||||
|
||||
private final Long id;
|
||||
private final Integer srNo;
|
||||
private final String description;
|
||||
private final Integer periodTypeEnum;
|
||||
private final String periodTypeLabel;
|
||||
private final Integer fromPeriod;
|
||||
private final Integer toPeriod;
|
||||
private final BigDecimal annualInterestRate;
|
||||
}
|
||||
+116
-6
@@ -65,10 +65,36 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
private String accountType;
|
||||
private String customerSegment;
|
||||
private String productCode;
|
||||
private java.time.LocalDate effectiveDate;
|
||||
private java.time.LocalDate effectiveTo;
|
||||
private java.time.LocalDate endDate;
|
||||
private Boolean introducerRequired;
|
||||
private Boolean shareLinkingEnabled;
|
||||
private Boolean nominationFacility;
|
||||
private Boolean dormancyAlert;
|
||||
private Integer interestCalculateSelector;
|
||||
private String averagePeriod;
|
||||
private Boolean autoPost;
|
||||
private Integer frequencyFixDay;
|
||||
private java.time.LocalDate lastInterestDate;
|
||||
private Boolean enableLockinPeriod;
|
||||
private String balanceFrequency;
|
||||
private Boolean accountClosedZeroBalanceInDayClosing;
|
||||
private Boolean balanceSwapOption;
|
||||
private Boolean msme;
|
||||
private Boolean autoDebitFacility;
|
||||
private Boolean loanFacility;
|
||||
private Boolean prematureWithdrawalOption;
|
||||
private Boolean tenureFlexibility;
|
||||
private Boolean lienMarked;
|
||||
private Boolean maturityInstructions;
|
||||
private Boolean creditMaturityProceeds;
|
||||
private Boolean penaltyGracePeriodMissedInstallments;
|
||||
private Boolean panelRequestApplicable;
|
||||
private Boolean noPenaltyNomineeHeirClosure;
|
||||
private Boolean deceasedSettlement;
|
||||
private Boolean allowDebitTxnAfterDueDate;
|
||||
private Boolean withdrawalFeeForTransfers;
|
||||
|
||||
public void setInterestCalculationTypeOption(Collection<EnumOptionData> v) { this.interestCalculationTypeOption = v; }
|
||||
public void setPeriodFrequencyOptions(Collection<EnumOptionData> v) { this.periodFrequencyOptions = v; }
|
||||
@@ -78,10 +104,84 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
public void setAccountType(String v) { this.accountType = v; }
|
||||
public void setCustomerSegment(String v) { this.customerSegment = v; }
|
||||
public void setProductCode(String v) { this.productCode = v; }
|
||||
public void setEffectiveDate(java.time.LocalDate v) { this.effectiveDate = v; }
|
||||
public void setEffectiveTo(java.time.LocalDate v) { this.effectiveTo = v; }
|
||||
public void setEndDate(java.time.LocalDate v) { this.endDate = v; }
|
||||
public void setIntroducerRequired(Boolean v) { this.introducerRequired = v; }
|
||||
public void setShareLinkingEnabled(Boolean v) { this.shareLinkingEnabled = v; }
|
||||
public void setNominationFacility(Boolean v) { this.nominationFacility = v; }
|
||||
public void setDormancyAlert(Boolean v) { this.dormancyAlert = v; }
|
||||
public void setInterestCalculateSelector(Integer v) { this.interestCalculateSelector = v; }
|
||||
public void setAveragePeriod(String v) { this.averagePeriod = v; }
|
||||
public void setAutoPost(Boolean v) { this.autoPost = v; }
|
||||
public void setFrequencyFixDay(Integer v) { this.frequencyFixDay = v; }
|
||||
public void setLastInterestDate(java.time.LocalDate v) { this.lastInterestDate = v; }
|
||||
public void setEnableLockinPeriod(Boolean v) { this.enableLockinPeriod = v; }
|
||||
public void setBalanceFrequency(String v) { this.balanceFrequency = v; }
|
||||
public void setAccountClosedZeroBalanceInDayClosing(Boolean v) { this.accountClosedZeroBalanceInDayClosing = v; }
|
||||
public void setBalanceSwapOption(Boolean v) { this.balanceSwapOption = v; }
|
||||
public void setMsme(Boolean v) { this.msme = v; }
|
||||
public void setAutoDebitFacility(Boolean v) { this.autoDebitFacility = v; }
|
||||
public void setLoanFacility(Boolean v) { this.loanFacility = v; }
|
||||
public void setPrematureWithdrawalOption(Boolean v) { this.prematureWithdrawalOption = v; }
|
||||
public void setTenureFlexibility(Boolean v) { this.tenureFlexibility = v; }
|
||||
public void setLienMarked(Boolean v) { this.lienMarked = v; }
|
||||
public void setMaturityInstructions(Boolean v) { this.maturityInstructions = v; }
|
||||
public void setCreditMaturityProceeds(Boolean v) { this.creditMaturityProceeds = v; }
|
||||
public void setPenaltyGracePeriodMissedInstallments(Boolean v) { this.penaltyGracePeriodMissedInstallments = v; }
|
||||
public void setPanelRequestApplicable(Boolean v) { this.panelRequestApplicable = v; }
|
||||
public void setNoPenaltyNomineeHeirClosure(Boolean v) { this.noPenaltyNomineeHeirClosure = v; }
|
||||
public void setDeceasedSettlement(Boolean v) { this.deceasedSettlement = v; }
|
||||
public void setAllowDebitTxnAfterDueDate(Boolean v) { this.allowDebitTxnAfterDueDate = v; }
|
||||
public void setWithdrawalFeeForTransfers(Boolean v) { this.withdrawalFeeForTransfers = v; }
|
||||
|
||||
/** Copy all CBS custom fields from source into this instance. */
|
||||
public void copyCbsFieldsFrom(final RecurringDepositProductData src) {
|
||||
if (src == null) return;
|
||||
this.accountType = src.accountType;
|
||||
this.customerSegment = src.customerSegment;
|
||||
this.productCode = src.productCode;
|
||||
this.effectiveDate = src.effectiveDate;
|
||||
this.effectiveTo = src.effectiveTo;
|
||||
this.endDate = src.endDate;
|
||||
this.introducerRequired = src.introducerRequired;
|
||||
this.shareLinkingEnabled = src.shareLinkingEnabled;
|
||||
this.nominationFacility = src.nominationFacility;
|
||||
this.dormancyAlert = src.dormancyAlert;
|
||||
this.interestCalculateSelector = src.interestCalculateSelector;
|
||||
this.averagePeriod = src.averagePeriod;
|
||||
this.autoPost = src.autoPost;
|
||||
this.frequencyFixDay = src.frequencyFixDay;
|
||||
this.lastInterestDate = src.lastInterestDate;
|
||||
this.enableLockinPeriod = src.enableLockinPeriod;
|
||||
this.balanceFrequency = src.balanceFrequency;
|
||||
this.accountClosedZeroBalanceInDayClosing = src.accountClosedZeroBalanceInDayClosing;
|
||||
this.balanceSwapOption = src.balanceSwapOption;
|
||||
this.msme = src.msme;
|
||||
this.autoDebitFacility = src.autoDebitFacility;
|
||||
this.loanFacility = src.loanFacility;
|
||||
this.prematureWithdrawalOption = src.prematureWithdrawalOption;
|
||||
this.tenureFlexibility = src.tenureFlexibility;
|
||||
this.lienMarked = src.lienMarked;
|
||||
this.maturityInstructions = src.maturityInstructions;
|
||||
this.creditMaturityProceeds = src.creditMaturityProceeds;
|
||||
this.penaltyGracePeriodMissedInstallments = src.penaltyGracePeriodMissedInstallments;
|
||||
this.panelRequestApplicable = src.panelRequestApplicable;
|
||||
this.noPenaltyNomineeHeirClosure = src.noPenaltyNomineeHeirClosure;
|
||||
this.deceasedSettlement = src.deceasedSettlement;
|
||||
this.allowDebitTxnAfterDueDate = src.allowDebitTxnAfterDueDate;
|
||||
this.withdrawalFeeForTransfers = src.withdrawalFeeForTransfers;
|
||||
// options
|
||||
this.interestCalculationTypeOption = src.interestCalculationTypeOption;
|
||||
this.periodFrequencyOptions = src.periodFrequencyOptions;
|
||||
this.yesNoOptions = src.yesNoOptions;
|
||||
this.accountTypeOptions = src.accountTypeOptions;
|
||||
this.customerSegmentOptions = src.customerSegmentOptions;
|
||||
}
|
||||
|
||||
public String getProductCode() {
|
||||
return productCode;
|
||||
}
|
||||
|
||||
public static RecurringDepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
|
||||
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
|
||||
@@ -146,7 +246,8 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
|
||||
public static RecurringDepositProductData withCharges(final RecurringDepositProductData existingProduct,
|
||||
final Collection<ChargeData> charges) {
|
||||
return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName,
|
||||
final RecurringDepositProductData result = new RecurringDepositProductData(existingProduct.id, existingProduct.name,
|
||||
existingProduct.shortName,
|
||||
existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate,
|
||||
existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType,
|
||||
existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType,
|
||||
@@ -166,6 +267,8 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
existingProduct.isMandatoryDeposit, existingProduct.allowWithdrawal, existingProduct.adjustAdvanceTowardsFuturePayments,
|
||||
existingProduct.periodFrequencyTypeOptions, existingProduct.minDepositAmount, existingProduct.depositAmount,
|
||||
existingProduct.maxDepositAmount, existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions);
|
||||
result.copyCbsFieldsFrom(existingProduct);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,7 +290,8 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName,
|
||||
final RecurringDepositProductData result = new RecurringDepositProductData(existingProduct.id, existingProduct.name,
|
||||
existingProduct.shortName,
|
||||
existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate,
|
||||
existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType,
|
||||
existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType,
|
||||
@@ -206,7 +310,8 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
existingProduct.adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, existingProduct.minDepositAmount,
|
||||
existingProduct.depositAmount, existingProduct.maxDepositAmount, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
taxGroupOptions);
|
||||
|
||||
result.copyCbsFieldsFrom(existingProduct);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static RecurringDepositProductData withAccountingDetails(final RecurringDepositProductData existingProduct,
|
||||
@@ -227,7 +332,8 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final Collection<ChargeData> penaltyOptions = null;
|
||||
|
||||
return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName,
|
||||
final RecurringDepositProductData result = new RecurringDepositProductData(existingProduct.id, existingProduct.name,
|
||||
existingProduct.shortName,
|
||||
existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate,
|
||||
existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType,
|
||||
existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType,
|
||||
@@ -245,6 +351,8 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
existingProduct.adjustAdvanceTowardsFuturePayments, existingProduct.periodFrequencyTypeOptions,
|
||||
existingProduct.minDepositAmount, existingProduct.depositAmount, existingProduct.maxDepositAmount,
|
||||
existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions);
|
||||
result.copyCbsFieldsFrom(existingProduct);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static RecurringDepositProductData instance(final DepositProductData depositProductData, final boolean preClosurePenalApplicable,
|
||||
@@ -367,7 +475,8 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
|
||||
public static RecurringDepositProductData withInterestChart(final RecurringDepositProductData product,
|
||||
final Collection<InterestRateChartData> interestRateCharts) {
|
||||
return new RecurringDepositProductData(product.id, product.name, product.shortName, product.description, product.currency,
|
||||
final RecurringDepositProductData result = new RecurringDepositProductData(product.id, product.name, product.shortName,
|
||||
product.description, product.currency,
|
||||
product.nominalAnnualInterestRate, product.interestCompoundingPeriodType, product.interestPostingPeriodType,
|
||||
product.interestCalculationType, product.interestCalculationDaysInYearType, product.lockinPeriodFrequency,
|
||||
product.lockinPeriodFrequencyType, product.minBalanceForInterestCalculation, product.accountingRule,
|
||||
@@ -383,7 +492,8 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
product.inMultiplesOfDepositTermType, product.isMandatoryDeposit, product.allowWithdrawal,
|
||||
product.adjustAdvanceTowardsFuturePayments, product.periodFrequencyTypeOptions, product.minDepositAmount,
|
||||
product.depositAmount, product.maxDepositAmount, product.withHoldTax, product.taxGroup, product.taxGroupOptions);
|
||||
|
||||
result.copyCbsFieldsFrom(product);
|
||||
return result;
|
||||
}
|
||||
|
||||
private RecurringDepositProductData(final Long id, final String name, final String shortName, final String description,
|
||||
|
||||
+69
@@ -58,9 +58,13 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHold
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.accounting.common.AccountingRuleType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
@@ -277,6 +281,47 @@ public class DepositProductAssembler {
|
||||
productTermAndPreClosure.updateProductReference(recurringDepositProduct);
|
||||
productRecurringDetail.updateProductReference(recurringDepositProduct);
|
||||
|
||||
// ── CBS Custom Fields (from 0251 migration) ─────────────────────────
|
||||
recurringDepositProduct.setAccountType(command.stringValueOfParameterNamed("accountType"));
|
||||
recurringDepositProduct.setCustomerSegment(command.stringValueOfParameterNamed("customerSegment"));
|
||||
recurringDepositProduct.setProductCode(command.stringValueOfParameterNamed("productCode"));
|
||||
recurringDepositProduct.setEffectiveDate(parseCbsDate(command.stringValueOfParameterNamed("effectiveDate")));
|
||||
recurringDepositProduct.setEffectiveTo(parseCbsDate(command.stringValueOfParameterNamed("effectiveTo")));
|
||||
recurringDepositProduct.setEndDate(parseCbsDate(command.stringValueOfParameterNamed("endDate")));
|
||||
recurringDepositProduct.setIntroducerRequired(command.booleanPrimitiveValueOfParameterNamed("introducerRequired"));
|
||||
recurringDepositProduct.setShareLinkingEnabled(command.booleanPrimitiveValueOfParameterNamed("shareLinkingEnabled"));
|
||||
recurringDepositProduct.setNominationFacility(command.booleanPrimitiveValueOfParameterNamed("nominationFacility"));
|
||||
recurringDepositProduct.setDormancyAlert(command.booleanPrimitiveValueOfParameterNamed("dormancyAlert"));
|
||||
recurringDepositProduct.setInterestCalculateSelector(command.integerValueOfParameterNamed("interestCalculateSelector"));
|
||||
recurringDepositProduct.setAveragePeriod(command.stringValueOfParameterNamed("averagePeriod"));
|
||||
recurringDepositProduct.setAutoPost(command.booleanPrimitiveValueOfParameterNamed("autoPost"));
|
||||
recurringDepositProduct.setFrequencyFixDay(command.integerValueOfParameterNamed("frequencyFixDay"));
|
||||
recurringDepositProduct.setLastInterestDate(parseCbsDate(command.stringValueOfParameterNamed("lastInterestDate")));
|
||||
recurringDepositProduct.setEnableLockinPeriod(command.booleanPrimitiveValueOfParameterNamed("enableLockinPeriod"));
|
||||
recurringDepositProduct.setBalanceFrequency(command.stringValueOfParameterNamed("balanceFrequency"));
|
||||
if (command.parameterExists("accountClosedZeroBalanceInDayClosing")) {
|
||||
recurringDepositProduct.setAccountClosedZeroBalanceInDayClosing(
|
||||
command.booleanPrimitiveValueOfParameterNamed("accountClosedZeroBalanceInDayClosing"));
|
||||
}
|
||||
|
||||
// ── RD-specific flags (from 0270 migration) ──────────────────────────
|
||||
recurringDepositProduct.setBalanceSwapOption(command.booleanPrimitiveValueOfParameterNamed("balanceSwapOption"));
|
||||
recurringDepositProduct.setMsme(command.booleanPrimitiveValueOfParameterNamed("msme"));
|
||||
recurringDepositProduct.setAutoDebitFacility(command.booleanPrimitiveValueOfParameterNamed("autoDebitFacility"));
|
||||
recurringDepositProduct.setLoanFacility(command.booleanPrimitiveValueOfParameterNamed("loanFacility"));
|
||||
recurringDepositProduct.setPrematureWithdrawalOption(command.booleanPrimitiveValueOfParameterNamed("prematureWithdrawalOption"));
|
||||
recurringDepositProduct.setTenureFlexibility(command.booleanPrimitiveValueOfParameterNamed("tenureFlexibility"));
|
||||
recurringDepositProduct.setLienMarked(command.booleanPrimitiveValueOfParameterNamed("lienMarked"));
|
||||
recurringDepositProduct.setMaturityInstructions(command.booleanPrimitiveValueOfParameterNamed("maturityInstructions"));
|
||||
recurringDepositProduct.setCreditMaturityProceeds(command.booleanPrimitiveValueOfParameterNamed("creditMaturityProceeds"));
|
||||
recurringDepositProduct.setPenaltyGracePeriodMissedInstallments(
|
||||
command.booleanPrimitiveValueOfParameterNamed("penaltyGracePeriodMissedInstallments"));
|
||||
recurringDepositProduct.setPanelRequestApplicable(command.booleanPrimitiveValueOfParameterNamed("panelRequestApplicable"));
|
||||
recurringDepositProduct.setNoPenaltyNomineeHeirClosure(
|
||||
command.booleanPrimitiveValueOfParameterNamed("noPenaltyNomineeHeirClosure"));
|
||||
recurringDepositProduct.setDeceasedSettlement(command.booleanPrimitiveValueOfParameterNamed("deceasedSettlement"));
|
||||
recurringDepositProduct.setAllowDebitTxnAfterDueDate(command.booleanPrimitiveValueOfParameterNamed("allowDebitTxnAfterDueDate"));
|
||||
|
||||
recurringDepositProduct.validateDomainRules();
|
||||
|
||||
return recurringDepositProduct;
|
||||
@@ -500,6 +545,30 @@ public class DepositProductAssembler {
|
||||
return new DepositProductAmountDetails(minDepositAmount, depositAmount, maxDepositAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a CBS date string using common formats (e.g. "17 April 2026", "2026-04-17").
|
||||
* Returns null if the input is null or blank.
|
||||
*/
|
||||
private LocalDate parseCbsDate(final String dateStr) {
|
||||
if (dateStr == null || dateStr.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
DateTimeFormatter[] formatters = {
|
||||
DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.ENGLISH),
|
||||
DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.ENGLISH),
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ENGLISH),
|
||||
DateTimeFormatter.ofPattern("dd/MM/yyyy", Locale.ENGLISH)
|
||||
};
|
||||
for (DateTimeFormatter fmt : formatters) {
|
||||
try {
|
||||
return LocalDate.parse(dateStr, fmt);
|
||||
} catch (DateTimeParseException ignored) {
|
||||
// try next format
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public TaxGroup assembleTaxGroup(final JsonCommand command) {
|
||||
final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName);
|
||||
TaxGroup taxGroup = null;
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_rd_interest_rate_effective")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class RdInterestRateEffective {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "product_id", nullable = false)
|
||||
private Long productId;
|
||||
|
||||
@Column(name = "version_label", nullable = false, length = 100)
|
||||
private String versionLabel;
|
||||
|
||||
@Column(name = "effective_from_date", nullable = false)
|
||||
private LocalDate effectiveFromDate;
|
||||
|
||||
@Column(name = "effective_to_date")
|
||||
private LocalDate effectiveToDate;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private Boolean isActive;
|
||||
|
||||
@Column(name = "remarks", length = 500)
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "created_by_id")
|
||||
private Long createdById;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDate createdDate;
|
||||
|
||||
public static RdInterestRateEffective create(Long productId, String versionLabel,
|
||||
LocalDate effectiveFromDate, String remarks, Long createdById) {
|
||||
RdInterestRateEffective e = new RdInterestRateEffective();
|
||||
e.productId = productId;
|
||||
e.versionLabel = versionLabel;
|
||||
e.effectiveFromDate = effectiveFromDate;
|
||||
e.isActive = true;
|
||||
e.remarks = remarks;
|
||||
e.createdById = createdById;
|
||||
e.createdDate = LocalDate.now();
|
||||
return e;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface RdInterestRateEffectiveRepository extends JpaRepository<RdInterestRateEffective, Long> {
|
||||
|
||||
List<RdInterestRateEffective> findByProductIdOrderByEffectiveFromDateDesc(Long productId);
|
||||
|
||||
List<RdInterestRateEffective> findByIsActiveTrueOrderByEffectiveFromDateDesc();
|
||||
|
||||
@Modifying
|
||||
@Query("UPDATE RdInterestRateEffective e SET e.isActive = false WHERE e.productId = :productId AND e.isActive = true")
|
||||
void deactivateAllForProduct(@Param("productId") Long productId);
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_rd_interest_rate_slab_history")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdInterestRateSlabHistory {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "effective_id", nullable = false)
|
||||
private Long effectiveId;
|
||||
|
||||
@Column(name = "sr_no", nullable = false)
|
||||
private Integer srNo;
|
||||
|
||||
@Column(name = "description", length = 200)
|
||||
private String description;
|
||||
|
||||
@Column(name = "period_type_enum", nullable = false)
|
||||
private Integer periodTypeEnum;
|
||||
|
||||
@Column(name = "from_period", nullable = false)
|
||||
private Integer fromPeriod;
|
||||
|
||||
@Column(name = "to_period")
|
||||
private Integer toPeriod;
|
||||
|
||||
@Column(name = "annual_interest_rate", nullable = false, precision = 19, scale = 6)
|
||||
private BigDecimal annualInterestRate;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDate createdDate;
|
||||
|
||||
public static RdInterestRateSlabHistory create(Long effectiveId, Integer srNo, String description,
|
||||
Integer periodTypeEnum, Integer fromPeriod, Integer toPeriod, BigDecimal annualInterestRate) {
|
||||
RdInterestRateSlabHistory s = new RdInterestRateSlabHistory();
|
||||
s.effectiveId = effectiveId;
|
||||
s.srNo = srNo;
|
||||
s.description = description;
|
||||
s.periodTypeEnum = periodTypeEnum != null ? periodTypeEnum : 1;
|
||||
s.fromPeriod = fromPeriod;
|
||||
s.toPeriod = toPeriod;
|
||||
s.annualInterestRate = annualInterestRate;
|
||||
s.createdDate = LocalDate.now();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface RdInterestRateSlabHistoryRepository extends JpaRepository<RdInterestRateSlabHistory, Long> {
|
||||
|
||||
List<RdInterestRateSlabHistory> findByEffectiveIdOrderBySrNo(Long effectiveId);
|
||||
}
|
||||
+10
-6
@@ -150,12 +150,16 @@ public class RecurringDepositProduct extends FixedDepositProduct {
|
||||
}
|
||||
|
||||
private void validateDomainRules(final DataValidatorBuilder baseDataValidator) {
|
||||
final DepositTermDetail termDetails = this.depositProductTermAndPreClosure().depositTermDetail();
|
||||
final boolean isMinTermGreaterThanMax = termDetails.isMinDepositTermGreaterThanMaxDepositTerm();
|
||||
if (isMinTermGreaterThanMax) {
|
||||
final Integer maxTerm = termDetails.maxDepositTerm();
|
||||
baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm)
|
||||
.failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term");
|
||||
final DepositTermDetail termDetails = this.depositProductTermAndPreClosure() != null
|
||||
? this.depositProductTermAndPreClosure().depositTermDetail()
|
||||
: null;
|
||||
if (termDetails != null) {
|
||||
final boolean isMinTermGreaterThanMax = termDetails.isMinDepositTermGreaterThanMaxDepositTerm();
|
||||
if (isMinTermGreaterThanMax) {
|
||||
final Integer maxTerm = termDetails.maxDepositTerm();
|
||||
baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm)
|
||||
.failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.charts != null) {
|
||||
|
||||
+191
-16
@@ -90,6 +90,9 @@ import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -290,6 +293,49 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
|
||||
@Column(name = "account_closed_zero_balance_in_day_closing")
|
||||
private Boolean accountClosedZeroBalanceInDayClosing = false;
|
||||
|
||||
// RD-specific flags
|
||||
@Column(name = "balance_swap_option")
|
||||
private boolean balanceSwapOption = false;
|
||||
|
||||
@Column(name = "msme")
|
||||
private boolean msme = false;
|
||||
|
||||
@Column(name = "auto_debit_facility")
|
||||
private boolean autoDebitFacility = false;
|
||||
|
||||
@Column(name = "loan_facility")
|
||||
private boolean loanFacility = false;
|
||||
|
||||
@Column(name = "premature_withdrawal_option")
|
||||
private boolean prematureWithdrawalOption = false;
|
||||
|
||||
@Column(name = "tenure_flexibility")
|
||||
private boolean tenureFlexibility = false;
|
||||
|
||||
@Column(name = "lien_marked")
|
||||
private boolean lienMarked = false;
|
||||
|
||||
@Column(name = "maturity_instructions")
|
||||
private boolean maturityInstructions = false;
|
||||
|
||||
@Column(name = "credit_maturity_proceeds")
|
||||
private boolean creditMaturityProceeds = false;
|
||||
|
||||
@Column(name = "penalty_grace_period_missed_installments")
|
||||
private boolean penaltyGracePeriodMissedInstallments = false;
|
||||
|
||||
@Column(name = "panel_request_applicable")
|
||||
private boolean panelRequestApplicable = false;
|
||||
|
||||
@Column(name = "no_penalty_nominee_heir_closure")
|
||||
private boolean noPenaltyNomineeHeirClosure = false;
|
||||
|
||||
@Column(name = "deceased_settlement")
|
||||
private boolean deceasedSettlement = false;
|
||||
|
||||
@Column(name = "allow_debit_txn_after_due_date")
|
||||
private boolean allowDebitTxnAfterDueDate = false;
|
||||
|
||||
// Relationships for complex objects
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "savingsProduct", orphanRemoval = true)
|
||||
private SavingsProductFacilityConfig facilityConfig;
|
||||
@@ -750,20 +796,26 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
|
||||
actualChanges.put(productCodeParamName, newValue);
|
||||
this.productCode = newValue;
|
||||
}
|
||||
if (command.isChangeInLocalDateParameterNamed(effectiveDateParamName, this.effectiveDate)) {
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(effectiveDateParamName);
|
||||
actualChanges.put(effectiveDateParamName, newValue);
|
||||
this.effectiveDate = newValue;
|
||||
if (command.parameterExists(effectiveDateParamName)) {
|
||||
final LocalDate newValue = parseCbsLocalDate(command.stringValueOfParameterNamed(effectiveDateParamName));
|
||||
if (!Objects.equals(newValue, this.effectiveDate)) {
|
||||
actualChanges.put(effectiveDateParamName, newValue);
|
||||
this.effectiveDate = newValue;
|
||||
}
|
||||
}
|
||||
if (command.isChangeInLocalDateParameterNamed(effectiveToParamName, this.effectiveTo)) {
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(effectiveToParamName);
|
||||
actualChanges.put(effectiveToParamName, newValue);
|
||||
this.effectiveTo = newValue;
|
||||
if (command.parameterExists(effectiveToParamName)) {
|
||||
final LocalDate newValue = parseCbsLocalDate(command.stringValueOfParameterNamed(effectiveToParamName));
|
||||
if (!Objects.equals(newValue, this.effectiveTo)) {
|
||||
actualChanges.put(effectiveToParamName, newValue);
|
||||
this.effectiveTo = newValue;
|
||||
}
|
||||
}
|
||||
if (command.isChangeInLocalDateParameterNamed(endDateParamName, this.endDate)) {
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(endDateParamName);
|
||||
actualChanges.put(endDateParamName, newValue);
|
||||
this.endDate = newValue;
|
||||
if (command.parameterExists(endDateParamName)) {
|
||||
final LocalDate newValue = parseCbsLocalDate(command.stringValueOfParameterNamed(endDateParamName));
|
||||
if (!Objects.equals(newValue, this.endDate)) {
|
||||
actualChanges.put(endDateParamName, newValue);
|
||||
this.endDate = newValue;
|
||||
}
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed(introducerRequiredParamName, this.introducerRequired)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(introducerRequiredParamName);
|
||||
@@ -805,10 +857,12 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
|
||||
actualChanges.put(frequencyFixDayParamName, newValue);
|
||||
this.frequencyFixDay = newValue;
|
||||
}
|
||||
if (command.isChangeInLocalDateParameterNamed(lastInterestDateParamName, this.lastInterestDate)) {
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(lastInterestDateParamName);
|
||||
actualChanges.put(lastInterestDateParamName, newValue);
|
||||
this.lastInterestDate = newValue;
|
||||
if (command.parameterExists(lastInterestDateParamName)) {
|
||||
final LocalDate newValue = parseCbsLocalDate(command.stringValueOfParameterNamed(lastInterestDateParamName));
|
||||
if (!Objects.equals(newValue, this.lastInterestDate)) {
|
||||
actualChanges.put(lastInterestDateParamName, newValue);
|
||||
this.lastInterestDate = newValue;
|
||||
}
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed(enableLockinPeriodParamName, this.enableLockinPeriod)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(enableLockinPeriodParamName);
|
||||
@@ -830,6 +884,64 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
|
||||
this.accountClosedZeroBalanceInDayClosing = newValue;
|
||||
}
|
||||
|
||||
// Update RD-specific flags (0270 migration fields)
|
||||
if (command.isChangeInBooleanParameterNamed("balanceSwapOption", this.balanceSwapOption)) {
|
||||
this.balanceSwapOption = command.booleanPrimitiveValueOfParameterNamed("balanceSwapOption");
|
||||
actualChanges.put("balanceSwapOption", this.balanceSwapOption);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("msme", this.msme)) {
|
||||
this.msme = command.booleanPrimitiveValueOfParameterNamed("msme");
|
||||
actualChanges.put("msme", this.msme);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("autoDebitFacility", this.autoDebitFacility)) {
|
||||
this.autoDebitFacility = command.booleanPrimitiveValueOfParameterNamed("autoDebitFacility");
|
||||
actualChanges.put("autoDebitFacility", this.autoDebitFacility);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("loanFacility", this.loanFacility)) {
|
||||
this.loanFacility = command.booleanPrimitiveValueOfParameterNamed("loanFacility");
|
||||
actualChanges.put("loanFacility", this.loanFacility);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("prematureWithdrawalOption", this.prematureWithdrawalOption)) {
|
||||
this.prematureWithdrawalOption = command.booleanPrimitiveValueOfParameterNamed("prematureWithdrawalOption");
|
||||
actualChanges.put("prematureWithdrawalOption", this.prematureWithdrawalOption);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("tenureFlexibility", this.tenureFlexibility)) {
|
||||
this.tenureFlexibility = command.booleanPrimitiveValueOfParameterNamed("tenureFlexibility");
|
||||
actualChanges.put("tenureFlexibility", this.tenureFlexibility);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("lienMarked", this.lienMarked)) {
|
||||
this.lienMarked = command.booleanPrimitiveValueOfParameterNamed("lienMarked");
|
||||
actualChanges.put("lienMarked", this.lienMarked);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("maturityInstructions", this.maturityInstructions)) {
|
||||
this.maturityInstructions = command.booleanPrimitiveValueOfParameterNamed("maturityInstructions");
|
||||
actualChanges.put("maturityInstructions", this.maturityInstructions);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("creditMaturityProceeds", this.creditMaturityProceeds)) {
|
||||
this.creditMaturityProceeds = command.booleanPrimitiveValueOfParameterNamed("creditMaturityProceeds");
|
||||
actualChanges.put("creditMaturityProceeds", this.creditMaturityProceeds);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("penaltyGracePeriodMissedInstallments", this.penaltyGracePeriodMissedInstallments)) {
|
||||
this.penaltyGracePeriodMissedInstallments = command.booleanPrimitiveValueOfParameterNamed("penaltyGracePeriodMissedInstallments");
|
||||
actualChanges.put("penaltyGracePeriodMissedInstallments", this.penaltyGracePeriodMissedInstallments);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("panelRequestApplicable", this.panelRequestApplicable)) {
|
||||
this.panelRequestApplicable = command.booleanPrimitiveValueOfParameterNamed("panelRequestApplicable");
|
||||
actualChanges.put("panelRequestApplicable", this.panelRequestApplicable);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("noPenaltyNomineeHeirClosure", this.noPenaltyNomineeHeirClosure)) {
|
||||
this.noPenaltyNomineeHeirClosure = command.booleanPrimitiveValueOfParameterNamed("noPenaltyNomineeHeirClosure");
|
||||
actualChanges.put("noPenaltyNomineeHeirClosure", this.noPenaltyNomineeHeirClosure);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("deceasedSettlement", this.deceasedSettlement)) {
|
||||
this.deceasedSettlement = command.booleanPrimitiveValueOfParameterNamed("deceasedSettlement");
|
||||
actualChanges.put("deceasedSettlement", this.deceasedSettlement);
|
||||
}
|
||||
if (command.isChangeInBooleanParameterNamed("allowDebitTxnAfterDueDate", this.allowDebitTxnAfterDueDate)) {
|
||||
this.allowDebitTxnAfterDueDate = command.booleanPrimitiveValueOfParameterNamed("allowDebitTxnAfterDueDate");
|
||||
actualChanges.put("allowDebitTxnAfterDueDate", this.allowDebitTxnAfterDueDate);
|
||||
}
|
||||
|
||||
validateLockinDetails();
|
||||
esnureOverdraftLimitsSetForOverdraftAccounts();
|
||||
|
||||
@@ -1142,6 +1254,48 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
|
||||
this.accountClosedZeroBalanceInDayClosing = accountClosedZeroBalanceInDayClosing;
|
||||
}
|
||||
|
||||
public boolean isBalanceSwapOption() { return balanceSwapOption; }
|
||||
public void setBalanceSwapOption(boolean v) { this.balanceSwapOption = v; }
|
||||
|
||||
public boolean isMsme() { return msme; }
|
||||
public void setMsme(boolean v) { this.msme = v; }
|
||||
|
||||
public boolean isAutoDebitFacility() { return autoDebitFacility; }
|
||||
public void setAutoDebitFacility(boolean v) { this.autoDebitFacility = v; }
|
||||
|
||||
public boolean isLoanFacility() { return loanFacility; }
|
||||
public void setLoanFacility(boolean v) { this.loanFacility = v; }
|
||||
|
||||
public boolean isPrematureWithdrawalOption() { return prematureWithdrawalOption; }
|
||||
public void setPrematureWithdrawalOption(boolean v) { this.prematureWithdrawalOption = v; }
|
||||
|
||||
public boolean isTenureFlexibility() { return tenureFlexibility; }
|
||||
public void setTenureFlexibility(boolean v) { this.tenureFlexibility = v; }
|
||||
|
||||
public boolean isLienMarked() { return lienMarked; }
|
||||
public void setLienMarked(boolean v) { this.lienMarked = v; }
|
||||
|
||||
public boolean isMaturityInstructions() { return maturityInstructions; }
|
||||
public void setMaturityInstructions(boolean v) { this.maturityInstructions = v; }
|
||||
|
||||
public boolean isCreditMaturityProceeds() { return creditMaturityProceeds; }
|
||||
public void setCreditMaturityProceeds(boolean v) { this.creditMaturityProceeds = v; }
|
||||
|
||||
public boolean isPenaltyGracePeriodMissedInstallments() { return penaltyGracePeriodMissedInstallments; }
|
||||
public void setPenaltyGracePeriodMissedInstallments(boolean v) { this.penaltyGracePeriodMissedInstallments = v; }
|
||||
|
||||
public boolean isPanelRequestApplicable() { return panelRequestApplicable; }
|
||||
public void setPanelRequestApplicable(boolean v) { this.panelRequestApplicable = v; }
|
||||
|
||||
public boolean isNoPenaltyNomineeHeirClosure() { return noPenaltyNomineeHeirClosure; }
|
||||
public void setNoPenaltyNomineeHeirClosure(boolean v) { this.noPenaltyNomineeHeirClosure = v; }
|
||||
|
||||
public boolean isDeceasedSettlement() { return deceasedSettlement; }
|
||||
public void setDeceasedSettlement(boolean v) { this.deceasedSettlement = v; }
|
||||
|
||||
public boolean isAllowDebitTxnAfterDueDate() { return allowDebitTxnAfterDueDate; }
|
||||
public void setAllowDebitTxnAfterDueDate(boolean v) { this.allowDebitTxnAfterDueDate = v; }
|
||||
|
||||
public SavingsProductFacilityConfig getFacilityConfig() {
|
||||
return facilityConfig;
|
||||
}
|
||||
@@ -1174,4 +1328,25 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses CBS date strings (e.g. "17 April 2026") without requiring a dateFormat parameter.
|
||||
*/
|
||||
public static LocalDate parseCbsLocalDate(final String dateStr) {
|
||||
if (dateStr == null || dateStr.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
for (DateTimeFormatter fmt : Arrays.asList(
|
||||
DateTimeFormatter.ofPattern("dd MMMM yyyy", java.util.Locale.ENGLISH),
|
||||
DateTimeFormatter.ofPattern("dd MMM yyyy", java.util.Locale.ENGLISH),
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd", java.util.Locale.ENGLISH),
|
||||
DateTimeFormatter.ofPattern("dd/MM/yyyy", java.util.Locale.ENGLISH))) {
|
||||
try {
|
||||
return LocalDate.parse(dateStr, fmt);
|
||||
} catch (DateTimeParseException ignored) {
|
||||
// try next
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user