feat: senior citizen interest rate fix for FD/RD accounts + CBS table renames
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s

- Apply senior citizen rate override in FixedDepositAccount and RecurringDepositAccount
  getEffectiveInterestRateAsFraction() so maturity is calculated at correct rate (e.g. 7.5%)
  instead of chart slab rate on every event: submit, activate, posting, pre-closure, maturity
- Override getEffectiveInterestRateAsFractionAccrual() in both FD/RD to keep accrual
  consistent with maturity calculation — independent of DB nominalAnnualInterestRate value
- Add isSeniorCitizen and seniorCitizenInterestRate fields to SavingsAccount entity with
  JPA mapping and accessor methods; updateNominalAnnualInterestRate() helper added
- Extend DepositsApiConstants whitelist and SavingsAccountAssembler.applyCbsFields()
  to read and persist senior citizen fields from API request as final override step
- Add DepositAccountAssembler early-stage rate override for senior citizen accounts
- Liquibase migration 2015: add is_senior_citizen and senior_citizen_interest_rate columns
  to m_savings_account table
- Liquibase migration 0300: rename fd_base_interest_rate tables to CBS naming convention
- Update CBS copyright header in FdBaseInterestRateEffective and FdBaseInterestRateSlabHistory

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-05-21 20:57:17 +05:30
co-authored by Claude Sonnet 4.6
parent 9f627bde61
commit ea2dfd789d
12 changed files with 3820 additions and 3665 deletions
@@ -120,6 +120,8 @@ public final class DepositsApiConstants {
public static final String principalAmountParamName = "principalAmount"; public static final String principalAmountParamName = "principalAmount";
public static final String annualInterestRateParamName = "annualInterestRate"; public static final String annualInterestRateParamName = "annualInterestRate";
public static final String interestPostingPeriodInMonthsParamName = "interestPostingPeriodInMonths"; public static final String interestPostingPeriodInMonthsParamName = "interestPostingPeriodInMonths";
public static final String isSeniorCitizenParamName = "isSeniorCitizen";
public static final String seniorCitizenInterestRateParamName = "seniorCitizenInterestRate";
public static final String tenureInMonthsParamName = "tenureInMonths"; public static final String tenureInMonthsParamName = "tenureInMonths";
public static final String interestCompoundingPeriodInMonthsParamName = "interestCompoundingPeriodInMonths"; public static final String interestCompoundingPeriodInMonthsParamName = "interestCompoundingPeriodInMonths";
@@ -544,6 +546,8 @@ public final class DepositsApiConstants {
fixedDepositRequestData.add(SavingsApiConstants.jointHoldersParamName); fixedDepositRequestData.add(SavingsApiConstants.jointHoldersParamName);
fixedDepositRequestData.add(SavingsApiConstants.autoDebitFacilityParamName); fixedDepositRequestData.add(SavingsApiConstants.autoDebitFacilityParamName);
fixedDepositRequestData.add(SavingsApiConstants.nominationFacilityParamName); fixedDepositRequestData.add(SavingsApiConstants.nominationFacilityParamName);
fixedDepositRequestData.add(isSeniorCitizenParamName);
fixedDepositRequestData.add(seniorCitizenInterestRateParamName);
return fixedDepositRequestData; return fixedDepositRequestData;
} }
@@ -612,6 +616,8 @@ public final class DepositsApiConstants {
recurringDepositRequestData.add(SavingsApiConstants.balanceSwapOptionParamName); recurringDepositRequestData.add(SavingsApiConstants.balanceSwapOptionParamName);
recurringDepositRequestData.add(SavingsApiConstants.msmeParamName); recurringDepositRequestData.add(SavingsApiConstants.msmeParamName);
recurringDepositRequestData.add(SavingsApiConstants.autoDebitFacilityParamName); recurringDepositRequestData.add(SavingsApiConstants.autoDebitFacilityParamName);
recurringDepositRequestData.add(isSeniorCitizenParamName);
recurringDepositRequestData.add(seniorCitizenInterestRateParamName);
return recurringDepositRequestData; return recurringDepositRequestData;
} }
@@ -31,6 +31,8 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.deposit
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isCalendarInheritedParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isCalendarInheritedParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isMandatoryDepositParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isMandatoryDepositParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isSeniorCitizenParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.seniorCitizenInterestRateParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.mandatoryRecommendedDepositAmountParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.mandatoryRecommendedDepositAmountParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
@@ -230,6 +232,16 @@ public class DepositAccountAssembler {
interestRate = product.nominalAnnualInterestRate(); interestRate = product.nominalAnnualInterestRate();
} }
// Senior citizen interest rate override: use the higher rate when applicable
if (command.parameterExists(isSeniorCitizenParamName) && command.booleanPrimitiveValueOfParameterNamed(isSeniorCitizenParamName)) {
if (command.parameterExists(seniorCitizenInterestRateParamName)) {
final BigDecimal scRate = command.bigDecimalValueOfParameterNamed(seniorCitizenInterestRateParamName);
if (scRate != null && scRate.compareTo(BigDecimal.ZERO) > 0) {
interestRate = scRate;
}
}
}
SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null; SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null;
final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName); final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName);
if (interestPeriodTypeValue != null) { if (interestPeriodTypeValue != null) {
@@ -22,7 +22,7 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@Entity @Entity
@Table(name = "m_fd_base_interest_rate_effective") @Table(name = "m_cbs_base_interest_rate_effective")
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
public class FdBaseInterestRateEffective { public class FdBaseInterestRateEffective {
@@ -23,7 +23,7 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@Entity @Entity
@Table(name = "m_fd_base_interest_rate_slab_history") @Table(name = "m_cbs_base_interest_rate_slab_history")
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
public class FdBaseInterestRateSlabHistory { public class FdBaseInterestRateSlabHistory {
@@ -182,6 +182,12 @@ public class FixedDepositAccount extends SavingsAccount {
final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount(); final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount();
applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate, this.client); applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate, this.client);
// CBS: override with senior citizen rate if applicable
if (isSeniorCitizen() && getSeniorCitizenInterestRate() != null
&& getSeniorCitizenInterestRate().compareTo(BigDecimal.ZERO) > 0) {
applicableInterestRate = getSeniorCitizenInterestRate();
}
if (applyPreMaturePenalty) { if (applyPreMaturePenalty) {
applicableInterestRate = applicableInterestRate.subtract(penalInterest); applicableInterestRate = applicableInterestRate.subtract(penalInterest);
applicableInterestRate = applicableInterestRate.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : applicableInterestRate; applicableInterestRate = applicableInterestRate.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : applicableInterestRate;
@@ -192,6 +198,16 @@ public class FixedDepositAccount extends SavingsAccount {
return applicableInterestRate.divide(BigDecimal.valueOf(100L), mc); return applicableInterestRate.divide(BigDecimal.valueOf(100L), mc);
} }
@Override
public BigDecimal getEffectiveInterestRateAsFractionAccrual(final MathContext mc, final LocalDate upToInterestCalculationDate) {
// CBS: accrual must also use senior citizen rate to stay consistent with maturity calculation
if (isSeniorCitizen() && getSeniorCitizenInterestRate() != null
&& getSeniorCitizenInterestRate().compareTo(BigDecimal.ZERO) > 0) {
return getSeniorCitizenInterestRate().divide(BigDecimal.valueOf(100L), mc);
}
return super.getEffectiveInterestRateAsFractionAccrual(mc, upToInterestCalculationDate);
}
public void updateMaturityDateAndAmountBeforeAccountActivation(final MathContext mc, final boolean isPreMatureClosure, public void updateMaturityDateAndAmountBeforeAccountActivation(final MathContext mc, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
List<SavingsAccountTransaction> allTransactions = new ArrayList<>(); List<SavingsAccountTransaction> allTransactions = new ArrayList<>();
@@ -234,6 +234,12 @@ public class RecurringDepositAccount extends SavingsAccount {
if (this.chart != null) { if (this.chart != null) {
applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate, this.client); applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate, this.client);
// CBS: override with senior citizen rate if applicable
if (isSeniorCitizen() && getSeniorCitizenInterestRate() != null
&& getSeniorCitizenInterestRate().compareTo(BigDecimal.ZERO) > 0) {
applicableInterestRate = getSeniorCitizenInterestRate();
}
if (applyPreMaturePenalty) { if (applyPreMaturePenalty) {
applicableInterestRate = applicableInterestRate.subtract(penalInterest); applicableInterestRate = applicableInterestRate.subtract(penalInterest);
applicableInterestRate = applicableInterestRate.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : applicableInterestRate; applicableInterestRate = applicableInterestRate.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : applicableInterestRate;
@@ -245,6 +251,16 @@ public class RecurringDepositAccount extends SavingsAccount {
return applicableInterestRate.divide(BigDecimal.valueOf(100L), mc); return applicableInterestRate.divide(BigDecimal.valueOf(100L), mc);
} }
@Override
public BigDecimal getEffectiveInterestRateAsFractionAccrual(final MathContext mc, final LocalDate upToInterestCalculationDate) {
// CBS: accrual must also use senior citizen rate to stay consistent with maturity calculation
if (isSeniorCitizen() && getSeniorCitizenInterestRate() != null
&& getSeniorCitizenInterestRate().compareTo(BigDecimal.ZERO) > 0) {
return getSeniorCitizenInterestRate().divide(BigDecimal.valueOf(100L), mc);
}
return super.getEffectiveInterestRateAsFractionAccrual(mc, upToInterestCalculationDate);
}
public void updateMaturityDateAndAmount(final MathContext mc, final boolean isPreMatureClosure, public void updateMaturityDateAndAmount(final MathContext mc, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final LocalDate maturityDate = calculateMaturityDate(); final LocalDate maturityDate = calculateMaturityDate();
@@ -42,6 +42,8 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.specialC
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.weakerSectionParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.weakerSectionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominationFacilityParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominationFacilityParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.autoDebitFacilityParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.autoDebitFacilityParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isSeniorCitizenParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.seniorCitizenInterestRateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.clientIdParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName;
@@ -394,6 +396,22 @@ public class SavingsAccountAssembler {
account.setAutoDebitFacility(this.fromApiJsonHelper.extractBooleanNamed(autoDebitFacilityParamName, element)); account.setAutoDebitFacility(this.fromApiJsonHelper.extractBooleanNamed(autoDebitFacilityParamName, element));
} }
// Senior citizen rate — runs after validateDomainRules so it is the final persisted value
if (command.parameterExists(isSeniorCitizenParamName)) {
final Boolean scFlag = this.fromApiJsonHelper.extractBooleanNamed(isSeniorCitizenParamName, element);
if (scFlag != null) {
account.setSeniorCitizen(scFlag);
if (Boolean.TRUE.equals(scFlag) && command.parameterExists(seniorCitizenInterestRateParamName)) {
final BigDecimal scRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
seniorCitizenInterestRateParamName, element);
if (scRate != null && scRate.compareTo(BigDecimal.ZERO) > 0) {
account.setSeniorCitizenInterestRate(scRate);
account.updateNominalAnnualInterestRate(scRate);
}
}
}
}
account.setGuardianName(this.fromApiJsonHelper.extractStringNamed(guardianNameParamName, element)); account.setGuardianName(this.fromApiJsonHelper.extractStringNamed(guardianNameParamName, element));
account.setGuardianCifNumber(this.fromApiJsonHelper.extractStringNamed(guardianCifNumberParamName, element)); account.setGuardianCifNumber(this.fromApiJsonHelper.extractStringNamed(guardianCifNumberParamName, element));
account.setGuardianRelationship(this.fromApiJsonHelper.extractStringNamed(guardianRelationshipParamName, element)); account.setGuardianRelationship(this.fromApiJsonHelper.extractStringNamed(guardianRelationshipParamName, element));
@@ -316,4 +316,5 @@
<include file="parts/0297_fix_unique_nominee_per_account.xml" relativeToChangelogFile="true"/> <include file="parts/0297_fix_unique_nominee_per_account.xml" relativeToChangelogFile="true"/>
<include file="parts/0298_unique_external_id_on_current_account.xml" relativeToChangelogFile="true"/> <include file="parts/0298_unique_external_id_on_current_account.xml" relativeToChangelogFile="true"/>
<include file="parts/0299_seed_indian_states.xml" relativeToChangelogFile="true"/> <include file="parts/0299_seed_indian_states.xml" relativeToChangelogFile="true"/>
<include file="parts/0300_rename_fd_base_interest_rate_tables_to_cbs.xml" relativeToChangelogFile="true"/>
</databaseChangeLog> </databaseChangeLog>
@@ -0,0 +1,40 @@
<?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">
<changeSet id="0300-1" author="cbs">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_fd_base_interest_rate_effective"/>
</preConditions>
<renameTable oldTableName="m_fd_base_interest_rate_effective"
newTableName="m_cbs_base_interest_rate_effective"/>
</changeSet>
<changeSet id="0300-2" author="cbs">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_fd_base_interest_rate_slab_history"/>
</preConditions>
<renameTable oldTableName="m_fd_base_interest_rate_slab_history"
newTableName="m_cbs_base_interest_rate_slab_history"/>
</changeSet>
</databaseChangeLog>
@@ -3969,6 +3969,12 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
@Column(name = "auto_debit_facility", nullable = false, columnDefinition = "boolean default false") @Column(name = "auto_debit_facility", nullable = false, columnDefinition = "boolean default false")
private Boolean autoDebitFacility = false; private Boolean autoDebitFacility = false;
@Column(name = "is_senior_citizen", nullable = false, columnDefinition = "boolean default false")
private boolean isSeniorCitizen = false;
@Column(name = "senior_citizen_interest_rate", scale = 6, precision = 19, nullable = true)
private BigDecimal seniorCitizenInterestRate;
@jakarta.persistence.OneToOne(cascade = jakarta.persistence.CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true, fetch = FetchType.LAZY) @jakarta.persistence.OneToOne(cascade = jakarta.persistence.CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true, fetch = FetchType.LAZY)
private SavingsAccountFacilityConfig facilityConfig; private SavingsAccountFacilityConfig facilityConfig;
@@ -4041,4 +4047,16 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
public Boolean getAutoDebitFacility() { return autoDebitFacility; } public Boolean getAutoDebitFacility() { return autoDebitFacility; }
public void setAutoDebitFacility(Boolean v) { this.autoDebitFacility = v; } public void setAutoDebitFacility(Boolean v) { this.autoDebitFacility = v; }
public boolean isSeniorCitizen() { return isSeniorCitizen; }
public void setSeniorCitizen(boolean v) { this.isSeniorCitizen = v; }
public BigDecimal getSeniorCitizenInterestRate() { return seniorCitizenInterestRate; }
public void setSeniorCitizenInterestRate(BigDecimal v) { this.seniorCitizenInterestRate = v; }
public void updateNominalAnnualInterestRate(BigDecimal rate) {
if (rate != null && rate.compareTo(BigDecimal.ZERO) > 0) {
this.nominalAnnualInterestRate = rate;
}
}
} }
@@ -37,4 +37,5 @@
<include file="parts/2012_add_current_account_permissions.xml" relativeToChangelogFile="true" /> <include file="parts/2012_add_current_account_permissions.xml" relativeToChangelogFile="true" />
<include file="parts/2013_add_missing_current_account_columns.xml" relativeToChangelogFile="true" /> <include file="parts/2013_add_missing_current_account_columns.xml" relativeToChangelogFile="true" />
<include file="parts/2014_add_is_mandatory_to_joint_holders.xml" relativeToChangelogFile="true" /> <include file="parts/2014_add_is_mandatory_to_joint_holders.xml" relativeToChangelogFile="true" />
<include file="parts/2015_add_senior_citizen_to_savings_account.xml" relativeToChangelogFile="true" />
</databaseChangeLog> </databaseChangeLog>
@@ -0,0 +1,27 @@
<?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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet id="2015_add_senior_citizen_to_savings_account" author="CBS">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_savings_account"/>
<not><columnExists tableName="m_savings_account" columnName="is_senior_citizen"/></not>
</preConditions>
<addColumn tableName="m_savings_account">
<column name="is_senior_citizen" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="senior_citizen_interest_rate" type="DECIMAL(19,6)">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>