update current product code and RD code

This commit is contained in:
Nitesh Yadav
2026-04-15 20:34:12 +05:30
parent d7821c7824
commit 2facd3744d
48 changed files with 32602 additions and 1551 deletions
+1
View File
@@ -0,0 +1 @@
{"sessionId":"0fda6e48-a79d-4980-a9ee-31a6d5f735e1","pid":15548,"acquiredAt":1776168814856}
+1 -1
View File
@@ -49,7 +49,7 @@
"hooks": [
{
"type": "command",
"command": "python3 \"%CLAUDE_PROJECT_DIR%/.claude/hooks/post-tool-call.py\""
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-call.py\""
}
]
}
+1
View File
@@ -41,6 +41,7 @@ buildscript {
'fineract-loan-origination',
'fineract-loan',
'fineract-savings',
'fineract-current-product',
'fineract-report',
'fineract-mix',
'integration-tests',
@@ -115,6 +115,14 @@ public interface ChargeReadPlatformService {
/** Retrieve savings account charges **/
List<ChargeData> retrieveSavingsAccountApplicableCharges(Long savingsId);
/**
* Returns all charges applicable for a given current product
*
* @param currentProductId
* @return
*/
List<ChargeData> retrieveCurrentProductCharges(Long currentProductId);
/**
* Returns charge definitions which are applicable for shares
*/
@@ -28,6 +28,10 @@ public class SavingsApiConstants {
public static final String YES_NO = "YesNo";
public static final String SAVINGS_PRODUCT_ACCOUNT_TYPE = "SavingsProductAccountType";
public static final String SAVINGS_PRODUCT_CUSTOMER_SEGMENT = "SavingsProductCustomerSegment";
public static final String CURRENT_PRODUCT_ACCOUNT_TYPE = "CurrentProductAccountType";
public static final String CURRENT_PRODUCT_CUSTOMER_SEGMENT = "CurrentProductCustomerSegment";
public static final String RD_PRODUCT_ACCOUNT_TYPE = "RecurringDepositProductAccountType";
public static final String RD_PRODUCT_CUSTOMER_SEGMENT = "RecurringDepositProductCustomerSegment";
public static final String PERIOD_FREQUENCY = "PeriodFrequency";
public static final String INTEREST_CALCULATION_TYPE_OPTION = "InterestCalculationTypeOption";
@@ -268,4 +272,12 @@ public class SavingsApiConstants {
// Additional custom fields
public static final String balanceFrequencyParamName = "balanceFrequency";
public static final String accountClosedZeroBalanceInDayClosingParamName = "accountClosedZeroBalanceInDayClosing";
// Current Product — new UI fields
public static final String balanceSwapOptionParamName = "balanceSwapOption";
public static final String msmeParamName = "msme";
public static final String autoDebitFacilityParamName = "autoDebitFacility";
public static final String avgBalanceCalcPeriodParamName = "avgBalanceCalcPeriod";
public static final String validForPeriodParamName = "validForPeriod";
public static final String odAccountClosedZeroBalanceInDayClosingParamName = "odAccountClosedZeroBalanceInDayClosing";
}
@@ -142,10 +142,19 @@ public final class SavingsProductData implements Serializable {
@Getter @Setter private String balanceFrequency;
@Getter @Setter private Boolean accountClosedZeroBalanceInDayClosing;
// New current product fields
@Getter @Setter private Boolean balanceSwapOption;
@Getter @Setter private Boolean msme;
@Getter @Setter private Boolean autoDebitFacility;
@Getter @Setter private String avgBalanceCalcPeriod;
@Getter @Setter private String validForPeriod;
@Getter @Setter private Boolean odAccountClosedZeroBalanceInDayClosing;
// Complex objects wrapped as Maps/Lists so Jackson converts them into the exact JSON your UI expects
@Getter @Setter private Map<String, Object> accountFacilityConfig;
@Getter @Setter private Collection<Map<String, Object>> accountFacilityLimits;
@Getter @Setter private Map<String, Object> depositLimitKyc;
@Getter @Setter private Map<String, List<Map<String, Object>>> otherGlMappingsMap;
public static SavingsProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
@@ -358,6 +367,13 @@ public final class SavingsProductData implements Serializable {
target.setEnableLockinPeriod(source.getEnableLockinPeriod());
target.setBalanceFrequency(source.getBalanceFrequency());
target.setAccountClosedZeroBalanceInDayClosing(source.getAccountClosedZeroBalanceInDayClosing());
target.setBalanceSwapOption(source.getBalanceSwapOption());
target.setMsme(source.getMsme());
target.setAutoDebitFacility(source.getAutoDebitFacility());
target.setAvgBalanceCalcPeriod(source.getAvgBalanceCalcPeriod());
target.setValidForPeriod(source.getValidForPeriod());
target.setOdAccountClosedZeroBalanceInDayClosing(source.getOdAccountClosedZeroBalanceInDayClosing());
target.setOtherGlMappingsMap(source.getOtherGlMappingsMap());
target.setAccountFacilityConfig(source.getAccountFacilityConfig());
target.setAccountFacilityLimits(source.getAccountFacilityLimits());
target.setDepositLimitKyc(source.getDepositLimitKyc());
+68
View File
@@ -0,0 +1,68 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'Fineract Current Product'
compileJava {
dependsOn ':fineract-avro-schemas:buildJavaSdk'
}
configurations {
providedRuntime // needed for Spring Boot executable WAR
providedCompile
compile() {
exclude module: 'hibernate-entitymanager'
exclude module: 'hibernate-validator'
exclude module: 'activation'
exclude module: 'bcmail-jdk14'
exclude module: 'bcprov-jdk14'
exclude module: 'bctsp-jdk14'
exclude module: 'c3p0'
exclude module: 'stax-api'
exclude module: 'jaxb-api'
exclude module: 'jaxb-impl'
exclude module: 'jboss-logging'
exclude module: 'itext-rtf'
exclude module: 'classworlds'
}
runtime
}
apply from: 'dependencies.gradle'
// If we are running Gradle within Eclipse to enhance classes with OpenJPA,
// set the classes directory to point to Eclipse's default build directory
if (project.hasProperty('env') && project.getProperty('env') == 'eclipse') {
sourceSets.main.java.outputDir = new File(rootProject.projectDir, "fineract-current-product/bin/main")
}
eclipse {
project {
buildCommand([ LaunchConfigHandle: "<project>/.externalToolBuilders/OpenJPA Enhance Builder.launch" ], 'org.eclipse.ui.externaltools.ExternalToolBuilder')
}
}
if (!(project.hasProperty('env') && project.getProperty('env') == 'dev')) {
sourceSets {
test {
java {
exclude '**/core/boot/tests/**'
}
}
}
}
@@ -0,0 +1,64 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
dependencies {
// Never use "compile" scope, but make all dependencies either 'implementation', 'runtimeOnly' or 'testCompile'.
implementation(project(path: ':fineract-core'))
implementation(project(path: ':fineract-accounting'))
implementation(project(path: ':fineract-charge'))
implementation(project(path: ':fineract-tax'))
implementation(project(path: ':fineract-savings'))
implementation(
'org.springframework.boot:spring-boot-starter-web',
'org.springframework.boot:spring-boot-starter-security',
'jakarta.ws.rs:jakarta.ws.rs-api',
'com.google.guava:guava',
'com.google.code.gson:gson',
'org.apache.commons:commons-lang3',
'com.jayway.jsonpath:json-path',
'com.github.spotbugs:spotbugs-annotations',
'io.swagger.core.v3:swagger-annotations-jakarta',
'org.mapstruct:mapstruct',
)
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.mapstruct:mapstruct-processor'
implementation ('org.springframework.boot:spring-boot-starter-data-jpa') {
exclude group: 'org.hibernate'
}
implementation('org.eclipse.persistence:org.eclipse.persistence.jpa') {
exclude group: 'org.eclipse.persistence', module: 'jakarta.persistence'
}
testImplementation( 'io.github.classgraph:classgraph' )
testImplementation ('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'com.jayway.jsonpath', module: 'json-path'
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
exclude group: 'jakarta.activation'
exclude group: 'javax.activation'
exclude group: 'org.skyscreamer'
}
testImplementation ('org.mockito:mockito-inline')
}
@@ -0,0 +1,460 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.data;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeAmountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingValidations;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
import org.apache.fineract.portfolio.savings.data.SavingsProductAccountingDataValidator;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class CurrentProductDataValidator {
private final FromJsonHelper fromApiJsonHelper;
private final SavingsProductAccountingDataValidator savingsProductAccountingDataValidator;
private static final Set<String> CURRENT_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(
SavingsApiConstants.localeParamName, SavingsApiConstants.dateFormatParamName,
SavingsApiConstants.monthDayFormatParamName, nameParamName, shortNameParamName,
descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, inMultiplesOfParamName,
nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName,
interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName,
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
SavingsApiConstants.withdrawalFeeAmountParamName, SavingsApiConstants.withdrawalFeeTypeParamName,
withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName,
SavingsApiConstants.accountingRuleParamName, SavingsApiConstants.chargesParamName,
SavingsApiConstants.otherGlMappingsParamName,
SavingProductAccountingParams.INCOME_FROM_FEES.getValue(),
SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(),
SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(),
SavingProductAccountingParams.PENALTIES_RECEIVABLE.getValue(),
SavingProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
SavingProductAccountingParams.SAVINGS_CONTROL.getValue(),
SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(),
SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(),
SavingProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
SavingProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(),
SavingProductAccountingParams.FEES_RECEIVABLE.getValue(),
SavingProductAccountingParams.INTEREST_PAYABLE.getValue(),
SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(),
SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue(),
SavingProductAccountingParams.INTEREST_RECEIVABLE.getValue(),
isDormancyTrackingActiveParamName, daysToDormancyParamName, daysToInactiveParamName,
daysToEscheatParamName, allowOverdraftParamName, overdraftLimitParamName,
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
SavingsApiConstants.minRequiredBalanceParamName, SavingsApiConstants.enforceMinRequiredBalanceParamName,
SavingsApiConstants.maxAllowedLienLimitParamName, SavingsApiConstants.lienAllowedParamName,
minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName,
SavingsApiConstants.accountTypeParamName, SavingsApiConstants.customerSegmentParamName,
SavingsApiConstants.productCodeParamName, SavingsApiConstants.effectiveDateParamName,
SavingsApiConstants.effectiveToParamName, SavingsApiConstants.endDateParamName,
SavingsApiConstants.introducerRequiredParamName, SavingsApiConstants.shareLinkingEnabledParamName,
SavingsApiConstants.nominationFacilityParamName, SavingsApiConstants.dormancyAlertParamName,
SavingsApiConstants.interestCalculateSelectorParamName, SavingsApiConstants.averagePeriodParamName,
SavingsApiConstants.autoPostParamName, SavingsApiConstants.frequencyFixDayParamName,
SavingsApiConstants.lastInterestDateParamName, SavingsApiConstants.enableLockinPeriodParamName,
SavingsApiConstants.accountFacilityConfigParamName, SavingsApiConstants.accountFacilityLimitsParamName,
SavingsApiConstants.depositLimitKycParamName,
SavingsApiConstants.balanceFrequencyParamName, SavingsApiConstants.accountClosedZeroBalanceInDayClosingParamName,
SavingsApiConstants.balanceSwapOptionParamName, SavingsApiConstants.msmeParamName,
SavingsApiConstants.autoDebitFacilityParamName, SavingsApiConstants.avgBalanceCalcPeriodParamName,
SavingsApiConstants.validForPeriodParamName, SavingsApiConstants.odAccountClosedZeroBalanceInDayClosingParamName));
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CURRENT_PRODUCT_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(CURRENT_PRODUCT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element);
baseDataValidator.reset().parameter(nameParamName).value(name).notBlank().notExceedingLengthOf(100);
final String shortName = this.fromApiJsonHelper.extractStringNamed(shortNameParamName, element);
baseDataValidator.reset().parameter(shortNameParamName).value(shortName).notBlank().notExceedingLengthOf(4);
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element);
baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank();
final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element);
baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
if (this.fromApiJsonHelper.parameterExists(inMultiplesOfParamName, element)) {
final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerNamed(inMultiplesOfParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(inMultiplesOfParamName).value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) {
final BigDecimal nominalAnnualInterestRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate).zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(interestCompoundingPeriodTypeParamName, element)) {
final Integer interestCompoundingPeriodType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType)
.isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
}
final Integer interestPostingPeriodType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPostingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestPostingPeriodTypeParamName).value(interestPostingPeriodType).notNull()
.isOneOfTheseValues(SavingsPostingInterestPeriodType.integerValues());
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
final Integer interestCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName, element);
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull();
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) {
final Integer interestCalculationDaysInYearType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationDaysInYearTypeParamName, element);
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType)
.isOneOfTheseValues(SavingsInterestCalculationDaysInYearType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) {
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
baseDataValidator.reset().parameter(descriptionParamName).value(description).ignoreIfNull().notExceedingLengthOf(500);
}
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName, element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater();
if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull().inMinMaxRange(0, 3);
}
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
if (lockinPeriodFrequencyType != null) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull().integerZeroOrGreater();
}
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper.extractBooleanNamed(withdrawalFeeForTransfersParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers).ignoreIfNull().validateForBooleanValue();
}
// dormancy
Boolean isDormancyActive = this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element);
if (isDormancyActive == null) {
isDormancyActive = false;
}
if (isDormancyActive) {
final Long daysToInact = this.fromApiJsonHelper.extractLongNamed(daysToInactiveParamName, element);
baseDataValidator.reset().parameter(daysToInactiveParamName).value(daysToInact).notNull().longGreaterThanZero();
final Long daysToDor = this.fromApiJsonHelper.extractLongNamed(daysToDormancyParamName, element);
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).notNull().longGreaterThanZero();
final Long daysToEsc = this.fromApiJsonHelper.extractLongNamed(daysToEscheatParamName, element);
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).notNull().longGreaterThanZero();
if (null != daysToInact && null != daysToDor && null != daysToEsc) {
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).longGreaterThanNumber(daysToInact);
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).longGreaterThanNumber(daysToDor);
}
}
if (this.fromApiJsonHelper.parameterExists(SavingsApiConstants.effectiveToParamName, element)
&& this.fromApiJsonHelper.parameterExists(SavingsApiConstants.effectiveDateParamName, element)) {
final java.time.LocalDate effectiveDate = this.fromApiJsonHelper.extractLocalDateNamed(SavingsApiConstants.effectiveDateParamName, element);
final java.time.LocalDate effectiveTo = this.fromApiJsonHelper.extractLocalDateNamed(SavingsApiConstants.effectiveToParamName, element);
if (effectiveDate != null && effectiveTo != null && effectiveTo.isBefore(effectiveDate)) {
baseDataValidator.reset().parameter(SavingsApiConstants.effectiveToParamName).value(effectiveTo)
.failWithCode("error.msg.effectiveto.cannot.be.before.effectivedate");
}
}
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (AccountingValidations.isCashBasedAccounting(accountingRuleType)
|| AccountingValidations.isAccrualPeriodicBasedAccounting(accountingRuleType)) {
savingsProductAccountingDataValidator.evaluateProductAccountingData(accountingRuleType, isDormancyActive, element,
baseDataValidator, DepositAccountType.SAVINGS_DEPOSIT, true);
}
validateOverdraftParams(baseDataValidator, element);
if (this.fromApiJsonHelper.parameterExists(minBalanceForInterestCalculationParamName, element)) {
final BigDecimal minBalanceForInterestCalculation = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minBalanceForInterestCalculationParamName, element);
baseDataValidator.reset().parameter(minBalanceForInterestCalculationParamName).value(minBalanceForInterestCalculation).ignoreIfNull().zeroOrPositiveAmount();
}
validateTaxWithHoldingParams(baseDataValidator, element, true);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(final String json, final CurrentProduct product) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CURRENT_PRODUCT_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(CURRENT_PRODUCT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
if (this.fromApiJsonHelper.parameterExists(nameParamName, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element);
baseDataValidator.reset().parameter(nameParamName).value(name).notBlank().notExceedingLengthOf(100);
}
if (this.fromApiJsonHelper.parameterExists(shortNameParamName, element)) {
final String shortName = this.fromApiJsonHelper.extractStringNamed(shortNameParamName, element);
baseDataValidator.reset().parameter(shortNameParamName).value(shortName).notBlank().notExceedingLengthOf(4);
}
if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) {
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
baseDataValidator.reset().parameter(descriptionParamName).value(description).notBlank().notExceedingLengthOf(500);
}
if (this.fromApiJsonHelper.parameterExists(currencyCodeParamName, element)) {
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element);
baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank();
}
if (this.fromApiJsonHelper.parameterExists(digitsAfterDecimalParamName, element)) {
final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element);
baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
}
if (this.fromApiJsonHelper.parameterExists(inMultiplesOfParamName, element)) {
final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerNamed(inMultiplesOfParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(inMultiplesOfParamName).value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) {
final BigDecimal interestRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(interestRate).notNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(interestCompoundingPeriodTypeParamName, element)) {
final Integer interestCompoundingPeriodType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull()
.isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
final Integer interestCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName, element);
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull().inMinMaxRange(1, 2);
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) {
final Integer interestCalculationDaysInYearType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationDaysInYearTypeParamName, element);
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType).notNull().isOneOfTheseValues(360, 365);
}
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName, element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull().integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper.extractBooleanNamed(withdrawalFeeForTransfersParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers).ignoreIfNull().validateForBooleanValue();
}
Integer accountingRuleType = product.getAccountingType();
if (this.fromApiJsonHelper.parameterExists("accountingRule", element)) {
accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
}
final Boolean isDormancyActive = this.fromApiJsonHelper.parameterExists(isDormancyTrackingActiveParamName, element)
? this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element)
: product.isDormancyTrackingActive();
if (AccountingValidations.isCashBasedAccounting(accountingRuleType)
|| AccountingValidations.isAccrualPeriodicBasedAccounting(accountingRuleType)) {
savingsProductAccountingDataValidator.evaluateProductAccountingData(accountingRuleType, isDormancyActive, element,
baseDataValidator, DepositAccountType.SAVINGS_DEPOSIT, false);
}
if (null != isDormancyActive && isDormancyActive) {
final Long daysToInact = this.fromApiJsonHelper.parameterExists(daysToInactiveParamName, element)
? this.fromApiJsonHelper.extractLongNamed(daysToInactiveParamName, element)
: product.getDaysToInactive();
baseDataValidator.reset().parameter(daysToInactiveParamName).value(daysToInact).notNull().longGreaterThanZero();
final Long daysToDor = this.fromApiJsonHelper.parameterExists(daysToDormancyParamName, element)
? this.fromApiJsonHelper.extractLongNamed(daysToDormancyParamName, element)
: product.getDaysToDormancy();
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).notNull().longGreaterThanZero();
final Long daysToEsc = this.fromApiJsonHelper.parameterExists(daysToEscheatParamName, element)
? this.fromApiJsonHelper.extractLongNamed(daysToEscheatParamName, element)
: product.getDaysToEscheat();
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).notNull().longGreaterThanZero();
if (null != daysToInact && null != daysToDor && null != daysToEsc) {
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).longGreaterThanNumber(daysToInact);
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).longGreaterThanNumber(daysToDor);
}
}
validateOverdraftParams(baseDataValidator, element);
if (this.fromApiJsonHelper.parameterExists(minBalanceForInterestCalculationParamName, element)) {
final BigDecimal minBalanceForInterestCalculation = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minBalanceForInterestCalculationParamName, element);
baseDataValidator.reset().parameter(minBalanceForInterestCalculationParamName).value(minBalanceForInterestCalculation).ignoreIfNull().zeroOrPositiveAmount();
}
validateTaxWithHoldingParams(baseDataValidator, element, false);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
private void validateOverdraftParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(allowOverdraftParamName, element)) {
final Boolean allowOverdraft = this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element);
baseDataValidator.reset().parameter(allowOverdraftParamName).value(allowOverdraft).ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(overdraftLimitParamName, element)) {
final BigDecimal overdraftLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(overdraftLimitParamName, element);
baseDataValidator.reset().parameter(overdraftLimitParamName).value(overdraftLimit).ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateOverdraftParamName, element)) {
final BigDecimal nominalAnnualInterestRateOverdraft = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateOverdraftParamName, element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateOverdraftParamName).value(nominalAnnualInterestRateOverdraft).ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(minOverdraftForInterestCalculationParamName, element)) {
final BigDecimal minOverdraftForInterestCalculation = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minOverdraftForInterestCalculationParamName, element);
baseDataValidator.reset().parameter(minOverdraftForInterestCalculationParamName).value(minOverdraftForInterestCalculation).ignoreIfNull().zeroOrPositiveAmount();
}
}
private void validateLienParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(lienAllowedParamName, element)) {
final Boolean lienAllowed = this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element);
baseDataValidator.reset().parameter(lienAllowedParamName).value(lienAllowed).ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(maxAllowedLienLimitParamName, element)) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName, element);
baseDataValidator.reset().parameter(maxAllowedLienLimitParamName).value(maxAllowedLienLimit).ignoreIfNull().zeroOrPositiveAmount();
}
if (BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))
&& BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element))) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName, element);
final BigDecimal overdraftLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(overdraftLimitParamName, element);
if (overdraftLimit.compareTo(maxAllowedLienLimit) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("Overdraft.limit.can.not.be.greater.than.lien.limit");
}
}
}
private void validateTaxWithHoldingParams(final DataValidatorBuilder baseDataValidator, final JsonElement element, final boolean isCreate) {
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
}
Boolean withHoldTax = this.fromApiJsonHelper.extractBooleanNamed(withHoldTaxParamName, element);
if (withHoldTax == null) {
withHoldTax = false;
}
if (this.fromApiJsonHelper.parameterExists(taxGroupIdParamName, element)) {
final Long taxGroupId = this.fromApiJsonHelper.extractLongNamed(taxGroupIdParamName, element);
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).ignoreIfNull().longGreaterThanZero();
if (withHoldTax) {
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
}
} else if (withHoldTax && isCreate) {
final Long taxGroupId = null;
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
}
}
}
@@ -0,0 +1,868 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.localeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.otherGlMappingsParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.customerSegmentParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.productCodeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.effectiveDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.effectiveToParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.endDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerRequiredParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shareLinkingEnabledParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominationFacilityParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dormancyAlertParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculateSelectorParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.averagePeriodParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.autoPostParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.frequencyFixDayParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lastInterestDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enableLockinPeriodParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.balanceSwapOptionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.msmeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.autoDebitFacilityParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.avgBalanceCalcPeriodParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.validForPeriodParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.odAccountClosedZeroBalanceInDayClosingParamName;
import com.google.gson.JsonArray;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "m_current_product", uniqueConstraints = {
@UniqueConstraint(columnNames = { "name" }, name = "cp_unq_name"),
@UniqueConstraint(columnNames = { "short_name" }, name = "cp_unq_short_name") })
public class CurrentProduct extends AbstractPersistableCustom<Long> {
@Column(name = "name", nullable = false, unique = true)
private String name;
@Column(name = "short_name", nullable = false, unique = true)
private String shortName;
@Column(name = "description", length = 500, nullable = false)
private String description;
@Embedded
private MonetaryCurrency currency;
@Column(name = "nominal_annual_interest_rate", scale = 6, precision = 19, nullable = false)
private BigDecimal nominalAnnualInterestRate;
@Column(name = "interest_compounding_period_enum", nullable = false)
private Integer interestCompoundingPeriodType;
@Column(name = "interest_posting_period_enum", nullable = false)
private Integer interestPostingPeriodType;
@Column(name = "interest_calculation_type_enum", nullable = false)
private Integer interestCalculationType;
@Column(name = "interest_calculation_days_in_year_type_enum", nullable = false)
private Integer interestCalculationDaysInYearType;
@Column(name = "min_required_opening_balance", scale = 6, precision = 19, nullable = true)
private BigDecimal minRequiredOpeningBalance;
@Column(name = "lockin_period_frequency", nullable = true)
private Integer lockinPeriodFrequency;
@Column(name = "lockin_period_frequency_enum", nullable = true)
private Integer lockinPeriodFrequencyType;
@Column(name = "accounting_type", nullable = false)
private Integer accountingRule;
@Column(name = "withdrawal_fee_for_transfer")
private boolean withdrawalFeeApplicableForTransfer;
@ManyToMany
@JoinTable(name = "m_current_product_charge",
joinColumns = @JoinColumn(name = "current_product_id"),
inverseJoinColumns = @JoinColumn(name = "charge_id"))
private Set<Charge> charges;
@Column(name = "allow_overdraft")
private boolean allowOverdraft;
@Column(name = "overdraft_limit", scale = 6, precision = 19, nullable = true)
private BigDecimal overdraftLimit;
@Column(name = "nominal_annual_interest_rate_overdraft", scale = 6, precision = 19, nullable = true)
private BigDecimal nominalAnnualInterestRateOverdraft;
@Column(name = "min_overdraft_for_interest_calculation", scale = 6, precision = 19, nullable = true)
private BigDecimal minOverdraftForInterestCalculation;
@Column(name = "enforce_min_required_balance")
private boolean enforceMinRequiredBalance;
@Column(name = "min_required_balance", scale = 6, precision = 19, nullable = true)
private BigDecimal minRequiredBalance;
@Column(name = "is_lien_allowed", nullable = false)
private boolean lienAllowed;
@Column(name = "max_allowed_lien_limit", scale = 6, precision = 19, nullable = true)
private BigDecimal maxAllowedLienLimit;
@Column(name = "min_balance_for_interest_calculation", scale = 6, precision = 19, nullable = true)
private BigDecimal minBalanceForInterestCalculation;
@Column(name = "withhold_tax", nullable = false)
private boolean withHoldTax;
@ManyToOne
@JoinColumn(name = "tax_group_id")
private TaxGroup taxGroup;
@Column(name = "is_dormancy_tracking_active")
private Boolean isDormancyTrackingActive;
@Column(name = "days_to_inactive")
private Long daysToInactive;
@Column(name = "days_to_dormancy")
private Long daysToDormancy;
@Column(name = "days_to_escheat")
private Long daysToEscheat;
@Lob
@Column(name = "other_gl_mappings")
private String otherGlMappings;
// Enhanced fields
@Column(name = "account_type", length = 50)
private String accountType;
@Column(name = "customer_segment", length = 50)
private String customerSegment;
@Column(name = "product_code", length = 20, unique = true)
private String productCode;
@Column(name = "effective_date")
private LocalDate effectiveDate;
@Column(name = "effective_to")
private LocalDate effectiveTo;
@Column(name = "end_date")
private LocalDate endDate;
@Column(name = "introducer_required")
private boolean introducerRequired = false;
@Column(name = "share_linking_enabled")
private boolean shareLinkingEnabled = false;
@Column(name = "nomination_facility")
private boolean nominationFacility = false;
@Column(name = "dormancy_alert")
private boolean dormancyAlert = false;
@Column(name = "interest_calculate_selector")
private Integer interestCalculateSelector;
@Column(name = "average_period", length = 20)
private String averagePeriod;
@Column(name = "auto_post")
private boolean autoPost = false;
@Column(name = "frequency_fix_day")
private Integer frequencyFixDay;
@Column(name = "last_interest_date")
private LocalDate lastInterestDate;
@Column(name = "enable_lockin_period")
private boolean enableLockinPeriod = false;
@Column(name = "balance_frequency", length = 50)
private String balanceFrequency;
@Column(name = "account_closed_zero_balance_in_day_closing")
private Boolean accountClosedZeroBalanceInDayClosing = false;
// New UI fields (Currency step)
@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;
// New UI fields (OD section)
@Column(name = "avg_balance_calc_period", length = 10)
private String avgBalanceCalcPeriod;
@Column(name = "valid_for_period", length = 10)
private String validForPeriod;
@Column(name = "od_account_closed_zero_balance_in_day_closing")
private boolean odAccountClosedZeroBalanceInDayClosing = false;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "currentProduct", orphanRemoval = true)
private CurrentProductFacilityConfig facilityConfig;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "currentProduct", orphanRemoval = true)
private Set<CurrentProductFacilityLimit> facilityLimits = new HashSet<>();
@OneToOne(cascade = CascadeType.ALL, mappedBy = "currentProduct", orphanRemoval = true)
private CurrentProductDepositLimitKyc depositLimitKyc;
public static CurrentProduct createNew(final String name, final String shortName, final String description,
final MonetaryCurrency currency, final BigDecimal interestRate,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsPostingInterestPeriodType interestPostingPeriodType,
final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType,
final Set<Charge> charges, final boolean allowOverdraft, final BigDecimal overdraftLimit,
final boolean enforceMinRequiredBalance, final BigDecimal minRequiredBalance,
final boolean lienAllowed, final BigDecimal maxAllowedLienLimit,
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroup taxGroup,
final String otherGlMappings, final Boolean isDormancyTrackingActive, final Long daysToInactive,
final Long daysToDormancy, final Long daysToEscheat) {
return new CurrentProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit,
enforceMinRequiredBalance, minRequiredBalance, lienAllowed, maxAllowedLienLimit,
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation,
withHoldTax, taxGroup, otherGlMappings, isDormancyTrackingActive, daysToInactive, daysToDormancy,
daysToEscheat);
}
protected CurrentProduct() {
this.name = null;
this.description = null;
}
private CurrentProduct(final String name, final String shortName, final String description,
final MonetaryCurrency currency, final BigDecimal interestRate,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsPostingInterestPeriodType interestPostingPeriodType,
final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType,
final Set<Charge> charges, final boolean allowOverdraft, final BigDecimal overdraftLimit,
final boolean enforceMinRequiredBalance, final BigDecimal minRequiredBalance,
final boolean lienAllowed, final BigDecimal maxAllowedLienLimit,
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroup taxGroup,
final String otherGlMappings, final Boolean isDormancyTrackingActive, final Long daysToInactive,
final Long daysToDormancy, final Long daysToEscheat) {
this.name = name;
this.shortName = shortName;
this.description = description;
this.currency = currency;
this.nominalAnnualInterestRate = interestRate;
this.interestCompoundingPeriodType = interestCompoundingPeriodType.getValue();
this.interestPostingPeriodType = interestPostingPeriodType.getValue();
this.interestCalculationType = interestCalculationType.getValue();
this.interestCalculationDaysInYearType = interestCalculationDaysInYearType.getValue();
if (minRequiredOpeningBalance != null) {
this.minRequiredOpeningBalance = Money.of(currency, minRequiredOpeningBalance).getAmount();
}
this.lockinPeriodFrequency = lockinPeriodFrequency;
if (lockinPeriodFrequency != null && lockinPeriodFrequencyType != null) {
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType.getValue();
}
this.withdrawalFeeApplicableForTransfer = withdrawalFeeApplicableForTransfer;
if (accountingRuleType != null) {
this.accountingRule = accountingRuleType.getValue();
}
if (charges != null) {
this.charges = charges;
}
validateLockinDetails();
this.allowOverdraft = allowOverdraft;
this.overdraftLimit = overdraftLimit;
this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft;
this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation;
ensureOverdraftLimitsSetForOverdraftAccounts();
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.minRequiredBalance = minRequiredBalance;
this.lienAllowed = lienAllowed;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.withHoldTax = withHoldTax;
this.taxGroup = taxGroup;
this.otherGlMappings = otherGlMappings;
this.isDormancyTrackingActive = isDormancyTrackingActive == null ? Boolean.FALSE : isDormancyTrackingActive;
this.daysToInactive = daysToInactive;
this.daysToDormancy = daysToDormancy;
this.daysToEscheat = daysToEscheat;
}
private void ensureOverdraftLimitsSetForOverdraftAccounts() {
if (this.allowOverdraft) {
this.overdraftLimit = this.overdraftLimit == null ? BigDecimal.ZERO : this.overdraftLimit;
this.nominalAnnualInterestRateOverdraft = this.nominalAnnualInterestRateOverdraft == null ? BigDecimal.ZERO
: this.nominalAnnualInterestRateOverdraft;
this.minOverdraftForInterestCalculation = this.minOverdraftForInterestCalculation == null ? BigDecimal.ZERO
: this.minOverdraftForInterestCalculation;
}
}
private void validateLockinDetails() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource("currentproduct");
if (this.lockinPeriodFrequency == null) {
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType)
.ignoreIfNull().inMinMaxRange(0, 3);
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public MonetaryCurrency currency() {
return this.currency.copy();
}
public BigDecimal nominalAnnualInterestRate() {
return this.nominalAnnualInterestRate;
}
public Integer getAccountingType() {
return this.accountingRule;
}
public boolean withHoldTax() {
return this.withHoldTax;
}
public TaxGroup getTaxGroup() {
return this.taxGroup;
}
public void setTaxGroup(final TaxGroup taxGroup) {
this.taxGroup = taxGroup;
}
public CurrentProductFacilityConfig getFacilityConfig() {
return this.facilityConfig;
}
public void setFacilityConfig(CurrentProductFacilityConfig facilityConfig) {
this.facilityConfig = facilityConfig;
}
public Set<CurrentProductFacilityLimit> getFacilityLimits() {
return this.facilityLimits;
}
public void setFacilityLimits(Set<CurrentProductFacilityLimit> facilityLimits) {
this.facilityLimits = facilityLimits;
}
public CurrentProductDepositLimitKyc getDepositLimitKyc() {
return this.depositLimitKyc;
}
public void setDepositLimitKyc(CurrentProductDepositLimitKyc depositLimitKyc) {
this.depositLimitKyc = depositLimitKyc;
}
public boolean update(final Set<Charge> newCharges) {
if (newCharges == null) {
return false;
}
final Set<Charge> currentSetOfCharges = this.charges == null ? new HashSet<>() : this.charges;
if (!currentSetOfCharges.equals(newCharges)) {
this.charges = newCharges;
return true;
}
return false;
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
final String localeAsInput = command.locale();
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
final String newValue = command.stringValueOfParameterNamed(nameParamName);
actualChanges.put(nameParamName, newValue);
this.name = newValue;
}
if (command.isChangeInStringParameterNamed(shortNameParamName, this.shortName)) {
final String newValue = command.stringValueOfParameterNamed(shortNameParamName);
actualChanges.put(shortNameParamName, newValue);
this.shortName = newValue;
}
if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) {
final String newValue = command.stringValueOfParameterNamed(descriptionParamName);
actualChanges.put(descriptionParamName, newValue);
this.description = newValue;
}
Integer digitsAfterDecimal = this.currency.getDigitsAfterDecimal();
if (command.isChangeInIntegerParameterNamed(digitsAfterDecimalParamName, digitsAfterDecimal)) {
final Integer newValue = command.integerValueOfParameterNamed(digitsAfterDecimalParamName);
actualChanges.put(digitsAfterDecimalParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
digitsAfterDecimal = newValue;
this.currency = new MonetaryCurrency(this.currency.getCode(), digitsAfterDecimal, this.currency.getInMultiplesOf());
}
String currencyCode = this.currency.getCode();
if (command.isChangeInStringParameterNamed(currencyCodeParamName, currencyCode)) {
final String newValue = command.stringValueOfParameterNamed(currencyCodeParamName);
actualChanges.put(currencyCodeParamName, newValue);
currencyCode = newValue;
this.currency = new MonetaryCurrency(currencyCode, this.currency.getDigitsAfterDecimal(), this.currency.getInMultiplesOf());
}
Integer inMultiplesOf = this.currency.getInMultiplesOf();
if (command.isChangeInIntegerParameterNamed(inMultiplesOfParamName, inMultiplesOf)) {
final Integer newValue = command.integerValueOfParameterNamed(inMultiplesOfParamName);
actualChanges.put(inMultiplesOfParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
inMultiplesOf = newValue;
this.currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDigitsAfterDecimal(), inMultiplesOf);
}
if (command.isChangeInBigDecimalParameterNamed(nominalAnnualInterestRateParamName, this.nominalAnnualInterestRate)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName);
actualChanges.put(nominalAnnualInterestRateParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.nominalAnnualInterestRate = newValue;
}
if (command.isChangeInIntegerParameterNamed(interestCompoundingPeriodTypeParamName, this.interestCompoundingPeriodType)) {
final Integer newValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName);
actualChanges.put(interestCompoundingPeriodTypeParamName, newValue);
this.interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(newValue).getValue();
}
if (command.isChangeInIntegerParameterNamed(interestPostingPeriodTypeParamName, this.interestPostingPeriodType)) {
final Integer newValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
actualChanges.put(interestPostingPeriodTypeParamName, newValue);
this.interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(newValue).getValue();
}
if (command.isChangeInIntegerParameterNamed(interestCalculationTypeParamName, this.interestCalculationType)) {
final Integer newValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName);
actualChanges.put(interestCalculationTypeParamName, newValue);
this.interestCalculationType = newValue;
}
if (command.isChangeInIntegerParameterNamed(interestCalculationDaysInYearTypeParamName, this.interestCalculationDaysInYearType)) {
final Integer newValue = command.integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName);
actualChanges.put(interestCalculationDaysInYearTypeParamName, newValue);
this.interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(newValue).getValue();
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minRequiredOpeningBalanceParamName, this.minRequiredOpeningBalance)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minRequiredOpeningBalanceParamName);
actualChanges.put(minRequiredOpeningBalanceParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.minRequiredOpeningBalance = newValue;
}
if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(lockinPeriodFrequencyParamName, this.lockinPeriodFrequency)) {
final Integer newValue = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName);
actualChanges.put(lockinPeriodFrequencyParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.lockinPeriodFrequency = newValue;
}
if (command.isChangeInIntegerParameterNamed(lockinPeriodFrequencyTypeParamName, this.lockinPeriodFrequencyType)) {
final Integer newValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
actualChanges.put(lockinPeriodFrequencyTypeParamName, newValue);
this.lockinPeriodFrequencyType = newValue != null ? SavingsPeriodFrequencyType.fromInt(newValue).getValue() : newValue;
}
if (this.lockinPeriodFrequency == null) {
this.lockinPeriodFrequencyType = null;
}
if (command.isChangeInBooleanParameterNamed(withdrawalFeeForTransfersParamName, this.withdrawalFeeApplicableForTransfer)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName);
actualChanges.put(withdrawalFeeForTransfersParamName, newValue);
this.withdrawalFeeApplicableForTransfer = newValue;
}
if (command.isChangeInIntegerParameterNamed(accountingRuleParamName, this.accountingRule)) {
final Integer newValue = command.integerValueOfParameterNamed(accountingRuleParamName);
actualChanges.put(accountingRuleParamName, newValue);
this.accountingRule = newValue;
}
if (command.hasParameter(chargesParamName)) {
final JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName);
if (jsonArray != null) {
actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName));
}
}
if (command.isChangeInBooleanParameterNamed(allowOverdraftParamName, this.allowOverdraft)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(allowOverdraftParamName);
actualChanges.put(allowOverdraftParamName, newValue);
this.allowOverdraft = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(overdraftLimitParamName, this.overdraftLimit)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(overdraftLimitParamName);
actualChanges.put(overdraftLimitParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.overdraftLimit = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(nominalAnnualInterestRateOverdraftParamName, this.nominalAnnualInterestRateOverdraft)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName);
actualChanges.put(nominalAnnualInterestRateOverdraftParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.nominalAnnualInterestRateOverdraft = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minOverdraftForInterestCalculationParamName, this.minOverdraftForInterestCalculation)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName);
actualChanges.put(minOverdraftForInterestCalculationParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.minOverdraftForInterestCalculation = newValue;
}
if (!this.allowOverdraft) {
this.overdraftLimit = null;
this.nominalAnnualInterestRateOverdraft = null;
this.minOverdraftForInterestCalculation = null;
}
if (command.isChangeInBooleanParameterNamed(enforceMinRequiredBalanceParamName, this.enforceMinRequiredBalance)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(enforceMinRequiredBalanceParamName);
actualChanges.put(enforceMinRequiredBalanceParamName, newValue);
this.enforceMinRequiredBalance = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minRequiredBalanceParamName, this.minRequiredBalance)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minRequiredBalanceParamName);
actualChanges.put(minRequiredBalanceParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.minRequiredBalance = newValue;
}
if (command.isChangeInBooleanParameterNamed(lienAllowedParamName, this.lienAllowed)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(lienAllowedParamName);
actualChanges.put(lienAllowedParamName, newValue);
this.lienAllowed = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(maxAllowedLienLimitParamName, this.maxAllowedLienLimit)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(maxAllowedLienLimitParamName);
actualChanges.put(maxAllowedLienLimitParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.maxAllowedLienLimit = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minBalanceForInterestCalculationParamName, this.minBalanceForInterestCalculation)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minBalanceForInterestCalculationParamName);
actualChanges.put(minBalanceForInterestCalculationParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.minBalanceForInterestCalculation = newValue;
}
if (command.isChangeInBooleanParameterNamed(withHoldTaxParamName, this.withHoldTax)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
actualChanges.put(withHoldTaxParamName, newValue);
this.withHoldTax = newValue;
}
if (this.withHoldTax) {
if (this.taxGroup == null || command.isChangeInLongParameterNamed(taxGroupIdParamName, this.taxGroup.getId())) {
final Long newValue = command.longValueOfParameterNamed(taxGroupIdParamName);
actualChanges.put(taxGroupIdParamName, newValue);
}
} else {
this.taxGroup = null;
}
if (command.hasParameter(otherGlMappingsParamName)) {
final String newValue = command.hasParameter(otherGlMappingsParamName) ? command.jsonFragment(otherGlMappingsParamName) : null;
actualChanges.put(otherGlMappingsParamName, newValue);
this.otherGlMappings = newValue;
}
if (command.isChangeInBooleanParameterNamed(isDormancyTrackingActiveParamName, this.isDormancyTrackingActive)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isDormancyTrackingActiveParamName);
actualChanges.put(isDormancyTrackingActiveParamName, newValue);
this.isDormancyTrackingActive = newValue;
}
if (command.isChangeInLongParameterNamed(daysToInactiveParamName, this.daysToInactive)) {
final Long newValue = command.longValueOfParameterNamed(daysToInactiveParamName);
actualChanges.put(daysToInactiveParamName, newValue);
this.daysToInactive = newValue;
}
if (command.isChangeInLongParameterNamed(daysToDormancyParamName, this.daysToDormancy)) {
final Long newValue = command.longValueOfParameterNamed(daysToDormancyParamName);
actualChanges.put(daysToDormancyParamName, newValue);
this.daysToDormancy = newValue;
}
if (command.isChangeInLongParameterNamed(daysToEscheatParamName, this.daysToEscheat)) {
final Long newValue = command.longValueOfParameterNamed(daysToEscheatParamName);
actualChanges.put(daysToEscheatParamName, newValue);
this.daysToEscheat = newValue;
}
// Enhanced fields
if (command.isChangeInStringParameterNamed(accountTypeParamName, this.accountType)) {
final String newValue = command.stringValueOfParameterNamed(accountTypeParamName);
actualChanges.put(accountTypeParamName, newValue);
this.accountType = newValue;
}
if (command.isChangeInStringParameterNamed(customerSegmentParamName, this.customerSegment)) {
final String newValue = command.stringValueOfParameterNamed(customerSegmentParamName);
actualChanges.put(customerSegmentParamName, newValue);
this.customerSegment = newValue;
}
if (command.isChangeInStringParameterNamed(productCodeParamName, this.productCode)) {
final String newValue = command.stringValueOfParameterNamed(productCodeParamName);
actualChanges.put(productCodeParamName, newValue);
this.productCode = newValue;
}
if (command.isChangeInBooleanParameterNamed(introducerRequiredParamName, this.introducerRequired)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(introducerRequiredParamName);
actualChanges.put(introducerRequiredParamName, newValue);
this.introducerRequired = newValue;
}
if (command.isChangeInBooleanParameterNamed(shareLinkingEnabledParamName, this.shareLinkingEnabled)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(shareLinkingEnabledParamName);
actualChanges.put(shareLinkingEnabledParamName, newValue);
this.shareLinkingEnabled = newValue;
}
if (command.isChangeInBooleanParameterNamed(nominationFacilityParamName, this.nominationFacility)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(nominationFacilityParamName);
actualChanges.put(nominationFacilityParamName, newValue);
this.nominationFacility = newValue;
}
if (command.isChangeInBooleanParameterNamed(dormancyAlertParamName, this.dormancyAlert)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(dormancyAlertParamName);
actualChanges.put(dormancyAlertParamName, newValue);
this.dormancyAlert = newValue;
}
if (command.isChangeInBooleanParameterNamed(autoPostParamName, this.autoPost)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(autoPostParamName);
actualChanges.put(autoPostParamName, newValue);
this.autoPost = newValue;
}
if (command.isChangeInBooleanParameterNamed(enableLockinPeriodParamName, this.enableLockinPeriod)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(enableLockinPeriodParamName);
actualChanges.put(enableLockinPeriodParamName, newValue);
this.enableLockinPeriod = newValue;
}
// New currency-step fields
if (command.isChangeInBooleanParameterNamed(balanceSwapOptionParamName, this.balanceSwapOption)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(balanceSwapOptionParamName);
actualChanges.put(balanceSwapOptionParamName, newValue);
this.balanceSwapOption = newValue;
}
if (command.isChangeInBooleanParameterNamed(msmeParamName, this.msme)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(msmeParamName);
actualChanges.put(msmeParamName, newValue);
this.msme = newValue;
}
if (command.isChangeInBooleanParameterNamed(autoDebitFacilityParamName, this.autoDebitFacility)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(autoDebitFacilityParamName);
actualChanges.put(autoDebitFacilityParamName, newValue);
this.autoDebitFacility = newValue;
}
// New OD-section fields
if (command.isChangeInStringParameterNamed(avgBalanceCalcPeriodParamName, this.avgBalanceCalcPeriod)) {
final String newValue = command.stringValueOfParameterNamed(avgBalanceCalcPeriodParamName);
actualChanges.put(avgBalanceCalcPeriodParamName, newValue);
this.avgBalanceCalcPeriod = newValue;
}
if (command.isChangeInStringParameterNamed(validForPeriodParamName, this.validForPeriod)) {
final String newValue = command.stringValueOfParameterNamed(validForPeriodParamName);
actualChanges.put(validForPeriodParamName, newValue);
this.validForPeriod = newValue;
}
if (command.isChangeInBooleanParameterNamed(odAccountClosedZeroBalanceInDayClosingParamName, this.odAccountClosedZeroBalanceInDayClosing)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(odAccountClosedZeroBalanceInDayClosingParamName);
actualChanges.put(odAccountClosedZeroBalanceInDayClosingParamName, newValue);
this.odAccountClosedZeroBalanceInDayClosing = newValue;
}
// Clear OD-specific fields when overdraft is disabled
if (!this.allowOverdraft) {
this.avgBalanceCalcPeriod = null;
this.validForPeriod = null;
this.odAccountClosedZeroBalanceInDayClosing = false;
}
return actualChanges;
}
// Dormancy/misc getters needed by validator
public Boolean isDormancyTrackingActive() { return this.isDormancyTrackingActive; }
public Long getDaysToInactive() { return this.daysToInactive; }
public Long getDaysToDormancy() { return this.daysToDormancy; }
public Long getDaysToEscheat() { return this.daysToEscheat; }
// Getters and setters for enhanced fields
public String getAccountType() { return accountType; }
public void setAccountType(String accountType) { this.accountType = accountType; }
public String getCustomerSegment() { return customerSegment; }
public void setCustomerSegment(String customerSegment) { this.customerSegment = customerSegment; }
public String getProductCode() { return productCode; }
public void setProductCode(String productCode) { this.productCode = productCode; }
public LocalDate getEffectiveDate() { return effectiveDate; }
public void setEffectiveDate(LocalDate effectiveDate) { this.effectiveDate = effectiveDate; }
public LocalDate getEffectiveTo() { return effectiveTo; }
public void setEffectiveTo(LocalDate effectiveTo) { this.effectiveTo = effectiveTo; }
public LocalDate getEndDate() { return endDate; }
public void setEndDate(LocalDate endDate) { this.endDate = endDate; }
public boolean isIntroducerRequired() { return introducerRequired; }
public void setIntroducerRequired(boolean introducerRequired) { this.introducerRequired = introducerRequired; }
public boolean isShareLinkingEnabled() { return shareLinkingEnabled; }
public void setShareLinkingEnabled(boolean shareLinkingEnabled) { this.shareLinkingEnabled = shareLinkingEnabled; }
public boolean isNominationFacility() { return nominationFacility; }
public void setNominationFacility(boolean nominationFacility) { this.nominationFacility = nominationFacility; }
public boolean isDormancyAlert() { return dormancyAlert; }
public void setDormancyAlert(boolean dormancyAlert) { this.dormancyAlert = dormancyAlert; }
public Integer getInterestCalculateSelector() { return interestCalculateSelector; }
public void setInterestCalculateSelector(Integer interestCalculateSelector) { this.interestCalculateSelector = interestCalculateSelector; }
public String getAveragePeriod() { return averagePeriod; }
public void setAveragePeriod(String averagePeriod) { this.averagePeriod = averagePeriod; }
public boolean isAutoPost() { return autoPost; }
public void setAutoPost(boolean autoPost) { this.autoPost = autoPost; }
public Integer getFrequencyFixDay() { return frequencyFixDay; }
public void setFrequencyFixDay(Integer frequencyFixDay) { this.frequencyFixDay = frequencyFixDay; }
public LocalDate getLastInterestDate() { return lastInterestDate; }
public void setLastInterestDate(LocalDate lastInterestDate) { this.lastInterestDate = lastInterestDate; }
public boolean isEnableLockinPeriod() { return enableLockinPeriod; }
public void setEnableLockinPeriod(boolean enableLockinPeriod) { this.enableLockinPeriod = enableLockinPeriod; }
public String getBalanceFrequency() { return balanceFrequency; }
public void setBalanceFrequency(String balanceFrequency) { this.balanceFrequency = balanceFrequency; }
public Boolean getAccountClosedZeroBalanceInDayClosing() { return accountClosedZeroBalanceInDayClosing; }
public void setAccountClosedZeroBalanceInDayClosing(Boolean val) { this.accountClosedZeroBalanceInDayClosing = val; }
// New field getters/setters
public boolean isBalanceSwapOption() { return balanceSwapOption; }
public void setBalanceSwapOption(boolean balanceSwapOption) { this.balanceSwapOption = balanceSwapOption; }
public boolean isMsme() { return msme; }
public void setMsme(boolean msme) { this.msme = msme; }
public boolean isAutoDebitFacility() { return autoDebitFacility; }
public void setAutoDebitFacility(boolean autoDebitFacility) { this.autoDebitFacility = autoDebitFacility; }
public String getAvgBalanceCalcPeriod() { return avgBalanceCalcPeriod; }
public void setAvgBalanceCalcPeriod(String avgBalanceCalcPeriod) { this.avgBalanceCalcPeriod = avgBalanceCalcPeriod; }
public String getValidForPeriod() { return validForPeriod; }
public void setValidForPeriod(String validForPeriod) { this.validForPeriod = validForPeriod; }
public boolean isOdAccountClosedZeroBalanceInDayClosing() { return odAccountClosedZeroBalanceInDayClosing; }
public void setOdAccountClosedZeroBalanceInDayClosing(boolean val) { this.odAccountClosedZeroBalanceInDayClosing = val; }
}
@@ -0,0 +1,429 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountFacilityConfigParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountFacilityLimitsParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.autoPostParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.averagePeriodParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.customerSegmentParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.depositLimitKycParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dormancyAlertParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.effectiveDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.effectiveToParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enableLockinPeriodParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.balanceSwapOptionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.msmeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.autoDebitFacilityParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.avgBalanceCalcPeriodParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.validForPeriodParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.odAccountClosedZeroBalanceInDayClosingParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.endDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.frequencyFixDayParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.idParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculateSelectorParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerRequiredParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lastInterestDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominationFacilityParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.otherGlMappingsParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.productCodeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shareLinkingEnabledParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.exception.InvalidCurrencyException;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CurrentProductAssembler {
private final ChargeRepositoryWrapper chargeRepository;
private final TaxGroupRepositoryWrapper taxGroupRepository;
@Autowired
public CurrentProductAssembler(final ChargeRepositoryWrapper chargeRepository,
final TaxGroupRepositoryWrapper taxGroupRepository) {
this.chargeRepository = chargeRepository;
this.taxGroupRepository = taxGroupRepository;
}
public CurrentProduct assemble(final JsonCommand command) {
final String name = command.stringValueOfParameterNamed(nameParamName);
final String shortName = command.stringValueOfParameterNamed(shortNameParamName);
final String description = command.stringValueOfParameterNamedAllowingNull(descriptionParamName);
final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName);
final Integer digitsAfterDecimal = command.integerValueOfParameterNamed(digitsAfterDecimalParamName);
final Integer inMultiplesOf = command.integerValueOfParameterNamed(inMultiplesOfParamName);
final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf);
BigDecimal interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName);
if (interestRate == null) {
interestRate = BigDecimal.ZERO;
}
SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.DAILY;
final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName);
if (interestPeriodTypeValue != null) {
interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue);
}
SavingsPostingInterestPeriodType interestPostingPeriodType = null;
final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
if (interestPostingPeriodTypeValue != null) {
interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue);
}
SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.DAILY_BALANCE;
final Integer interestCalculationTypeValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName);
if (interestCalculationTypeValue != null) {
interestCalculationType = SavingsInterestCalculationType.fromInt(interestCalculationTypeValue);
}
SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.DAYS_365;
final Integer interestCalculationDaysInYearTypeValue = command.integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName);
if (interestCalculationDaysInYearTypeValue != null) {
interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue);
}
final BigDecimal minRequiredOpeningBalance = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minRequiredOpeningBalanceParamName);
final Integer lockinPeriodFrequency = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName);
SavingsPeriodFrequencyType lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
if (lockinPeriodFrequencyTypeValue != null) {
lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
}
boolean isWithdrawalFeeApplicableForTransfer = false;
if (command.parameterExists(withdrawalFeeForTransfersParamName)) {
isWithdrawalFeeApplicableForTransfer = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName);
}
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
final Set<Charge> charges = assembleListOfCurrentProductCharges(command, currencyCode);
boolean allowOverdraft = false;
if (command.parameterExists(allowOverdraftParamName)) {
allowOverdraft = command.booleanPrimitiveValueOfParameterNamed(allowOverdraftParamName);
}
BigDecimal overdraftLimit = BigDecimal.ZERO;
if (command.parameterExists(overdraftLimitParamName)) {
overdraftLimit = command.bigDecimalValueOfParameterNamed(overdraftLimitParamName);
}
BigDecimal nominalAnnualInterestRateOverdraft = BigDecimal.ZERO;
if (command.parameterExists(nominalAnnualInterestRateOverdraftParamName)) {
nominalAnnualInterestRateOverdraft = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateOverdraftParamName);
}
BigDecimal minOverdraftForInterestCalculation = BigDecimal.ZERO;
if (command.parameterExists(minOverdraftForInterestCalculationParamName)) {
minOverdraftForInterestCalculation = command.bigDecimalValueOfParameterNamed(minOverdraftForInterestCalculationParamName);
}
boolean enforceMinRequiredBalance = false;
if (command.parameterExists(enforceMinRequiredBalanceParamName)) {
enforceMinRequiredBalance = command.booleanPrimitiveValueOfParameterNamed(enforceMinRequiredBalanceParamName);
}
BigDecimal minRequiredBalance = BigDecimal.ZERO;
if (command.parameterExists(minRequiredBalanceParamName)) {
minRequiredBalance = command.bigDecimalValueOfParameterNamed(minRequiredBalanceParamName);
}
boolean lienAllowed = false;
if (command.parameterExists(lienAllowedParamName)) {
lienAllowed = command.booleanPrimitiveValueOfParameterNamed(lienAllowedParamName);
}
BigDecimal maxAllowedLienLimit = BigDecimal.ZERO;
if (command.parameterExists(maxAllowedLienLimitParamName)) {
maxAllowedLienLimit = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(maxAllowedLienLimitParamName);
}
final BigDecimal minBalanceForInterestCalculation = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minBalanceForInterestCalculationParamName);
boolean withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
final TaxGroup taxGroup = assembleTaxGroup(command);
final Boolean isDormancyTrackingActive = command.booleanObjectValueOfParameterNamed(isDormancyTrackingActiveParamName);
final Long daysToInactive = command.longValueOfParameterNamed(daysToInactiveParamName);
final Long daysToDormancy = command.longValueOfParameterNamed(daysToDormancyParamName);
final Long daysToEscheat = command.longValueOfParameterNamed(daysToEscheatParamName);
final String otherGlMappings = command.hasParameter(otherGlMappingsParamName) ? command.jsonFragment(otherGlMappingsParamName) : null;
CurrentProduct product = CurrentProduct.createNew(name, shortName, description, currency, interestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, isWithdrawalFeeApplicableForTransfer, accountingRuleType, charges,
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, lienAllowed,
maxAllowedLienLimit, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, otherGlMappings,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
// Set enhanced fields
if (command.parameterExists(accountTypeParamName)) {
product.setAccountType(command.stringValueOfParameterNamed(accountTypeParamName));
}
if (command.parameterExists(customerSegmentParamName)) {
product.setCustomerSegment(command.stringValueOfParameterNamed(customerSegmentParamName));
}
if (command.parameterExists(productCodeParamName)) {
product.setProductCode(command.stringValueOfParameterNamed(productCodeParamName));
}
if (command.parameterExists(effectiveDateParamName)) {
product.setEffectiveDate(command.localDateValueOfParameterNamed(effectiveDateParamName));
}
if (command.parameterExists(effectiveToParamName)) {
product.setEffectiveTo(command.localDateValueOfParameterNamed(effectiveToParamName));
}
if (command.parameterExists(endDateParamName)) {
product.setEndDate(command.localDateValueOfParameterNamed(endDateParamName));
}
if (command.parameterExists(introducerRequiredParamName)) {
product.setIntroducerRequired(command.booleanPrimitiveValueOfParameterNamed(introducerRequiredParamName));
}
if (command.parameterExists(shareLinkingEnabledParamName)) {
product.setShareLinkingEnabled(command.booleanPrimitiveValueOfParameterNamed(shareLinkingEnabledParamName));
}
if (command.parameterExists(nominationFacilityParamName)) {
product.setNominationFacility(command.booleanPrimitiveValueOfParameterNamed(nominationFacilityParamName));
}
if (command.parameterExists(dormancyAlertParamName)) {
product.setDormancyAlert(command.booleanPrimitiveValueOfParameterNamed(dormancyAlertParamName));
}
if (command.parameterExists(interestCalculateSelectorParamName)) {
product.setInterestCalculateSelector(command.integerValueOfParameterNamed(interestCalculateSelectorParamName));
}
if (command.parameterExists(averagePeriodParamName)) {
product.setAveragePeriod(command.stringValueOfParameterNamed(averagePeriodParamName));
}
if (command.parameterExists(autoPostParamName)) {
product.setAutoPost(command.booleanPrimitiveValueOfParameterNamed(autoPostParamName));
}
if (command.parameterExists(frequencyFixDayParamName)) {
product.setFrequencyFixDay(command.integerValueOfParameterNamed(frequencyFixDayParamName));
}
if (command.parameterExists(lastInterestDateParamName)) {
product.setLastInterestDate(command.localDateValueOfParameterNamed(lastInterestDateParamName));
}
if (command.parameterExists(enableLockinPeriodParamName)) {
product.setEnableLockinPeriod(command.booleanPrimitiveValueOfParameterNamed(enableLockinPeriodParamName));
}
if (command.parameterExists("balanceFrequency")) {
product.setBalanceFrequency(command.stringValueOfParameterNamed("balanceFrequency"));
}
if (command.parameterExists("accountClosedZeroBalanceInDayClosing")) {
product.setAccountClosedZeroBalanceInDayClosing(command.booleanObjectValueOfParameterNamed("accountClosedZeroBalanceInDayClosing"));
}
// New currency-step fields
if (command.parameterExists(balanceSwapOptionParamName)) {
product.setBalanceSwapOption(command.booleanPrimitiveValueOfParameterNamed(balanceSwapOptionParamName));
}
if (command.parameterExists(msmeParamName)) {
product.setMsme(command.booleanPrimitiveValueOfParameterNamed(msmeParamName));
}
if (command.parameterExists(autoDebitFacilityParamName)) {
product.setAutoDebitFacility(command.booleanPrimitiveValueOfParameterNamed(autoDebitFacilityParamName));
}
// New OD-section fields (only set when overdraft is allowed)
if (allowOverdraft) {
if (command.parameterExists(avgBalanceCalcPeriodParamName)) {
product.setAvgBalanceCalcPeriod(command.stringValueOfParameterNamed(avgBalanceCalcPeriodParamName));
}
if (command.parameterExists(validForPeriodParamName)) {
product.setValidForPeriod(command.stringValueOfParameterNamed(validForPeriodParamName));
}
if (command.parameterExists(odAccountClosedZeroBalanceInDayClosingParamName)) {
product.setOdAccountClosedZeroBalanceInDayClosing(command.booleanPrimitiveValueOfParameterNamed(odAccountClosedZeroBalanceInDayClosingParamName));
}
}
return product;
}
public Set<Charge> assembleListOfCurrentProductCharges(final JsonCommand command, final String currencyCode) {
final Set<Charge> charges = new HashSet<>();
if (command.parameterExists(chargesParamName)) {
final JsonArray chargesArray = command.arrayOfParameterNamed(chargesParamName);
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
if (jsonObject.has(idParamName)) {
final Long id = jsonObject.get(idParamName).getAsLong();
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
if (!charge.isSavingsCharge()) {
final String errorMessage = "Charge with identifier " + charge.getId()
+ " cannot be applied to Current product.";
throw new ChargeCannotBeAppliedToException("current.product", errorMessage, charge.getId());
}
if (!currencyCode.equals(charge.getCurrencyCode())) {
final String errorMessage = "Charge and Current Product must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.current.product", errorMessage);
}
charges.add(charge);
}
}
}
}
return charges;
}
public TaxGroup assembleTaxGroup(final JsonCommand command) {
final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName);
TaxGroup taxGroup = null;
if (taxGroupId != null) {
taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId);
}
return taxGroup;
}
public CurrentProductFacilityConfig assembleAccountFacilityConfig(final JsonCommand command, final CurrentProduct product) {
if (!command.hasParameter(accountFacilityConfigParamName)) {
return null;
}
JsonElement facilityConfigElement = command.parsedJson().getAsJsonObject().get(accountFacilityConfigParamName);
if (facilityConfigElement == null || facilityConfigElement.isJsonNull()) {
return null;
}
final JsonObject facilityConfigJson = facilityConfigElement.getAsJsonObject();
CurrentProductFacilityConfig config = new CurrentProductFacilityConfig(product);
config.setAtmFacility(facilityConfigJson.has("atmFacility") ? facilityConfigJson.get("atmFacility").getAsBoolean() : false);
config.setAtmWithdrawalFacility(facilityConfigJson.has("atmWithdrawalFacility") ? facilityConfigJson.get("atmWithdrawalFacility").getAsBoolean() : false);
config.setUpiFacility(facilityConfigJson.has("upiFacility") ? facilityConfigJson.get("upiFacility").getAsBoolean() : false);
config.setPosTransactionsFacility(facilityConfigJson.has("posTransactionsFacility") ? facilityConfigJson.get("posTransactionsFacility").getAsBoolean() : false);
config.setChequeBook(facilityConfigJson.has("chequeBook") ? facilityConfigJson.get("chequeBook").getAsBoolean() : false);
config.setImpsFacility(facilityConfigJson.has("impsFacility") ? facilityConfigJson.get("impsFacility").getAsBoolean() : false);
config.setSmsAlerts(facilityConfigJson.has("smsAlerts") ? facilityConfigJson.get("smsAlerts").getAsBoolean() : false);
config.setBbpsFacility(facilityConfigJson.has("bbpsFacility") ? facilityConfigJson.get("bbpsFacility").getAsBoolean() : false);
config.setInsuranceCover(facilityConfigJson.has("insuranceCover") ? facilityConfigJson.get("insuranceCover").getAsBoolean() : false);
config.setBepgFacility(facilityConfigJson.has("bepgFacility") ? facilityConfigJson.get("bepgFacility").getAsBoolean() : false);
config.setNeftFacility(facilityConfigJson.has("neftFacility") ? facilityConfigJson.get("neftFacility").getAsBoolean() : false);
config.setRtgsFacility(facilityConfigJson.has("rtgsFacility") ? facilityConfigJson.get("rtgsFacility").getAsBoolean() : false);
config.setInterBankTotalCapFacility(facilityConfigJson.has("interBankTotalCapFacility") ? facilityConfigJson.get("interBankTotalCapFacility").getAsBoolean() : false);
config.setWithdrawalFacility(facilityConfigJson.has("withdrawalFacility") ? facilityConfigJson.get("withdrawalFacility").getAsBoolean() : false);
config.setDepositFacilityKyc(facilityConfigJson.has("depositFacilityKyc") ? facilityConfigJson.get("depositFacilityKyc").getAsBoolean() : false);
config.setVirtualDebitCard(facilityConfigJson.has("virtualDebitCard") ? facilityConfigJson.get("virtualDebitCard").getAsBoolean() : false);
return config;
}
public Set<CurrentProductFacilityLimit> assembleAccountFacilityLimits(final JsonCommand command, final CurrentProduct product) {
final Set<CurrentProductFacilityLimit> limits = new HashSet<>();
if (!command.hasParameter(accountFacilityLimitsParamName)) {
return limits;
}
final JsonArray limitsArray = command.arrayOfParameterNamed(accountFacilityLimitsParamName);
if (limitsArray == null) {
return limits;
}
for (int i = 0; i < limitsArray.size(); i++) {
final JsonObject limitJson = limitsArray.get(i).getAsJsonObject();
limits.add(CurrentProductFacilityLimit.create(product,
limitJson.has("limitType") ? limitJson.get("limitType").getAsString() : null,
limitJson.has("generalLimit") ? limitJson.get("generalLimit").getAsString() : null,
limitJson.has("minorAccountLimit") ? limitJson.get("minorAccountLimit").getAsString() : null,
limitJson.has("smallSavingLimit") ? limitJson.get("smallSavingLimit").getAsString() : null));
}
return limits;
}
public CurrentProductDepositLimitKyc assembleDepositLimitKyc(final JsonCommand command, final CurrentProduct product) {
if (!command.hasParameter(depositLimitKycParamName)) {
return null;
}
JsonElement kycElement = command.parsedJson().getAsJsonObject().get(depositLimitKycParamName);
if (kycElement == null || kycElement.isJsonNull()) {
return null;
}
final JsonObject kycJson = kycElement.getAsJsonObject();
CurrentProductDepositLimitKyc kyc = new CurrentProductDepositLimitKyc(product);
if (kycJson.has("general")) {
final JsonObject generalJson = kycJson.getAsJsonObject("general");
kyc.setGeneralKycType(generalJson.has("kycType") ? generalJson.get("kycType").getAsString() : null);
kyc.setGeneralLimit(generalJson.has("limit") ? generalJson.get("limit").getAsString() : null);
}
if (kycJson.has("minor")) {
final JsonObject minorJson = kycJson.getAsJsonObject("minor");
kyc.setMinorKycType(minorJson.has("kycType") ? minorJson.get("kycType").getAsString() : null);
kyc.setMinorLimit(minorJson.has("limit") ? minorJson.get("limit").getAsLong() : null);
}
if (kycJson.has("smallSaving")) {
final JsonObject smallSavingJson = kycJson.getAsJsonObject("smallSaving");
kyc.setSmallSavingKycType(smallSavingJson.has("kycType") ? smallSavingJson.get("kycType").getAsString() : null);
kyc.setSmallSavingLimit(smallSavingJson.has("limit") ? smallSavingJson.get("limit").getAsLong() : null);
}
return kyc;
}
}
@@ -0,0 +1,92 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_current_product_deposit_limit_kyc")
public class CurrentProductDepositLimitKyc extends AbstractPersistableCustom<Long> {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_product_id", nullable = false)
private CurrentProduct currentProduct;
@Column(name = "general_kyc_type", length = 50)
private String generalKycType;
@Column(name = "general_limit", length = 50)
private String generalLimit;
@Column(name = "minor_kyc_type", length = 50)
private String minorKycType;
@Column(name = "minor_limit")
private Long minorLimit;
@Column(name = "small_saving_kyc_type", length = 50)
private String smallSavingKycType;
@Column(name = "small_saving_limit")
private Long smallSavingLimit;
@Column(name = "created_date", nullable = false)
private LocalDateTime createdDate;
protected CurrentProductDepositLimitKyc() {
this.createdDate = LocalDateTime.now();
}
public CurrentProductDepositLimitKyc(CurrentProduct currentProduct) {
this();
this.currentProduct = currentProduct;
}
public CurrentProduct getCurrentProduct() { return currentProduct; }
public void setCurrentProduct(CurrentProduct currentProduct) { this.currentProduct = currentProduct; }
public String getGeneralKycType() { return generalKycType; }
public void setGeneralKycType(String generalKycType) { this.generalKycType = generalKycType; }
public String getGeneralLimit() { return generalLimit; }
public void setGeneralLimit(String generalLimit) { this.generalLimit = generalLimit; }
public String getMinorKycType() { return minorKycType; }
public void setMinorKycType(String minorKycType) { this.minorKycType = minorKycType; }
public Long getMinorLimit() { return minorLimit; }
public void setMinorLimit(Long minorLimit) { this.minorLimit = minorLimit; }
public String getSmallSavingKycType() { return smallSavingKycType; }
public void setSmallSavingKycType(String smallSavingKycType) { this.smallSavingKycType = smallSavingKycType; }
public Long getSmallSavingLimit() { return smallSavingLimit; }
public void setSmallSavingLimit(Long smallSavingLimit) { this.smallSavingLimit = smallSavingLimit; }
public LocalDateTime getCreatedDate() { return createdDate; }
public void updateFrom(CurrentProductDepositLimitKyc other) {
this.generalKycType = other.getGeneralKycType();
this.generalLimit = other.getGeneralLimit();
this.minorKycType = other.getMinorKycType();
this.minorLimit = other.getMinorLimit();
this.smallSavingKycType = other.getSmallSavingKycType();
this.smallSavingLimit = other.getSmallSavingLimit();
}
}
@@ -0,0 +1,26 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CurrentProductDepositLimitKycRepository extends JpaRepository<CurrentProductDepositLimitKyc, Long> {
CurrentProductDepositLimitKyc findByCurrentProductId(Long currentProductId);
}
@@ -0,0 +1,153 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_current_product_facility_config")
public class CurrentProductFacilityConfig extends AbstractPersistableCustom<Long> {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_product_id", nullable = false)
private CurrentProduct currentProduct;
@Column(name = "atm_facility")
private Boolean atmFacility = false;
@Column(name = "atm_withdrawal_facility")
private Boolean atmWithdrawalFacility = false;
@Column(name = "upi_facility")
private Boolean upiFacility = false;
@Column(name = "pos_transactions_facility")
private Boolean posTransactionsFacility = false;
@Column(name = "cheque_book")
private Boolean chequeBook = false;
@Column(name = "imps_facility")
private Boolean impsFacility = false;
@Column(name = "sms_alerts")
private Boolean smsAlerts = false;
@Column(name = "bbps_facility")
private Boolean bbpsFacility = false;
@Column(name = "insurance_cover")
private Boolean insuranceCover = false;
@Column(name = "bepg_facility")
private Boolean bepgFacility = false;
@Column(name = "neft_facility")
private Boolean neftFacility = false;
@Column(name = "rtgs_facility")
private Boolean rtgsFacility = false;
@Column(name = "inter_bank_total_cap_facility")
private Boolean interBankTotalCapFacility = false;
@Column(name = "withdrawal_facility")
private Boolean withdrawalFacility = false;
@Column(name = "deposit_facility_kyc")
private Boolean depositFacilityKyc = false;
@Column(name = "virtual_debit_card")
private Boolean virtualDebitCard = false;
@Column(name = "created_date", nullable = false)
private LocalDateTime createdDate;
protected CurrentProductFacilityConfig() {
this.createdDate = LocalDateTime.now();
}
public CurrentProductFacilityConfig(CurrentProduct currentProduct) {
this();
this.currentProduct = currentProduct;
}
public CurrentProduct getCurrentProduct() { return currentProduct; }
public void setCurrentProduct(CurrentProduct currentProduct) { this.currentProduct = currentProduct; }
public Boolean getAtmFacility() { return atmFacility; }
public void setAtmFacility(Boolean v) { this.atmFacility = v != null ? v : false; }
public Boolean getAtmWithdrawalFacility() { return atmWithdrawalFacility; }
public void setAtmWithdrawalFacility(Boolean v) { this.atmWithdrawalFacility = v != null ? v : false; }
public Boolean getUpiFacility() { return upiFacility; }
public void setUpiFacility(Boolean v) { this.upiFacility = v != null ? v : false; }
public Boolean getPosTransactionsFacility() { return posTransactionsFacility; }
public void setPosTransactionsFacility(Boolean v) { this.posTransactionsFacility = v != null ? v : false; }
public Boolean getChequeBook() { return chequeBook; }
public void setChequeBook(Boolean v) { this.chequeBook = v != null ? v : false; }
public Boolean getImpsFacility() { return impsFacility; }
public void setImpsFacility(Boolean v) { this.impsFacility = v != null ? v : false; }
public Boolean getSmsAlerts() { return smsAlerts; }
public void setSmsAlerts(Boolean v) { this.smsAlerts = v != null ? v : false; }
public Boolean getBbpsFacility() { return bbpsFacility; }
public void setBbpsFacility(Boolean v) { this.bbpsFacility = v != null ? v : false; }
public Boolean getInsuranceCover() { return insuranceCover; }
public void setInsuranceCover(Boolean v) { this.insuranceCover = v != null ? v : false; }
public Boolean getBepgFacility() { return bepgFacility; }
public void setBepgFacility(Boolean v) { this.bepgFacility = v != null ? v : false; }
public Boolean getNeftFacility() { return neftFacility; }
public void setNeftFacility(Boolean v) { this.neftFacility = v != null ? v : false; }
public Boolean getRtgsFacility() { return rtgsFacility; }
public void setRtgsFacility(Boolean v) { this.rtgsFacility = v != null ? v : false; }
public Boolean getInterBankTotalCapFacility() { return interBankTotalCapFacility; }
public void setInterBankTotalCapFacility(Boolean v) { this.interBankTotalCapFacility = v != null ? v : false; }
public Boolean getWithdrawalFacility() { return withdrawalFacility; }
public void setWithdrawalFacility(Boolean v) { this.withdrawalFacility = v != null ? v : false; }
public Boolean getDepositFacilityKyc() { return depositFacilityKyc; }
public void setDepositFacilityKyc(Boolean v) { this.depositFacilityKyc = v != null ? v : false; }
public Boolean getVirtualDebitCard() { return virtualDebitCard; }
public void setVirtualDebitCard(Boolean v) { this.virtualDebitCard = v != null ? v : false; }
public LocalDateTime getCreatedDate() { return createdDate; }
public void updateFrom(CurrentProductFacilityConfig other) {
this.atmFacility = other.getAtmFacility();
this.atmWithdrawalFacility = other.getAtmWithdrawalFacility();
this.upiFacility = other.getUpiFacility();
this.posTransactionsFacility = other.getPosTransactionsFacility();
this.chequeBook = other.getChequeBook();
this.impsFacility = other.getImpsFacility();
this.smsAlerts = other.getSmsAlerts();
this.bbpsFacility = other.getBbpsFacility();
this.insuranceCover = other.getInsuranceCover();
this.bepgFacility = other.getBepgFacility();
this.neftFacility = other.getNeftFacility();
this.rtgsFacility = other.getRtgsFacility();
this.interBankTotalCapFacility = other.getInterBankTotalCapFacility();
this.withdrawalFacility = other.getWithdrawalFacility();
this.depositFacilityKyc = other.getDepositFacilityKyc();
this.virtualDebitCard = other.getVirtualDebitCard();
}
}
@@ -0,0 +1,26 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CurrentProductFacilityConfigRepository extends JpaRepository<CurrentProductFacilityConfig, Long> {
CurrentProductFacilityConfig findByCurrentProductId(Long currentProductId);
}
@@ -0,0 +1,83 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_current_product_facility_limits")
public class CurrentProductFacilityLimit extends AbstractPersistableCustom<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_product_id", nullable = false)
private CurrentProduct currentProduct;
@Column(name = "limit_type", length = 100, nullable = false)
private String limitType;
@Column(name = "general_limit", length = 50)
private String generalLimit;
@Column(name = "minor_account_limit", length = 50)
private String minorAccountLimit;
@Column(name = "small_saving_limit", length = 50)
private String smallSavingLimit;
@Column(name = "created_date", nullable = false)
private LocalDateTime createdDate;
protected CurrentProductFacilityLimit() {
this.createdDate = LocalDateTime.now();
}
private CurrentProductFacilityLimit(CurrentProduct currentProduct, String limitType,
String generalLimit, String minorAccountLimit, String smallSavingLimit) {
this();
this.currentProduct = currentProduct;
this.limitType = limitType;
this.generalLimit = generalLimit;
this.minorAccountLimit = minorAccountLimit;
this.smallSavingLimit = smallSavingLimit;
}
public static CurrentProductFacilityLimit create(CurrentProduct currentProduct, String limitType,
String generalLimit, String minorAccountLimit, String smallSavingLimit) {
return new CurrentProductFacilityLimit(currentProduct, limitType, generalLimit, minorAccountLimit, smallSavingLimit);
}
public CurrentProduct getCurrentProduct() { return currentProduct; }
public void setCurrentProduct(CurrentProduct currentProduct) { this.currentProduct = currentProduct; }
public String getLimitType() { return limitType; }
public void setLimitType(String limitType) { this.limitType = limitType; }
public String getGeneralLimit() { return generalLimit; }
public void setGeneralLimit(String generalLimit) { this.generalLimit = generalLimit; }
public String getMinorAccountLimit() { return minorAccountLimit; }
public void setMinorAccountLimit(String minorAccountLimit) { this.minorAccountLimit = minorAccountLimit; }
public String getSmallSavingLimit() { return smallSavingLimit; }
public void setSmallSavingLimit(String smallSavingLimit) { this.smallSavingLimit = smallSavingLimit; }
public LocalDateTime getCreatedDate() { return createdDate; }
}
@@ -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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
public interface CurrentProductFacilityLimitRepository extends JpaRepository<CurrentProductFacilityLimit, Long> {
List<CurrentProductFacilityLimit> findByCurrentProductId(Long currentProductId);
@Transactional
void deleteByCurrentProductId(Long currentProductId);
}
@@ -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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface CurrentProductRepository extends JpaRepository<CurrentProduct, Long>, JpaSpecificationExecutor<CurrentProduct> {
}
@@ -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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.service;
import java.util.Collection;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
public interface CurrentProductReadPlatformService {
Collection<SavingsProductData> retrieveAll();
Collection<SavingsProductData> retrieveAllForLookup();
SavingsProductData retrieveOne(Long productId);
}
@@ -0,0 +1,346 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.service;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingEnumerations;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKyc;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfig;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimit;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@RequiredArgsConstructor
public class CurrentProductReadPlatformServiceImpl implements CurrentProductReadPlatformService {
private final PlatformSecurityContext context;
private final JdbcTemplate jdbcTemplate;
private final CurrentProductFacilityConfigRepository facilityConfigRepository;
private final CurrentProductFacilityLimitRepository facilityLimitRepository;
private final CurrentProductDepositLimitKycRepository depositLimitKycRepository;
private final CurrentProductMapper currentProductRowMapper = new CurrentProductMapper();
private final CurrentProductLookupMapper currentProductLookupsRowMapper = new CurrentProductLookupMapper();
private static final Gson GSON = new Gson();
private static final Type OTHER_GL_MAPPINGS_TYPE = new TypeToken<Map<String, List<Map<String, Object>>>>() {}.getType();
@Override
public Collection<SavingsProductData> retrieveAll() {
this.context.authenticatedUser();
final String sql = "select " + this.currentProductRowMapper.schema();
return this.jdbcTemplate.query(sql, this.currentProductRowMapper); // NOSONAR
}
@Override
public Collection<SavingsProductData> retrieveAllForLookup() {
final String sql = "select " + this.currentProductLookupsRowMapper.schema();
return this.jdbcTemplate.query(sql, this.currentProductLookupsRowMapper); // NOSONAR
}
@Override
public SavingsProductData retrieveOne(final Long productId) {
try {
this.context.authenticatedUser();
final String sql = "select " + this.currentProductRowMapper.schema() + " where cp.id = ?";
SavingsProductData product = this.jdbcTemplate.queryForObject(sql, this.currentProductRowMapper, // NOSONAR
new Object[] { productId });
if (product != null) {
// Fetch Facility Config
CurrentProductFacilityConfig facilityConfig = facilityConfigRepository.findByCurrentProductId(productId);
if (facilityConfig != null) {
Map<String, Object> facilityConfigMap = new HashMap<>();
facilityConfigMap.put("atmFacility", facilityConfig.getAtmFacility());
facilityConfigMap.put("atmWithdrawalFacility", facilityConfig.getAtmWithdrawalFacility());
facilityConfigMap.put("upiFacility", facilityConfig.getUpiFacility());
facilityConfigMap.put("posTransactionsFacility", facilityConfig.getPosTransactionsFacility());
facilityConfigMap.put("chequeBook", facilityConfig.getChequeBook());
facilityConfigMap.put("impsFacility", facilityConfig.getImpsFacility());
facilityConfigMap.put("smsAlerts", facilityConfig.getSmsAlerts());
facilityConfigMap.put("bbpsFacility", facilityConfig.getBbpsFacility());
facilityConfigMap.put("insuranceCover", facilityConfig.getInsuranceCover());
facilityConfigMap.put("bepgFacility", facilityConfig.getBepgFacility());
facilityConfigMap.put("neftFacility", facilityConfig.getNeftFacility());
facilityConfigMap.put("rtgsFacility", facilityConfig.getRtgsFacility());
facilityConfigMap.put("interBankTotalCapFacility", facilityConfig.getInterBankTotalCapFacility());
facilityConfigMap.put("withdrawalFacility", facilityConfig.getWithdrawalFacility());
facilityConfigMap.put("depositFacilityKyc", facilityConfig.getDepositFacilityKyc());
facilityConfigMap.put("virtualDebitCard", facilityConfig.getVirtualDebitCard());
product.setAccountFacilityConfig(facilityConfigMap);
}
// Fetch Facility Limits
List<CurrentProductFacilityLimit> facilityLimits = facilityLimitRepository.findByCurrentProductId(productId);
if (facilityLimits != null && !facilityLimits.isEmpty()) {
List<Map<String, Object>> limits = facilityLimits.stream().map(limit -> {
Map<String, Object> limitMap = new HashMap<>();
limitMap.put("limitType", limit.getLimitType());
limitMap.put("generalLimit", limit.getGeneralLimit());
limitMap.put("minorAccountLimit", limit.getMinorAccountLimit());
limitMap.put("smallSavingLimit", limit.getSmallSavingLimit());
return limitMap;
}).toList();
product.setAccountFacilityLimits(limits);
}
// Fetch Deposit Limit KYC
CurrentProductDepositLimitKyc depositLimitKyc = depositLimitKycRepository.findByCurrentProductId(productId);
if (depositLimitKyc != null) {
Map<String, Object> depositLimitKycMap = new HashMap<>();
depositLimitKycMap.put("general", Map.of(
"kycType", depositLimitKyc.getGeneralKycType() != null ? depositLimitKyc.getGeneralKycType() : "",
"limit", depositLimitKyc.getGeneralLimit() != null ? depositLimitKyc.getGeneralLimit() : ""));
depositLimitKycMap.put("minor", Map.of(
"kycType", depositLimitKyc.getMinorKycType() != null ? depositLimitKyc.getMinorKycType() : "",
"limit", depositLimitKyc.getMinorLimit() != null ? depositLimitKyc.getMinorLimit() : 0L));
depositLimitKycMap.put("smallSaving", Map.of(
"kycType", depositLimitKyc.getSmallSavingKycType() != null ? depositLimitKyc.getSmallSavingKycType() : "",
"limit", depositLimitKyc.getSmallSavingLimit() != null ? depositLimitKyc.getSmallSavingLimit() : 0L));
product.setDepositLimitKyc(depositLimitKycMap);
}
}
return product;
} catch (final EmptyResultDataAccessException e) {
throw new SavingsProductNotFoundException(productId, e);
}
}
private static final class CurrentProductMapper implements RowMapper<SavingsProductData> {
private final String schemaSql;
CurrentProductMapper() {
final StringBuilder sqlBuilder = new StringBuilder(1000);
sqlBuilder.append("cp.id as id, cp.name as name, cp.short_name as shortName, cp.description as description, ");
sqlBuilder.append(
"cp.currency_code as currencyCode, cp.currency_digits as currencyDigits, cp.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("cp.nominal_annual_interest_rate as nominalAnnualInterestRate, ");
sqlBuilder.append("cp.interest_compounding_period_enum as compoundingInterestPeriodType, ");
sqlBuilder.append("cp.interest_posting_period_enum as interestPostingPeriodType, ");
sqlBuilder.append("cp.interest_calculation_type_enum as interestCalculationType, ");
sqlBuilder.append("cp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
sqlBuilder.append("cp.min_required_opening_balance as minRequiredOpeningBalance, ");
sqlBuilder.append("cp.lockin_period_frequency as lockinPeriodFrequency,");
sqlBuilder.append("cp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
sqlBuilder.append("cp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
sqlBuilder.append("cp.allow_overdraft as allowOverdraft, ");
sqlBuilder.append("cp.overdraft_limit as overdraftLimit, ");
sqlBuilder.append("cp.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, ");
sqlBuilder.append("cp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
sqlBuilder.append("cp.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("cp.enforce_min_required_balance as enforceMinRequiredBalance, ");
sqlBuilder.append("cp.max_allowed_lien_limit as maxAllowedLienLimit, ");
sqlBuilder.append("cp.is_lien_allowed as lienAllowed, ");
sqlBuilder.append("cp.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
sqlBuilder.append("cp.accounting_type as accountingType, ");
sqlBuilder.append("cp.withhold_tax as withHoldTax,");
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
sqlBuilder.append("cp.is_dormancy_tracking_active as isDormancyTrackingActive,");
sqlBuilder.append("cp.days_to_inactive as daysToInactive,");
sqlBuilder.append("cp.days_to_dormancy as daysToDormancy,");
sqlBuilder.append("cp.days_to_escheat as daysToEscheat, ");
sqlBuilder.append("cp.other_gl_mappings as otherGlMappings, ");
// Enhanced fields
sqlBuilder.append("cp.account_type as accountType, cp.customer_segment as customerSegment, cp.product_code as productCode, ");
sqlBuilder.append("cp.effective_date as effectiveDate, cp.effective_to as effectiveTo, cp.end_date as endDate, ");
sqlBuilder.append("cp.introducer_required as introducerRequired, cp.share_linking_enabled as shareLinkingEnabled, ");
sqlBuilder.append("cp.nomination_facility as nominationFacility, cp.dormancy_alert as dormancyAlert, ");
sqlBuilder.append("cp.interest_calculate_selector as interestCalculateSelector, cp.average_period as averagePeriod, ");
sqlBuilder.append("cp.auto_post as autoPost, cp.frequency_fix_day as frequencyFixDay, cp.last_interest_date as lastInterestDate, ");
sqlBuilder.append("cp.enable_lockin_period as enableLockinPeriod, ");
sqlBuilder.append("cp.balance_frequency as balanceFrequency, ");
sqlBuilder.append("cp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
sqlBuilder.append("cp.balance_swap_option as balanceSwapOption, cp.msme as msme, cp.auto_debit_facility as autoDebitFacility, ");
sqlBuilder.append("cp.avg_balance_calc_period as avgBalanceCalcPeriod, cp.valid_for_period as validForPeriod, ");
sqlBuilder.append("cp.od_account_closed_zero_balance_in_day_closing as odAccountClosedZeroBalanceInDayClosing ");
sqlBuilder.append("from m_current_product cp ");
sqlBuilder.append("join m_currency curr on curr.code = cp.currency_code ");
sqlBuilder.append("left join m_tax_group tg on tg.id = cp.tax_group_id ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public SavingsProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
final String shortName = rs.getString("shortName");
final String description = rs.getString("description");
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol,
currencyNameCode);
final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate");
final Integer compoundingInterestPeriodTypeValue = JdbcSupport.getInteger(rs, "compoundingInterestPeriodType");
final EnumOptionData compoundingInterestPeriodType = SavingsEnumerations
.compoundingInterestPeriodType(compoundingInterestPeriodTypeValue);
final Integer interestPostingPeriodTypeValue = JdbcSupport.getInteger(rs, "interestPostingPeriodType");
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(interestPostingPeriodTypeValue);
final Integer interestCalculationTypeValue = JdbcSupport.getInteger(rs, "interestCalculationType");
final EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(interestCalculationTypeValue);
EnumOptionData interestCalculationDaysInYearType = null;
final Integer interestCalculationDaysInYearTypeValue = JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType");
if (interestCalculationDaysInYearTypeValue != null) {
interestCalculationDaysInYearType = SavingsEnumerations
.interestCalculationDaysInYearType(interestCalculationDaysInYearTypeValue);
}
final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId);
final BigDecimal minRequiredOpeningBalance = rs.getBigDecimal("minRequiredOpeningBalance");
final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
EnumOptionData lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
if (lockinPeriodFrequencyTypeValue != null) {
lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodFrequencyTypeValue);
}
final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
final BigDecimal overdraftLimit = rs.getBigDecimal("overdraftLimit");
final BigDecimal nominalAnnualInterestRateOverdraft = rs.getBigDecimal("nominalAnnualInterestRateOverdraft");
final BigDecimal minOverdraftForInterestCalculation = rs.getBigDecimal("minOverdraftForInterestCalculation");
final BigDecimal minRequiredBalance = rs.getBigDecimal("minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
final BigDecimal maxAllowedLienLimit = rs.getBigDecimal("maxAllowedLienLimit");
final boolean lienAllowed = rs.getBoolean("lienAllowed");
final BigDecimal minBalanceForInterestCalculation = rs.getBigDecimal("minBalanceForInterestCalculation");
final boolean withHoldTax = rs.getBoolean("withHoldTax");
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
final String taxGroupName = rs.getString("taxGroupName");
TaxGroupData taxGroupData = null;
if (taxGroupId != null) {
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
}
final Boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive");
final Long daysToInactive = JdbcSupport.getLong(rs, "daysToInactive");
final Long daysToDormancy = JdbcSupport.getLong(rs, "daysToDormancy");
final Long daysToEscheat = JdbcSupport.getLong(rs, "daysToEscheat");
final Map<String, List<Map<String, Object>>> otherGlMappings = parseOtherGlMappings(rs.getString("otherGlMappings"));
SavingsProductData productData = SavingsProductData.instance(id, name, shortName, description, currency,
nominalAnnualInterestRate, compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance,
enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData, null, null, null,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, null);
productData.setOtherGlMappingsMap(otherGlMappings);
// Populate Enhanced Fields
productData.setAccountType(rs.getString("accountType"));
productData.setCustomerSegment(rs.getString("customerSegment"));
productData.setProductCode(rs.getString("productCode"));
productData.setEffectiveDate(JdbcSupport.getLocalDate(rs, "effectiveDate"));
productData.setEffectiveTo(JdbcSupport.getLocalDate(rs, "effectiveTo"));
productData.setEndDate(JdbcSupport.getLocalDate(rs, "endDate"));
productData.setIntroducerRequired(rs.getBoolean("introducerRequired"));
productData.setShareLinkingEnabled(rs.getBoolean("shareLinkingEnabled"));
productData.setNominationFacility(rs.getBoolean("nominationFacility"));
productData.setDormancyAlert(rs.getBoolean("dormancyAlert"));
productData.setInterestCalculateSelector(JdbcSupport.getInteger(rs, "interestCalculateSelector"));
productData.setAveragePeriod(rs.getString("averagePeriod"));
productData.setAutoPost(rs.getBoolean("autoPost"));
productData.setFrequencyFixDay(JdbcSupport.getInteger(rs, "frequencyFixDay"));
productData.setLastInterestDate(JdbcSupport.getLocalDate(rs, "lastInterestDate"));
productData.setEnableLockinPeriod(rs.getBoolean("enableLockinPeriod"));
productData.setBalanceFrequency(rs.getString("balanceFrequency"));
productData.setAccountClosedZeroBalanceInDayClosing(rs.getBoolean("accountClosedZeroBalanceInDayClosing"));
productData.setBalanceSwapOption(rs.getBoolean("balanceSwapOption"));
productData.setMsme(rs.getBoolean("msme"));
productData.setAutoDebitFacility(rs.getBoolean("autoDebitFacility"));
productData.setAvgBalanceCalcPeriod(rs.getString("avgBalanceCalcPeriod"));
productData.setValidForPeriod(rs.getString("validForPeriod"));
productData.setOdAccountClosedZeroBalanceInDayClosing(rs.getBoolean("odAccountClosedZeroBalanceInDayClosing"));
return productData;
}
}
private static final class CurrentProductLookupMapper implements RowMapper<SavingsProductData> {
public String schema() {
return " cp.id as id, cp.name as name from m_current_product cp";
}
@Override
public SavingsProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
return SavingsProductData.lookup(id, name);
}
}
private static Map<String, List<Map<String, Object>>> parseOtherGlMappings(final String otherGlMappingsJson) {
if (otherGlMappingsJson == null || otherGlMappingsJson.isBlank()) {
return null;
}
return GSON.fromJson(otherGlMappingsJson, OTHER_GL_MAPPINGS_TYPE);
}
}
@@ -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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface CurrentProductWritePlatformService {
CommandProcessingResult create(JsonCommand command);
CommandProcessingResult update(Long productId, JsonCommand command);
CommandProcessingResult delete(Long productId);
}
@@ -0,0 +1,267 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.service;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
import jakarta.persistence.PersistenceException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductAssembler;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKyc;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfig;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimit;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
import org.springframework.dao.DataAccessException;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@RequiredArgsConstructor
public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements CurrentProductWritePlatformService {
private final PlatformSecurityContext context;
private final CurrentProductRepository currentProductRepository;
private final CurrentProductDataValidator fromApiJsonDataValidator;
private final CurrentProductAssembler currentProductAssembler;
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
private final CurrentProductFacilityConfigRepository facilityConfigRepository;
private final CurrentProductFacilityLimitRepository facilityLimitRepository;
private final CurrentProductDepositLimitKycRepository depositLimitKycRepository;
private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dae) {
String msgCode = "error.msg." + DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME;
String msg = "Unknown data integrity issue with current product.";
String param = null;
Object[] msgArgs;
Throwable checkEx = realCause == null ? dae : realCause;
if (checkEx.getMessage().contains("cp_unq_name")) {
final String name = command.stringValueOfParameterNamed("name");
msgCode += ".duplicate.name";
msg = "Current product with name `" + name + "` already exists";
param = "name";
msgArgs = new Object[] { name, dae };
} else if (checkEx.getMessage().contains("cp_unq_short_name")) {
final String shortName = command.stringValueOfParameterNamed("shortName");
msgCode += ".duplicate.short.name";
msg = "Current product with short name `" + shortName + "` already exists";
param = "shortName";
msgArgs = new Object[] { shortName, dae };
} else if (checkEx.getMessage().contains("cp_unq_product_code")) {
final String productCode = command.stringValueOfParameterNamed("productCode");
msgCode += ".duplicate.product.code";
msg = "Current product with product code `" + productCode + "` already exists";
param = "productCode";
msgArgs = new Object[] { productCode, dae };
} else {
msgCode += ".unknown.data.integrity.issue";
msg = "Unknown data integrity issue with current product. Details: " + checkEx.getMessage();
msgArgs = new Object[] { dae };
}
log.error("Error occured.", dae);
throw ErrorHandler.getMappable(dae, msgCode, msg, param, msgArgs);
}
@Transactional
@Override
public CommandProcessingResult create(final JsonCommand command) {
try {
this.fromApiJsonDataValidator.validateForCreate(command.json());
final CurrentProduct product = this.currentProductAssembler.assemble(command);
final CurrentProductFacilityConfig facilityConfig =
this.currentProductAssembler.assembleAccountFacilityConfig(command, product);
final Set<CurrentProductFacilityLimit> facilityLimits =
this.currentProductAssembler.assembleAccountFacilityLimits(command, product);
final CurrentProductDepositLimitKyc depositLimitKyc =
this.currentProductAssembler.assembleDepositLimitKyc(command, product);
this.currentProductRepository.saveAndFlush(product);
if (facilityConfig != null) {
this.facilityConfigRepository.saveAndFlush(facilityConfig);
}
if (!facilityLimits.isEmpty()) {
this.facilityLimitRepository.saveAll(facilityLimits);
}
if (depositLimitKyc != null) {
this.depositLimitKycRepository.saveAndFlush(depositLimitKyc);
}
this.accountMappingWritePlatformService.createSavingProductToGLAccountMapping(product.getId(), command,
DepositAccountType.SAVINGS_DEPOSIT);
return new CommandProcessingResultBuilder() //
.withEntityId(product.getId()) //
.build();
} catch (final DataAccessException e) {
handleDataIntegrityIssues(command, e.getMostSpecificCause(), e);
return CommandProcessingResult.empty();
} catch (final PersistenceException dve) {
handleDataIntegrityIssues(command, ExceptionUtils.getRootCause(dve.getCause()), dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult update(final Long productId, final JsonCommand command) {
try {
this.context.authenticatedUser();
final CurrentProduct product = this.currentProductRepository.findById(productId)
.orElseThrow(() -> new SavingsProductNotFoundException(productId));
this.fromApiJsonDataValidator.validateForUpdate(command.json(), product);
final Map<String, Object> changes = product.update(command);
if (changes.containsKey(chargesParamName)) {
final Set<Charge> charges = this.currentProductAssembler.assembleListOfCurrentProductCharges(command,
product.currency().getCode());
final boolean updated = product.update(charges);
if (!updated) {
changes.remove(chargesParamName);
}
}
if (changes.containsKey(taxGroupIdParamName)) {
final TaxGroup taxGroup = this.currentProductAssembler.assembleTaxGroup(command);
product.setTaxGroup(taxGroup);
if (product.withHoldTax() && product.getTaxGroup() == null) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
final Long taxGroupId = null;
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
// Update facility config
final CurrentProductFacilityConfig existingFacilityConfig = product.getFacilityConfig();
final CurrentProductFacilityConfig newFacilityConfig =
this.currentProductAssembler.assembleAccountFacilityConfig(command, product);
if (newFacilityConfig != null) {
if (existingFacilityConfig != null) {
existingFacilityConfig.updateFrom(newFacilityConfig);
this.facilityConfigRepository.saveAndFlush(existingFacilityConfig);
} else {
this.facilityConfigRepository.saveAndFlush(newFacilityConfig);
}
} else if (existingFacilityConfig != null) {
this.facilityConfigRepository.delete(existingFacilityConfig);
}
// Update facility limits (delete old, insert new)
final Set<CurrentProductFacilityLimit> newFacilityLimits =
this.currentProductAssembler.assembleAccountFacilityLimits(command, product);
if (!newFacilityLimits.isEmpty() || command.hasParameter("accountFacilityLimits")) {
if (product.getFacilityLimits() != null && !product.getFacilityLimits().isEmpty()) {
this.facilityLimitRepository.deleteAll(product.getFacilityLimits());
product.getFacilityLimits().clear();
}
if (!newFacilityLimits.isEmpty()) {
this.facilityLimitRepository.saveAll(newFacilityLimits);
product.setFacilityLimits(newFacilityLimits);
}
}
// Update deposit limit KYC
final CurrentProductDepositLimitKyc existingDepositLimitKyc = product.getDepositLimitKyc();
final CurrentProductDepositLimitKyc newDepositLimitKyc =
this.currentProductAssembler.assembleDepositLimitKyc(command, product);
if (newDepositLimitKyc != null) {
if (existingDepositLimitKyc != null) {
existingDepositLimitKyc.updateFrom(newDepositLimitKyc);
this.depositLimitKycRepository.saveAndFlush(existingDepositLimitKyc);
} else {
this.depositLimitKycRepository.saveAndFlush(newDepositLimitKyc);
}
} else if (existingDepositLimitKyc != null) {
this.depositLimitKycRepository.delete(existingDepositLimitKyc);
}
// accounting related changes
final boolean accountingTypeChanged = changes.containsKey(accountingRuleParamName);
final Map<String, Object> accountingMappingChanges = this.accountMappingWritePlatformService
.updateSavingsProductToGLAccountMapping(product.getId(), command, accountingTypeChanged, product.getAccountingType(),
DepositAccountType.SAVINGS_DEPOSIT);
changes.putAll(accountingMappingChanges);
if (!changes.isEmpty()) {
this.currentProductRepository.saveAndFlush(product);
}
return new CommandProcessingResultBuilder() //
.withEntityId(product.getId()) //
.with(changes).build();
} catch (final DataAccessException e) {
handleDataIntegrityIssues(command, e.getMostSpecificCause(), e);
return CommandProcessingResult.empty();
} catch (final PersistenceException dve) {
handleDataIntegrityIssues(command, ExceptionUtils.getRootCause(dve.getCause()), dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult delete(final Long productId) {
this.context.authenticatedUser();
final CurrentProduct product = this.currentProductRepository.findById(productId)
.orElseThrow(() -> new SavingsProductNotFoundException(productId));
this.currentProductRepository.delete(product);
return new CommandProcessingResultBuilder() //
.withEntityId(product.getId()) //
.build();
}
}
@@ -0,0 +1,64 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.currentproduct.starter;
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductAssembler;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformServiceImpl;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformServiceJpaRepositoryImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class CurrentProductConfiguration {
@Bean
@ConditionalOnMissingBean(CurrentProductReadPlatformService.class)
public CurrentProductReadPlatformService currentProductReadPlatformService(PlatformSecurityContext context,
JdbcTemplate jdbcTemplate, CurrentProductFacilityConfigRepository currentFacilityConfigRepository,
CurrentProductFacilityLimitRepository currentFacilityLimitRepository,
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository) {
return new CurrentProductReadPlatformServiceImpl(context, jdbcTemplate, currentFacilityConfigRepository,
currentFacilityLimitRepository, currentDepositLimitKycRepository);
}
@Bean
@ConditionalOnMissingBean(CurrentProductWritePlatformService.class)
public CurrentProductWritePlatformService currentProductWritePlatformService(PlatformSecurityContext context,
CurrentProductRepository currentProductRepository, CurrentProductDataValidator currentProductDataValidator,
CurrentProductAssembler currentProductAssembler,
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
CurrentProductFacilityConfigRepository currentFacilityConfigRepository,
CurrentProductFacilityLimitRepository currentFacilityLimitRepository,
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository) {
return new CurrentProductWritePlatformServiceJpaRepositoryImpl(context, currentProductRepository, currentProductDataValidator,
currentProductAssembler, accountMappingWritePlatformService, currentFacilityConfigRepository,
currentFacilityLimitRepository, currentDepositLimitKycRepository);
}
}
+1
View File
@@ -34,6 +34,7 @@ dependencies {
implementation(project(path: ':fineract-charge'))
implementation(project(path: ':fineract-loan'))
implementation(project(path: ':fineract-savings'))
implementation(project(path: ':fineract-current-product'))
implementation(project(path: ':fineract-branch'))
implementation(project(path: ':fineract-document'))
implementation(project(path: ':fineract-progressive-loan'))
+18
View File
@@ -0,0 +1,18 @@
> Task :buildSrc:extractPluginRequests UP-TO-DATE
> Task :buildSrc:generatePluginAdapters UP-TO-DATE
> Task :buildSrc:compileJava UP-TO-DATE
> Task :buildSrc:compileGroovy UP-TO-DATE
> Task :buildSrc:compileGroovyPlugins UP-TO-DATE
> Task :buildSrc:pluginDescriptors UP-TO-DATE
> Task :buildSrc:processResources UP-TO-DATE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:jar UP-TO-DATE
> Configure project :
matching ref: COMMIT - d7821c7824067a96d31c4c031ea538fc5e4107fc
ref configuration: COMMIT - pattern: null
version: ${describe.tag.version.major}.${describe.tag.version.minor.next}.0-SNAPSHOT
describeTagPattern: .*(\d+\.\d+\.\d+).*
describeTagFirstParent: false
project version: 0.1.0-SNAPSHOT
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -304,6 +304,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
return chargeSchema() + " join m_share_product_charge mspc on mspc.charge_id = c.id";
}
public String currentProductChargeSchema() {
return chargeSchema() + " join m_current_product_charge cpc on cpc.charge_id = c.id";
}
@Override
public ChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
@@ -438,6 +442,17 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
return this.jdbcTemplate.query(sql, rm, new Object[] { shareProductId }); // NOSONAR
}
@Override
public List<ChargeData> retrieveCurrentProductCharges(final Long currentProductId) {
final ChargeMapper rm = new ChargeMapper();
String sql = "select " + rm.currentProductChargeSchema()
+ " where c.is_deleted=false and c.is_active=true and cpc.current_product_id=? ";
sql += addInClauseToSQL_toLimitChargesMappedToOffice_ifOfficeSpecificProductsEnabled();
return this.jdbcTemplate.query(sql, rm, new Object[] { currentProductId }); // NOSONAR
}
@Override
public List<ChargeData> retrieveSavingsAccountApplicableCharges(Long savingsAccountId) {
@@ -59,9 +59,9 @@ import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformService;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -72,7 +72,7 @@ import org.springframework.util.CollectionUtils;
public class CurrentAccountsApiResource {
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
private final SavingsAccountTemplateReadPlatformService savingsAccountTemplateReadPlatformService;
private final CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService;
private final PlatformSecurityContext context;
private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@@ -93,7 +93,7 @@ public class CurrentAccountsApiResource {
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
final SavingsAccountData savingsAccount = savingsAccountTemplateReadPlatformService.retrieveTemplate(clientId, groupId, productId,
final SavingsAccountData savingsAccount = currentAccountTemplateReadPlatformService.retrieveTemplate(clientId, groupId, productId,
staffInSelectedOfficeOnly);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiSetConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
@@ -290,7 +290,7 @@ public class CurrentAccountsApiResource {
SavingsAccountData templateData = null;
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) {
templateData = savingsAccountTemplateReadPlatformService.retrieveTemplate(savingsAccount.getClientId(),
templateData = currentAccountTemplateReadPlatformService.retrieveTemplate(savingsAccount.getClientId(),
savingsAccount.getGroupId(), savingsAccount.getSavingsProductId(), staffInSelectedOfficeOnly);
}
@@ -71,11 +71,12 @@ 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.data.SavingsProductData;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -85,7 +86,7 @@ import org.springframework.util.CollectionUtils;
@RequiredArgsConstructor
public class CurrentProductsApiResource {
private final SavingsProductReadPlatformService savingProductReadPlatformService;
private final CurrentProductReadPlatformService savingProductReadPlatformService;
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
private final CurrencyReadPlatformService currencyReadPlatformService;
private final PlatformSecurityContext context;
@@ -99,6 +100,23 @@ public class CurrentProductsApiResource {
private final TaxReadPlatformService taxReadPlatformService;
private final ConfigurationDomainService configurationDomainService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
private final JdbcTemplate jdbcTemplate;
private String generateNextProductCode(final String tableName, final String prefix) {
try {
String maxCode = jdbcTemplate.queryForObject(
"SELECT MAX(product_code) FROM " + tableName + " WHERE product_code LIKE ?",
String.class, prefix + "%");
if (maxCode == null || maxCode.isBlank()) {
return prefix + "001";
}
String numStr = maxCode.substring(prefix.length());
int num = Integer.parseInt(numStr);
return prefix + String.format("%03d", num + 1);
} catch (Exception e) {
return prefix + "001";
}
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@@ -147,7 +165,7 @@ public class CurrentProductsApiResource {
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
SavingsProductData savingProductData = savingProductReadPlatformService.retrieveOne(productId);
final Collection<ChargeData> charges = chargeReadPlatformService.retrieveSavingsProductCharges(productId);
final Collection<ChargeData> charges = chargeReadPlatformService.retrieveCurrentProductCharges(productId);
savingProductData = SavingsProductData.withCharges(savingProductData, charges);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -266,21 +284,29 @@ public class CurrentProductsApiResource {
penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions;
final Collection<TaxGroupData> taxGroupOptions = taxReadPlatformService.retrieveTaxGroupsForLookUp();
final List<CodeValueData> yesNoOptions = new ArrayList<>(
codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.YES_NO));
final List<CodeValueData> accountTypeOptions = new ArrayList<>(
codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.CURRENT_PRODUCT_ACCOUNT_TYPE));
final List<CodeValueData> customerSegmentOptions = new ArrayList<>(
codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.CURRENT_PRODUCT_CUSTOMER_SEGMENT));
SavingsProductData savingsProductToReturn = null;
if (savingsProduct != null) {
savingsProductToReturn = SavingsProductData.withTemplate(savingsProduct, currencyOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationTypeOption,
interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, periodFrequencyOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions, null, null, null,
accountMappingForPayment);
accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions, yesNoOptions,
accountTypeOptions, customerSegmentOptions, accountMappingForPayment);
} else {
savingsProductToReturn = SavingsProductData.template(currency, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationTypeOption, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
periodFrequencyOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions,
penaltyOptions, taxGroupOptions, null, null, null, accountMappingForPayment);
penaltyOptions, taxGroupOptions, yesNoOptions, accountTypeOptions, customerSegmentOptions, accountMappingForPayment);
savingsProductToReturn.setProductCode(generateNextProductCode("m_current_product", "CA-IND-"));
}
return savingsProductToReturn;
@@ -41,6 +41,7 @@ import jakarta.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
@@ -68,12 +69,16 @@ import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartDat
import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartReadPlatformService;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadService;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.data.RecurringDepositProductData;
import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformService;
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
@@ -110,6 +115,8 @@ public class RecurringDepositProductsApiResource {
private final DropdownReadPlatformService dropdownReadPlatformService;
private final PaymentTypeReadService paymentTypeReadPlatformService;
private final TaxReadPlatformService taxReadPlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
private final JdbcTemplate jdbcTemplate;
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@@ -294,6 +301,15 @@ public class RecurringDepositProductsApiResource {
final InterestRateChartData chartTemplate = this.interestRateChartReadPlatformService.template();
final Collection<TaxGroupData> taxGroupOptions = this.taxReadPlatformService.retrieveTaxGroupsForLookUp();
final Collection<EnumOptionData> interestCalculationTypeOption = retrieveInterestCalculationTypeOption();
final Collection<EnumOptionData> periodFrequencyOptions = retrievePeriodFrequencyOptions();
final List<CodeValueData> yesNoOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.YES_NO));
final List<CodeValueData> accountTypeOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.RD_PRODUCT_ACCOUNT_TYPE));
final List<CodeValueData> customerSegmentOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.RD_PRODUCT_CUSTOMER_SEGMENT));
RecurringDepositProductData recurringDepositProductToReturn = null;
if (savingsProduct != null) {
recurringDepositProductToReturn = RecurringDepositProductData.withTemplate(savingsProduct, currencyOptions,
@@ -308,11 +324,49 @@ public class RecurringDepositProductsApiResource {
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate,
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions, taxGroupOptions);
recurringDepositProductToReturn.setProductCode(generateNextProductCode("m_savings_product", "RD-IND-"));
}
recurringDepositProductToReturn.setInterestCalculationTypeOption(interestCalculationTypeOption);
recurringDepositProductToReturn.setPeriodFrequencyOptions(periodFrequencyOptions);
recurringDepositProductToReturn.setYesNoOptions(yesNoOptions);
recurringDepositProductToReturn.setAccountTypeOptions(accountTypeOptions);
recurringDepositProductToReturn.setCustomerSegmentOptions(customerSegmentOptions);
return recurringDepositProductToReturn;
}
private Collection<EnumOptionData> retrievePeriodFrequencyOptions() {
return this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.PERIOD_FREQUENCY).stream()
.map(codeValue -> new EnumOptionData(codeValue.getId(),
"periodFrequencyType." + codeValue.getName().toLowerCase(Locale.ROOT).replace(' ', '_'), codeValue.getName()))
.toList();
}
private Collection<EnumOptionData> retrieveInterestCalculationTypeOption() {
return this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.INTEREST_CALCULATION_TYPE_OPTION).stream()
.map(codeValue -> new EnumOptionData(codeValue.getId(),
"interestCalculationTypeOption." + codeValue.getName().toLowerCase(Locale.ROOT).replace(' ', '_'),
codeValue.getName()))
.toList();
}
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",
String.class, prefix + "%");
if (maxCode == null || maxCode.isBlank()) {
return prefix + "00";
}
String numStr = maxCode.substring(prefix.length());
int num = Integer.parseInt(numStr);
return prefix + String.format("%02d", num + 1);
} catch (Exception e) {
return prefix + "00";
}
}
@DELETE
@Path("{productId}")
@Consumes({ MediaType.APPLICATION_JSON })
@@ -79,6 +79,7 @@ import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -103,6 +104,23 @@ public class SavingsProductsApiResource {
private final PaymentTypeReadService paymentTypeReadPlatformService;
private final TaxReadPlatformService taxReadPlatformService;
private final ConfigurationDomainService configurationDomainService;
private final JdbcTemplate jdbcTemplate;
private String generateNextProductCode(final String tableName, final String prefix) {
try {
String maxCode = jdbcTemplate.queryForObject(
"SELECT MAX(product_code) FROM " + tableName + " WHERE product_code LIKE ?",
String.class, prefix + "%");
if (maxCode == null || maxCode.isBlank()) {
return prefix + "001";
}
String numStr = maxCode.substring(prefix.length());
int num = Integer.parseInt(numStr);
return prefix + String.format("%03d", num + 1);
} catch (Exception e) {
return prefix + "001";
}
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@@ -297,6 +315,7 @@ public class SavingsProductsApiResource {
periodFrequencyOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions,
penaltyOptions, taxGroupOptions, yesNoOptions, accountTypeOptions, customerSegmentOptions,
accountMappingForPayment);
savingsProductToReturn.setProductCode(generateNextProductCode("m_savings_product", "SB-IND-"));
}
return savingsProductToReturn;
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTPRODUCT", action = "CREATE")
public class CreateCurrentProductCommandHandler implements NewCommandSourceHandler {
private final SavingsProductWritePlatformService productWritePlatformService;
private final CurrentProductWritePlatformService productWritePlatformService;
@Autowired
public CreateCurrentProductCommandHandler(final SavingsProductWritePlatformService productWritePlatformService) {
public CreateCurrentProductCommandHandler(final CurrentProductWritePlatformService productWritePlatformService) {
this.productWritePlatformService = productWritePlatformService;
}
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTPRODUCT", action = "DELETE")
public class DeleteCurrentProductCommandHandler implements NewCommandSourceHandler {
private final SavingsProductWritePlatformService productWritePlatformService;
private final CurrentProductWritePlatformService productWritePlatformService;
@Autowired
public DeleteCurrentProductCommandHandler(final SavingsProductWritePlatformService productWritePlatformService) {
public DeleteCurrentProductCommandHandler(final CurrentProductWritePlatformService productWritePlatformService) {
this.productWritePlatformService = productWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.productWritePlatformService.delete(command.getProductId());
return this.productWritePlatformService.delete(command.entityId());
}
}
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTPRODUCT", action = "UPDATE")
public class UpdateCurrentProductCommandHandler implements NewCommandSourceHandler {
private final SavingsProductWritePlatformService productWritePlatformService;
private final CurrentProductWritePlatformService productWritePlatformService;
@Autowired
public UpdateCurrentProductCommandHandler(final SavingsProductWritePlatformService productWritePlatformService) {
public UpdateCurrentProductCommandHandler(final CurrentProductWritePlatformService productWritePlatformService) {
this.productWritePlatformService = productWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.productWritePlatformService.update(command.getProductId(), command);
return this.productWritePlatformService.update(command.entityId(), command);
}
}
@@ -0,0 +1,375 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.savings.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.charge.util.ConvertChargeDataToSpecificChargeData;
import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimelineData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.CollectionUtils;
public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAccountTemplateReadPlatformService {
private final PlatformSecurityContext context;
private final JdbcTemplate jdbcTemplate;
private final ClientReadPlatformService clientReadPlatformService;
private final GroupReadPlatformService groupReadPlatformService;
private final CurrentProductReadPlatformService currentProductReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
private final ChargeReadPlatformService chargeReadPlatformService;
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
public CurrentAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
final CurrentProductReadPlatformService currentProductReadPlatformService,
final StaffReadPlatformService staffReadPlatformService,
final SavingsDropdownReadPlatformService dropdownReadPlatformService,
final ChargeReadPlatformService chargeReadPlatformService,
final EntityDatatableChecksReadService entityDatatableChecksReadService,
final ColumnValidator columnValidator) {
this.context = context;
this.jdbcTemplate = jdbcTemplate;
this.clientReadPlatformService = clientReadPlatformService;
this.groupReadPlatformService = groupReadPlatformService;
this.currentProductReadPlatformService = currentProductReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService;
this.chargeReadPlatformService = chargeReadPlatformService;
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
}
@Override
public SavingsAccountData retrieveTemplate(final Long clientId, final Long groupId, final Long productId,
final boolean staffInSelectedOfficeOnly) {
final AppUser loggedInUser = this.context.authenticatedUser();
Long officeId = loggedInUser.getOffice().getId();
ClientData client = null;
if (clientId != null) {
client = this.clientReadPlatformService.retrieveOne(clientId);
officeId = client.getOfficeId();
}
GroupGeneralData group = null;
if (groupId != null) {
group = this.groupReadPlatformService.retrieveOne(groupId);
officeId = group.getOfficeId();
}
final Collection<SavingsProductData> productOptions = this.currentProductReadPlatformService.retrieveAllForLookup();
SavingsAccountData template = null;
if (productId != null) {
final CurrentAccountTemplateMapper mapper = new CurrentAccountTemplateMapper(client, group);
final String sql = "select " + mapper.schema() + " where cp.id = ?";
template = this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { productId }); // NOSONAR
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = this.dropdownReadPlatformService
.retrieveCompoundingInterestPeriodTypeOptions();
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = this.dropdownReadPlatformService
.retrieveInterestPostingPeriodTypeOptions();
final Collection<EnumOptionData> interestCalculationTypeOptions = this.dropdownReadPlatformService
.retrieveInterestCalculationTypeOptions();
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService
.retrieveInterestCalculationDaysInYearTypeOptions();
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService
.retrieveLockinPeriodFrequencyTypeOptions();
final Collection<EnumOptionData> withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<ChargeData> productCharges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId);
final Collection<SavingsAccountChargeData> charges = fromChargesToSavingsCharges(productCharges);
final boolean feeChargesOnly = false;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
Collection<StaffData> fieldOfficerOptions = null;
if (officeId != null) {
if (staffInSelectedOfficeOnly) {
final Collection<StaffData> fieldOfficersInBranch = this.staffReadPlatformService
.retrieveAllLoanOfficersInOfficeById(officeId);
if (!CollectionUtils.isEmpty(fieldOfficersInBranch)) {
fieldOfficerOptions = new ArrayList<>(fieldOfficersInBranch);
}
} else {
final boolean restrictToLoanOfficersOnly = true;
final Collection<StaffData> loanOfficersInHierarchy = this.staffReadPlatformService
.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, restrictToLoanOfficersOnly);
if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) {
fieldOfficerOptions = new ArrayList<>(loanOfficersInHierarchy);
}
}
}
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
charges, chargeOptions);
} else {
String clientName = null;
if (client != null) {
clientName = client.getDisplayName();
}
String groupName = null;
if (group != null) {
groupName = group.getName();
}
template = SavingsAccountData.withClientTemplate(clientId, clientName, groupId, groupName);
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsAccountChargeData> charges = null;
final boolean feeChargesOnly = false;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
charges, chargeOptions);
}
final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService.retrieveTemplates(StatusEnum.CREATE.getValue(),
EntityTables.SAVINGS.getName(), productId);
template.setDatatables(datatableTemplates);
return template;
}
private static final class CurrentAccountTemplateMapper implements RowMapper<SavingsAccountData> {
private final ClientData client;
private final GroupGeneralData group;
private final String schemaSql;
CurrentAccountTemplateMapper(final ClientData client, final GroupGeneralData group) {
this.client = client;
this.group = group;
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("cp.id as productId, cp.name as productName, ");
sqlBuilder.append(
"cp.currency_code as currencyCode, cp.currency_digits as currencyDigits, cp.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("cp.nominal_annual_interest_rate as nominalAnnualIterestRate, ");
sqlBuilder.append("cp.interest_compounding_period_enum as interestCompoundingPeriodType, ");
sqlBuilder.append("cp.interest_posting_period_enum as interestPostingPeriodType, ");
sqlBuilder.append("cp.interest_calculation_type_enum as interestCalculationType, ");
sqlBuilder.append("cp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
sqlBuilder.append("cp.min_required_opening_balance as minRequiredOpeningBalance, ");
sqlBuilder.append("cp.lockin_period_frequency as lockinPeriodFrequency,");
sqlBuilder.append("cp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
sqlBuilder.append("cp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
sqlBuilder.append("cp.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
sqlBuilder.append("cp.allow_overdraft as allowOverdraft, ");
sqlBuilder.append("cp.overdraft_limit as overdraftLimit, ");
sqlBuilder.append("cp.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, ");
sqlBuilder.append("cp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
sqlBuilder.append("cp.withhold_tax as withHoldTax,");
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
sqlBuilder.append("cp.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("cp.enforce_min_required_balance as enforceMinRequiredBalance, ");
sqlBuilder.append("0 as maxAllowedLienLimit, ");
sqlBuilder.append("0 as lienAllowed ");
sqlBuilder.append("from m_current_product cp ");
sqlBuilder.append("join m_currency curr on curr.code = cp.currency_code ");
sqlBuilder.append("left join m_tax_group tg on tg.id = cp.tax_group_id ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public SavingsAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long productId = rs.getLong("productId");
final String productName = rs.getString("productName");
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol,
currencyNameCode);
final BigDecimal nominalAnnualIterestRate = rs.getBigDecimal("nominalAnnualIterestRate");
final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations.compoundingInterestPeriodType(
SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestCompoundingPeriodType")));
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(
SavingsPostingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
final EnumOptionData interestCalculationType = SavingsEnumerations
.interestCalculationType(SavingsInterestCalculationType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));
final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations.interestCalculationDaysInYearType(
SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType")));
final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredOpeningBalance");
final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
EnumOptionData lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
if (lockinPeriodFrequencyTypeValue != null) {
final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType);
}
final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"nominalAnnualInterestRateOverdraft");
final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"minOverdraftForInterestCalculation");
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"minBalanceForInterestCalculation");
final boolean withHoldTax = rs.getBoolean("withHoldTax");
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
final String taxGroupName = rs.getString("taxGroupName");
TaxGroupData taxGroupData = null;
if (taxGroupId != null) {
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
}
Long clientId = null;
String clientName = null;
if (this.client != null) {
clientId = this.client.getId();
clientName = this.client.getDisplayName();
}
Long groupId = null;
String groupName = null;
if (this.group != null) {
groupId = this.group.getId();
groupName = this.group.getName();
}
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
final SavingsAccountSummaryData summary = null;
final BigDecimal onHoldFunds = null;
final BigDecimal savingsAmountOnHold = null;
final SavingsAccountSubStatusEnumData subStatus = null;
final String reasonForBlock = null;
final LocalDate lastActiveTransactionDate = null;
final boolean isDormancyTrackingActive = false;
final Integer daysToInactive = null;
final Integer daysToDormancy = null;
final Integer daysToEscheat = null;
final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault();
final EnumOptionData depositType = null;
return SavingsAccountData.instance(null, null, depositType, null, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, subStatus, reasonForBlock, timeline, currency,
nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
}
private Collection<SavingsAccountChargeData> fromChargesToSavingsCharges(final Collection<ChargeData> productCharges) {
final Collection<SavingsAccountChargeData> savingsCharges = new ArrayList<>();
for (final ChargeData chargeData : productCharges) {
final SavingsAccountChargeData savingsCharge = ConvertChargeDataToSpecificChargeData.toSavingsAccountChargeData(chargeData);
savingsCharges.add(savingsCharge);
}
return savingsCharges;
}
}
@@ -298,7 +298,12 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
sqlBuilder.append("dptp.min_deposit_term_type_enum as minDepositTermTypeId, ");
sqlBuilder.append("dptp.max_deposit_term_type_enum as maxDepositTermTypeId, ");
sqlBuilder.append("dptp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, ");
sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId ");
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.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("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 ");
@@ -342,10 +347,27 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
final EnumOptionData inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null
: SavingsEnumerations.depositTermFrequencyType(inMultiplesOfDepositTermTypeId);
return RecurringDepositProductData.instance(depositProductData, preClosurePenalApplicable, preClosurePenalInterest,
final String accountType = rs.getString("accountType");
final String customerSegment = rs.getString("customerSegment");
final String productCode = rs.getString("productCode");
final Integer interestCalculateSelector = JdbcSupport.getInteger(rs, "interestCalculateSelector");
final String averagePeriod = rs.getString("averagePeriod");
final Boolean autoPost = rs.getBoolean("autoPost");
final Integer frequencyFixDay = JdbcSupport.getInteger(rs, "frequencyFixDay");
RecurringDepositProductData result = RecurringDepositProductData.instance(depositProductData,
preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, isMandatoryDeposit, allowWithdrawal,
adjustAdvanceTowardsFuturePayments, minDepositAmount, depositAmount, maxDepositAmount);
result.setAccountType(accountType);
result.setCustomerSegment(customerSegment);
result.setProductCode(productCode);
result.setInterestCalculateSelector(interestCalculateSelector);
result.setAveragePeriod(averagePeriod);
result.setAutoPost(autoPost);
result.setFrequencyFixDay(frequencyFixDay);
return result;
}
}
@@ -63,6 +63,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
private final SavingsProductDepositLimitKycRepository depositLimitKycRepository;
private static final Gson GSON = new Gson();
private static final Type OTHER_GL_MAPPINGS_TYPE = new TypeToken<List<Map<String, Object>>>() {}.getType();
private static final Type OTHER_GL_MAPPINGS_MAP_TYPE = new TypeToken<Map<String, List<Map<String, Object>>>>() {}.getType();
@Override
public Collection<SavingsProductData> retrieveAll() {
@@ -300,7 +301,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
final Long daysToInactive = JdbcSupport.getLong(rs, "daysToInactive");
final Long daysToDormancy = JdbcSupport.getLong(rs, "daysToDormancy");
final Long daysToEscheat = JdbcSupport.getLong(rs, "daysToEscheat");
final List<Map<String, Object>> otherGlMappings = parseOtherGlMappings(rs.getString("otherGlMappings"));
final String otherGlMappingsJson = rs.getString("otherGlMappings");
final List<Map<String, Object>> otherGlMappings = parseOtherGlMappings(otherGlMappingsJson);
final Map<String, List<Map<String, Object>>> otherGlMappingsMap = parseOtherGlMappingsAsMap(otherGlMappingsJson);
SavingsProductData productData = SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate,
compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
@@ -309,6 +312,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
lienAllowed, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation,
withHoldTax, taxGroupData, null, null, null, isDormancyTrackingActive, daysToInactive, daysToDormancy,
daysToEscheat, otherGlMappings);
if (otherGlMappingsMap != null) {
productData.setOtherGlMappingsMap(otherGlMappingsMap);
}
// Populate Enhanced Fields
productData.setAccountType(rs.getString("accountType"));
@@ -354,7 +360,22 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
if (otherGlMappingsJson == null || otherGlMappingsJson.isBlank()) {
return null;
}
return GSON.fromJson(otherGlMappingsJson, OTHER_GL_MAPPINGS_TYPE);
com.google.gson.JsonElement element = GSON.fromJson(otherGlMappingsJson, com.google.gson.JsonElement.class);
if (!element.isJsonArray()) {
return null;
}
return GSON.fromJson(element, OTHER_GL_MAPPINGS_TYPE);
}
private static Map<String, List<Map<String, Object>>> parseOtherGlMappingsAsMap(final String otherGlMappingsJson) {
if (otherGlMappingsJson == null || otherGlMappingsJson.isBlank()) {
return null;
}
com.google.gson.JsonElement element = GSON.fromJson(otherGlMappingsJson, com.google.gson.JsonElement.class);
if (!element.isJsonObject()) {
return null;
}
return GSON.fromJson(element, OTHER_GL_MAPPINGS_MAP_TYPE);
}
@Override
@@ -129,6 +129,9 @@ import org.apache.fineract.portfolio.savings.service.SavingsAccountInterestPosti
import org.apache.fineract.portfolio.savings.service.SavingsAccountInterestPostingServiceImpl;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformServiceImpl;
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformServiceImpl;
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformServiceImpl;
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
@@ -361,6 +364,19 @@ public class SavingsConfiguration {
entityDatatableChecksReadService, columnValidator);
}
@Bean
@ConditionalOnMissingBean(CurrentAccountTemplateReadPlatformService.class)
public CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService(PlatformSecurityContext context,
JdbcTemplate jdbcTemplate, ClientReadPlatformService clientReadPlatformService,
GroupReadPlatformService groupReadPlatformService, CurrentProductReadPlatformService currentProductReadPlatformService,
StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService,
ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService,
ColumnValidator columnValidator) {
return new CurrentAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService,
currentProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService,
entityDatatableChecksReadService, columnValidator);
}
@Bean
@ConditionalOnMissingBean(SavingsAccountWritePlatformService.class)
public SavingsAccountWritePlatformService savingsAccountWritePlatformService(PlatformSecurityContext context,
@@ -460,4 +476,5 @@ public class SavingsConfiguration {
public SavingsSchedularInterestPosterTask savingsSchedularInterestPosterTask(SavingsSchedularInterestPoster interestPoster) {
return new SavingsSchedularInterestPosterTask(interestPoster);
}
}
@@ -269,4 +269,6 @@
<include file="parts/0248_add_cin_din_verification_fields.xml" relativeToChangelogFile="true"/>
<include file="parts/0249_add_non_person_extended_fields.xml" relativeToChangelogFile="true"/>
<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"/>
</databaseChangeLog>
@@ -0,0 +1,390 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<!-- Create m_current_product table (same structure as m_savings_product) -->
<changeSet id="0255_create_current_product_table" author="system">
<createTable tableName="m_current_product">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="name" type="varchar(100)">
<constraints nullable="false" unique="true" uniqueConstraintName="cp_unq_name"/>
</column>
<column name="short_name" type="varchar(4)">
<constraints nullable="false" unique="true" uniqueConstraintName="cp_unq_short_name"/>
</column>
<column name="description" type="varchar(500)">
<constraints nullable="false"/>
</column>
<column name="deposit_type_enum" type="smallint" defaultValue="400">
<constraints nullable="false"/>
</column>
<column name="currency_code" type="varchar(3)">
<constraints nullable="false"/>
</column>
<column name="currency_digits" type="smallint">
<constraints nullable="false"/>
</column>
<column name="currency_multiplesof" type="smallint">
<constraints nullable="true"/>
</column>
<column name="nominal_annual_interest_rate" type="decimal(19,6)">
<constraints nullable="false"/>
</column>
<column name="interest_compounding_period_enum" type="smallint">
<constraints nullable="false"/>
</column>
<column name="interest_posting_period_enum" type="smallint" defaultValue="4">
<constraints nullable="false"/>
</column>
<column name="interest_calculation_type_enum" type="smallint">
<constraints nullable="false"/>
</column>
<column name="interest_calculation_days_in_year_type_enum" type="smallint">
<constraints nullable="false"/>
</column>
<column name="min_required_opening_balance" type="decimal(19,6)">
<constraints nullable="true"/>
</column>
<column name="lockin_period_frequency" type="int">
<constraints nullable="true"/>
</column>
<column name="lockin_period_frequency_enum" type="smallint">
<constraints nullable="true"/>
</column>
<column name="accounting_type" type="smallint">
<constraints nullable="false"/>
</column>
<column name="withdrawal_fee_for_transfer" type="boolean" defaultValue="true">
<constraints nullable="false"/>
</column>
<column name="allow_overdraft" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="overdraft_limit" type="decimal(19,6)">
<constraints nullable="true"/>
</column>
<column name="nominal_annual_interest_rate_overdraft" type="decimal(19,6)" defaultValue="0.000000">
<constraints nullable="true"/>
</column>
<column name="min_overdraft_for_interest_calculation" type="decimal(19,6)" defaultValue="0.000000">
<constraints nullable="true"/>
</column>
<column name="enforce_min_required_balance" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="min_required_balance" type="decimal(19,6)">
<constraints nullable="true"/>
</column>
<column name="is_lien_allowed" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="max_allowed_lien_limit" type="decimal(19,6)">
<constraints nullable="true"/>
</column>
<column name="min_balance_for_interest_calculation" type="decimal(19,6)">
<constraints nullable="true"/>
</column>
<column name="withhold_tax" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="tax_group_id" type="bigint">
<constraints nullable="true"/>
</column>
<column name="is_dormancy_tracking_active" type="boolean">
<constraints nullable="true"/>
</column>
<column name="days_to_inactive" type="int">
<constraints nullable="true"/>
</column>
<column name="days_to_dormancy" type="int">
<constraints nullable="true"/>
</column>
<column name="days_to_escheat" type="int">
<constraints nullable="true"/>
</column>
<column name="other_gl_mappings" type="longtext">
<constraints nullable="true"/>
</column>
<!-- Enhanced fields -->
<column name="account_type" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="customer_segment" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="product_code" type="varchar(20)">
<constraints nullable="true" unique="true" uniqueConstraintName="cp_unq_product_code"/>
</column>
<column name="effective_date" type="date">
<constraints nullable="true"/>
</column>
<column name="effective_to" type="date">
<constraints nullable="true"/>
</column>
<column name="end_date" type="date">
<constraints nullable="true"/>
</column>
<column name="introducer_required" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="share_linking_enabled" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="nomination_facility" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="dormancy_alert" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="interest_calculate_selector" type="int">
<constraints nullable="true"/>
</column>
<column name="average_period" type="varchar(20)">
<constraints nullable="true"/>
</column>
<column name="auto_post" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="frequency_fix_day" type="int">
<constraints nullable="true"/>
</column>
<column name="last_interest_date" type="date">
<constraints nullable="true"/>
</column>
<column name="enable_lockin_period" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
<column name="balance_frequency" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="account_closed_zero_balance_in_day_closing" type="boolean" defaultValue="false">
<constraints nullable="true"/>
</column>
</createTable>
<addForeignKeyConstraint baseTableName="m_current_product" baseColumnNames="tax_group_id"
constraintName="fk_current_product_tax_group"
referencedTableName="m_tax_group" referencedColumnNames="id"/>
</changeSet>
<!-- Create m_current_product_charge join table -->
<changeSet id="0256_create_current_product_charge_table" author="system">
<createTable tableName="m_current_product_charge">
<column name="current_product_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="charge_id" type="bigint">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey tableName="m_current_product_charge" columnNames="current_product_id,charge_id"
constraintName="pk_current_product_charge"/>
<addForeignKeyConstraint baseTableName="m_current_product_charge" baseColumnNames="current_product_id"
constraintName="fk_current_product_charge_product"
referencedTableName="m_current_product" referencedColumnNames="id"/>
<addForeignKeyConstraint baseTableName="m_current_product_charge" baseColumnNames="charge_id"
constraintName="fk_current_product_charge_charge"
referencedTableName="m_charge" referencedColumnNames="id"/>
</changeSet>
<!-- Create m_current_product_facility_config table -->
<changeSet id="0257_create_current_product_facility_config_table" author="system">
<createTable tableName="m_current_product_facility_config">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_product_id" type="bigint">
<constraints nullable="false" unique="true" uniqueConstraintName="uq_current_product_facility_config_product_id"/>
</column>
<column name="atm_facility" type="boolean" defaultValue="false"/>
<column name="atm_withdrawal_facility" type="boolean" defaultValue="false"/>
<column name="upi_facility" type="boolean" defaultValue="false"/>
<column name="pos_transactions_facility" type="boolean" defaultValue="false"/>
<column name="cheque_book" type="boolean" defaultValue="false"/>
<column name="imps_facility" type="boolean" defaultValue="false"/>
<column name="sms_alerts" type="boolean" defaultValue="false"/>
<column name="bbps_facility" type="boolean" defaultValue="false"/>
<column name="insurance_cover" type="boolean" defaultValue="false"/>
<column name="bepg_facility" type="boolean" defaultValue="false"/>
<column name="neft_facility" type="boolean" defaultValue="false"/>
<column name="rtgs_facility" type="boolean" defaultValue="false"/>
<column name="inter_bank_total_cap_facility" type="boolean" defaultValue="false"/>
<column name="withdrawal_facility" type="boolean" defaultValue="false"/>
<column name="deposit_facility_kyc" type="boolean" defaultValue="false"/>
<column name="virtual_debit_card" type="boolean" defaultValue="false"/>
<column name="created_date" type="datetime">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseTableName="m_current_product_facility_config" baseColumnNames="current_product_id"
constraintName="fk_current_product_facility_config_product_id"
referencedTableName="m_current_product" referencedColumnNames="id"
onDelete="CASCADE"/>
</changeSet>
<!-- Create m_current_product_facility_limits table -->
<changeSet id="0258_create_current_product_facility_limits_table" author="system">
<createTable tableName="m_current_product_facility_limits">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_product_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="limit_type" type="varchar(100)">
<constraints nullable="false"/>
</column>
<column name="general_limit" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="minor_account_limit" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="small_saving_limit" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="created_date" type="datetime">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseTableName="m_current_product_facility_limits" baseColumnNames="current_product_id"
constraintName="fk_current_product_facility_limits_product_id"
referencedTableName="m_current_product" referencedColumnNames="id"
onDelete="CASCADE"/>
<createIndex tableName="m_current_product_facility_limits" indexName="idx_current_product_facility_limits_product_type">
<column name="current_product_id"/>
<column name="limit_type"/>
</createIndex>
</changeSet>
<!-- Create m_current_product_deposit_limit_kyc table -->
<changeSet id="0259_create_current_product_deposit_limit_kyc_table" author="system">
<createTable tableName="m_current_product_deposit_limit_kyc">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_product_id" type="bigint">
<constraints nullable="false" unique="true" uniqueConstraintName="uq_current_product_deposit_limit_kyc_product_id"/>
</column>
<column name="general_kyc_type" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="general_limit" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="minor_kyc_type" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="minor_limit" type="bigint">
<constraints nullable="true"/>
</column>
<column name="small_saving_kyc_type" type="varchar(50)">
<constraints nullable="true"/>
</column>
<column name="small_saving_limit" type="bigint">
<constraints nullable="true"/>
</column>
<column name="created_date" type="datetime">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseTableName="m_current_product_deposit_limit_kyc" baseColumnNames="current_product_id"
constraintName="fk_current_product_deposit_limit_kyc_product_id"
referencedTableName="m_current_product" referencedColumnNames="id"
onDelete="CASCADE"/>
</changeSet>
<!-- Add new UI fields to m_current_product -->
<changeSet id="0262_add_current_product_new_ui_fields" author="system">
<addColumn tableName="m_current_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="avg_balance_calc_period" type="varchar(10)">
<constraints nullable="true"/>
</column>
<column name="valid_for_period" type="varchar(10)">
<constraints nullable="true"/>
</column>
<column name="od_account_closed_zero_balance_in_day_closing" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<!-- Insert CurrentProductAccountType code and values (separate from SavingsProductAccountType) -->
<changeSet id="0260_add_current_product_account_type_code" author="system">
<sql>
INSERT INTO m_code (code_name, is_system_defined)
SELECT 'CurrentProductAccountType', false
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'CurrentProductAccountType');
</sql>
<sql>
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
SELECT c.id, v.code_value, v.order_position, true, false
FROM m_code c,
(VALUES
('Standard Account', 1),
('Basic Account', 2),
('Premium Current Account', 3)
) AS v(code_value, order_position)
WHERE c.code_name = 'CurrentProductAccountType'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
);
</sql>
</changeSet>
<!-- Insert CurrentProductCustomerSegment code and values (separate from SavingsProductCustomerSegment) -->
<changeSet id="0261_add_current_product_customer_segment_code" author="system">
<sql>
INSERT INTO m_code (code_name, is_system_defined)
SELECT 'CurrentProductCustomerSegment', false
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'CurrentProductCustomerSegment');
</sql>
<sql>
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
SELECT c.id, v.code_value, v.order_position, true, false
FROM m_code c,
(VALUES
('Non Individual', 1)
) AS v(code_value, order_position)
WHERE c.code_name = 'CurrentProductCustomerSegment'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
);
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,51 @@
<?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">
<!-- RecurringDepositProductAccountType code and values -->
<changeSet id="0262_add_rd_product_account_type_code" author="system">
<sql>
INSERT INTO m_code (code_name, is_system_defined)
SELECT 'RecurringDepositProductAccountType', false
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'RecurringDepositProductAccountType');
</sql>
<sql>
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
SELECT c.id, v.code_value, v.order_position, true, false
FROM m_code c,
(VALUES
('Rd Account', 1)
) AS v(code_value, order_position)
WHERE c.code_name = 'RecurringDepositProductAccountType'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
);
</sql>
</changeSet>
<!-- RecurringDepositProductCustomerSegment code and values -->
<changeSet id="0263_add_rd_product_customer_segment_code" author="system">
<sql>
INSERT INTO m_code (code_name, is_system_defined)
SELECT 'RecurringDepositProductCustomerSegment', false
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'RecurringDepositProductCustomerSegment');
</sql>
<sql>
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
SELECT c.id, v.code_value, v.order_position, true, false
FROM m_code c,
(VALUES
('Individual', 1),
('Non-Individual', 2)
) AS v(code_value, order_position)
WHERE c.code_name = 'RecurringDepositProductCustomerSegment'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
);
</sql>
</changeSet>
</databaseChangeLog>
@@ -41,7 +41,7 @@ public class SavingsApiSetConstants extends SavingsApiConstants {
"interestCalculationTypeOptions", "interestCalculationTypeOption", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions",
"withdrawalFeeTypeOptions", nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
withHoldTaxParamName, taxGroupIdParamName, isDormancyTrackingActiveParamName, daysToInactiveParamName,
daysToDormancyParamName, daysToInactiveParamName, accountMappingForPaymentParamName, "otherGlMappings", "accountTypeOptions",
daysToDormancyParamName, daysToInactiveParamName, accountMappingForPaymentParamName, "otherGlMappings", "otherGlMappingsMap", "accountTypeOptions",
"customerSegmentOptions", "yesNoOptions",
// Enhanced custom fields
"accountType", "customerSegment", "productCode", "effectiveDate", "effectiveTo", "endDate",
@@ -51,7 +51,13 @@ public class SavingsApiSetConstants extends SavingsApiConstants {
"accountFacilityConfig", "accountFacilityLimits", "depositLimitKyc",
"allowOverdraft", "overdraftLimit", "minRequiredBalance", "enforceMinRequiredBalance",
"maxAllowedLienLimit", "lienAllowed", "minBalanceForInterestCalculation",
"accountingRule", "accountingMappings", "charges", "periodFrequencyOptions"));
"accountingRule", "accountingMappings", "charges", "periodFrequencyOptions",
// New current product fields
"balanceSwapOption", "msme", "autoDebitFacility",
"avgBalanceCalcPeriod", "validForPeriod", "odAccountClosedZeroBalanceInDayClosing",
// Accounting detail mappings
"paymentChannelToFundSourceMappings", "feeToIncomeAccountMappings", "penaltyToIncomeAccountMappings",
"paymentTypeOptions", "taxGroup", "taxGroupOptions", "accountingMappingOptions"));
/**
* These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to
@@ -56,6 +56,33 @@ public final class RecurringDepositProductData extends DepositProductData {
private Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions;
private Collection<EnumOptionData> periodFrequencyTypeOptions;
// CBS extended fields
private Collection<EnumOptionData> interestCalculationTypeOption;
private Collection<EnumOptionData> periodFrequencyOptions;
private java.util.List<org.apache.fineract.infrastructure.codes.data.CodeValueData> yesNoOptions;
private java.util.List<org.apache.fineract.infrastructure.codes.data.CodeValueData> accountTypeOptions;
private java.util.List<org.apache.fineract.infrastructure.codes.data.CodeValueData> customerSegmentOptions;
private String accountType;
private String customerSegment;
private String productCode;
private Integer interestCalculateSelector;
private String averagePeriod;
private Boolean autoPost;
private Integer frequencyFixDay;
public void setInterestCalculationTypeOption(Collection<EnumOptionData> v) { this.interestCalculationTypeOption = v; }
public void setPeriodFrequencyOptions(Collection<EnumOptionData> v) { this.periodFrequencyOptions = v; }
public void setYesNoOptions(java.util.List<org.apache.fineract.infrastructure.codes.data.CodeValueData> v) { this.yesNoOptions = v; }
public void setAccountTypeOptions(java.util.List<org.apache.fineract.infrastructure.codes.data.CodeValueData> v) { this.accountTypeOptions = v; }
public void setCustomerSegmentOptions(java.util.List<org.apache.fineract.infrastructure.codes.data.CodeValueData> v) { this.customerSegmentOptions = v; }
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 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 static RecurringDepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule,
@@ -0,0 +1,27 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.savings.service;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
public interface CurrentAccountTemplateReadPlatformService {
SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly);
}
@@ -163,4 +163,29 @@
);
</sql>
</changeSet>
<changeSet author="fineract" id="2005-6-savings-product-customer-segment-remove-corporate">
<sql>
DELETE FROM m_code_value
WHERE code_value = 'Corporate'
AND code_id = (SELECT id FROM m_code WHERE code_name = 'SavingsProductCustomerSegment');
</sql>
</changeSet>
<changeSet author="fineract" id="2005-7-savings-product-customer-segment-add-non-individual">
<sql>
INSERT INTO m_code (code_name, is_system_defined)
SELECT 'SavingsProductCustomerSegment', TRUE
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'SavingsProductCustomerSegment');
INSERT INTO m_code_value (code_id, code_value, order_position, code_description, is_active, is_mandatory)
SELECT c.id, 'Non Individual', 3, 'Non Individual', TRUE, FALSE
FROM m_code c
WHERE c.code_name = 'SavingsProductCustomerSegment'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv
WHERE cv.code_id = c.id AND cv.code_value = 'Non Individual'
);
</sql>
</changeSet>
</databaseChangeLog>
+1
View File
@@ -63,6 +63,7 @@ include ':fineract-tax'
include ':fineract-loan-origination'
include ':fineract-loan'
include ':fineract-savings'
include ':fineract-current-product'
include ':fineract-report'
include ':fineract-mix'
include ':fineract-war'