This commit is contained in:
Anshul Rathore
2026-07-31 12:41:22 +05:30
351 changed files with 18873 additions and 1986 deletions
@@ -46,7 +46,7 @@ fineract.security.oauth2.client.registrations.frontend-client.require-authorizat
fineract.tenant.host=${FINERACT_DEFAULT_TENANTDB_HOSTNAME:localhost}
fineract.tenant.port=${FINERACT_DEFAULT_TENANTDB_PORT:5432}
fineract.tenant.username=${FINERACT_DEFAULT_TENANTDB_UID:postgres}
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:313863}
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:Keshav@898966}
fineract.tenant.parameters=${FINERACT_DEFAULT_TENANTDB_CONN_PARAMS:}
fineract.tenant.timezone=${FINERACT_DEFAULT_TENANTDB_TIMEZONE:Asia/Kolkata}
fineract.tenant.identifier=${FINERACT_DEFAULT_TENANTDB_IDENTIFIER:default}
@@ -407,7 +407,7 @@ server.tomcat.mbeanregistry.enabled=${FINERACT_SERVER_TOMCAT_MBEANREGISTRY_ENABL
spring.datasource.hikari.driverClassName=${FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME:org.postgresql.Driver}
spring.datasource.hikari.jdbcUrl=${FINERACT_HIKARI_JDBC_URL:jdbc:postgresql://localhost:5432/fineract_tenants}
spring.datasource.hikari.username=${FINERACT_HIKARI_USERNAME:postgres}
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:313863}
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:Keshav@898966}
spring.datasource.hikari.minimumIdle=${FINERACT_HIKARI_MINIMUM_IDLE:3}
spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10}
spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000}
@@ -436,4 +436,52 @@
<include file="parts/0413_inward_item_verify_signature_permission.xml" relativeToChangelogFile="true"/>
<include file="parts/0414_inward_item_full_source_details.xml" relativeToChangelogFile="true"/>
<include file="parts/0415_welcome_kit_dispatch.xml" relativeToChangelogFile="true"/>
<include file="parts/0415_add_otp_delivery_method_and_first_login_gate_to_appuser.xml" relativeToChangelogFile="true"/>
<include file="parts/0416_create_user_agreement_tables.xml" relativeToChangelogFile="true"/>
<include file="parts/0417_add_first_login_gate_config.xml" relativeToChangelogFile="true"/>
<include file="parts/0418_add_user_agreement_permissions.xml" relativeToChangelogFile="true"/>
<include file="parts/0419_revert_first_login_gate_column_and_config.xml" relativeToChangelogFile="true"/>
<include file="parts/0420_add_missing_clientkyc_action_permissions.xml" relativeToChangelogFile="true"/>
<include file="parts/0421_add_signature_id_to_client.xml" relativeToChangelogFile="true"/>
<include file="parts/0422_add_monthly_turnover_to_client.xml" relativeToChangelogFile="true"/>
<include file="parts/0423_add_annual_income_to_client.xml" relativeToChangelogFile="true"/>
<include file="parts/0424_add_senior_citizen_pwd_to_client.xml" relativeToChangelogFile="true"/>
<include file="parts/0425_add_diploma_degree_qualification.xml" relativeToChangelogFile="true"/>
<include file="parts/0426_add_graduate_education_level.xml" relativeToChangelogFile="true"/>
<include file="parts/0427_add_caretaker_relationship_value.xml" relativeToChangelogFile="true"/>
<include file="parts/0428_add_minor_kyc_id_proof_codes.xml" relativeToChangelogFile="true"/>
<include file="parts/0429_add_client_pending_edit_table.xml" relativeToChangelogFile="true"/>
<include file="parts/0430_add_client_edit_approval_permissions.xml" relativeToChangelogFile="true"/>
<include file="parts/0431_add_original_json_to_client_pending_edit.xml" relativeToChangelogFile="true"/>
<include file="parts/0432_add_entity_kyc_document_type_codes.xml" relativeToChangelogFile="true"/>
<!-- NOTE: the numbers below (0341-0359) re-use prefixes already used above by the chequebook/CTS
clearing catchup migrations — these come from a long-diverged branch (uate) that assigned its own
sequence independently. Liquibase tracks changesets by filename + id + author, not by the numeric
prefix, so this causes no functional collision; the prefixes are just no longer strictly sequential
across the whole file. Left as-is rather than renumbering, since renumbering an already-created
migration risks Liquibase re-running it on any environment where it was already applied under its
original filename/id. -->
<include file="parts/0341_add_current_account_pending_edit_table.xml" relativeToChangelogFile="true"/>
<include file="parts/0342_add_current_account_edit_approval_permissions.xml" relativeToChangelogFile="true"/>
<include file="parts/0343_create_sponsor_bank_dd_tables.xml" relativeToChangelogFile="true"/>
<include file="parts/0344_create_sponsor_dd_issue_request_tables.xml" relativeToChangelogFile="true"/>
<include file="parts/0345_add_sponsor_dd_payable_gl_account_sbi.xml" relativeToChangelogFile="true"/>
<include file="parts/0347_add_sponsor_dd_walkin_cash_support.xml" relativeToChangelogFile="true"/>
<include file="parts/0348_add_sponsor_dd_cash_charges_gl_mapping.xml" relativeToChangelogFile="true"/>
<include file="parts/0349_replace_payment_type_with_sponsor_bank.xml" relativeToChangelogFile="true"/>
<include file="parts/0350_add_sponsor_dd_cash_receipt_suspense.xml" relativeToChangelogFile="true"/>
<include file="parts/0351_add_sponsor_dd_reprint_approval.xml" relativeToChangelogFile="true"/>
<include file="parts/0352_add_sponsor_dd_payable_at.xml" relativeToChangelogFile="true"/>
<include file="parts/0353_add_sponsor_dd_gst_on_charges.xml" relativeToChangelogFile="true"/>
<include file="parts/0354_add_sponsor_bank_identification_codes.xml" relativeToChangelogFile="true"/>
<include file="parts/0355_add_sponsor_dd_cashier_deposit_link.xml" relativeToChangelogFile="true"/>
<include file="parts/0356_add_dd_breakdown_to_cashier_deposit_request.xml" relativeToChangelogFile="true"/>
<include file="parts/0357_add_dd_limit_to_sponsor_dd_gl_mapping.xml" relativeToChangelogFile="true"/>
<include file="parts/0358_add_batch_id_to_sponsor_dd_issue_request.xml" relativeToChangelogFile="true"/>
<include file="parts/0359_reclassify_dd_gst_payable_as_income.xml" relativeToChangelogFile="true"/>
<include file="parts/0360_add_resolution_number_to_share_account.xml" relativeToChangelogFile="true"/>
<include file="parts/0361_add_share_account_pending_edit_table.xml" relativeToChangelogFile="true"/>
<include file="parts/0362_add_share_account_edit_approval_permissions.xml" relativeToChangelogFile="true"/>
<include file="parts/0421_add_master_salutation_value.xml" relativeToChangelogFile="true"/>
<include file="parts/0428_add_client_risk_rules.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
@@ -0,0 +1,218 @@
<?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">
<!--
Holds a pending edit request for an ACTIVE current account's Nominee + Digital & Account
Features, mirroring m_savings_account_pending_edit's maker-checker pattern: presence of a row
here with status=PENDING (enforced via the unique pending_lock_key) is itself the lock, and
nothing is applied to the real m_current_account_facility_config / m_client_nominee rows until
a checker approves (see CurrentAccountEditWritePlatformServiceJpaRepositoryImpl). One row per
request; only one nominee change (add/update/delete) can be proposed per request, so its fields
are kept as flat typed columns on this same table rather than a satellite table. Also carries a
full point-in-time "wide snapshot" of every business column on m_current_account at submission
time, for audit/UI display purposes only.
-->
<changeSet author="fineract" id="0341-1">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_current_account_pending_edit" />
</not>
</preConditions>
<createTable tableName="m_current_account_pending_edit">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="current_account_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_caoe_current_account_id" references="m_current_account(id)" />
</column>
<column name="createdby_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_caoe_maker_id" references="m_appuser(id)" />
</column>
<column name="lastmodifiedby_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_caoe_checker_id" references="m_appuser(id)" />
</column>
<column name="created_date" type="DATETIME">
<constraints nullable="false" />
</column>
<column name="lastmodified_date" type="DATETIME">
<constraints nullable="true" />
</column>
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
<constraints nullable="false" />
</column>
<column name="reject_reason" type="VARCHAR(500)" />
<column name="pending_lock_key" type="VARCHAR(40)">
<constraints nullable="true" unique="true" uniqueConstraintName="caoe_unq_pending_lock_key" />
</column>
<column name="command_source_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_caoe_command_source_id" references="m_portfolio_command_source(id)" />
</column>
<!-- Hold placed at submission time for the total charge of newly-enabled facilities; released
(no charge) on reject, released-then-converted-to-a-posted-charge on approve. -->
<column name="hold_transaction_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_caoe_hold_txn_id" references="m_current_account_transaction(id)" />
</column>
<column name="hold_amount" type="DECIMAL(19,6)" />
<!-- Digital & Account Features - same 18 columns/names as m_current_account_facility_config.
NULL = maker did not touch this toggle; facility_config_submitted tells approve()/UI
whether this block was submitted at all. -->
<column name="facility_config_submitted" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
<column name="atm_facility" type="BOOLEAN" />
<column name="atm_withdrawal_facility" type="BOOLEAN" />
<column name="upi_facility" type="BOOLEAN" />
<column name="pos_transactions_facility" type="BOOLEAN" />
<column name="cheque_book" type="BOOLEAN" />
<column name="imps_facility" type="BOOLEAN" />
<column name="sms_alerts" type="BOOLEAN" />
<column name="bbps_facility" type="BOOLEAN" />
<column name="insurance_cover" type="BOOLEAN" />
<column name="bepg_facility" type="BOOLEAN" />
<column name="neft_facility" type="BOOLEAN" />
<column name="rtgs_facility" type="BOOLEAN" />
<column name="inter_bank_total_cap_facility" type="BOOLEAN" />
<column name="withdrawal_facility" type="BOOLEAN" />
<column name="deposit_facility_kyc" type="BOOLEAN" />
<column name="virtual_debit_card" type="BOOLEAN" />
<column name="internet_banking" type="BOOLEAN" />
<column name="mobile_banking" type="BOOLEAN" />
<!-- Nominee change - one per request (add / update / delete), same fields as m_client_nominee. -->
<column name="nominee_submitted" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
<column name="nominee_operation" type="VARCHAR(10)" />
<column name="nominee_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_caoe_nominee_id" references="m_client_nominee(id)" />
</column>
<column name="nominee_client_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_caoe_nominee_client_id" references="m_client(id)" />
</column>
<column name="relationship_cv_id" type="BIGINT" />
<column name="percentage_share" type="DECIMAL(5,2)" />
<column name="nominee_is_active" type="BOOLEAN" />
<column name="nominee_is_minor" type="BOOLEAN" />
<column name="guardian_client_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_caoe_guardian_client_id" references="m_client(id)" />
</column>
<column name="guardian_relationship_cv_id" type="BIGINT" />
<column name="effective_date" type="DATE" />
<column name="nominee_remarks" type="VARCHAR(500)" />
<!-- Wide point-in-time snapshot of every business column on m_current_account at submission
time (audit/UI display only - approve()/reject() logic reads the live account, not this).
All nullable, no unique constraints, no defaults - this is a frozen copy. -->
<column name="account_no" type="VARCHAR(20)" />
<column name="external_id" type="VARCHAR(100)" />
<column name="client_id" type="BIGINT" />
<column name="product_id" type="BIGINT" />
<column name="office_id" type="BIGINT" />
<column name="staff_id" type="BIGINT" />
<column name="status_enum" type="INTEGER" />
<column name="sub_status_enum" type="INTEGER" />
<column name="account_type_enum" type="INTEGER" />
<column name="submittedon_date" type="DATE" />
<column name="approvedon_date" type="DATE" />
<column name="activatedon_date" type="DATE" />
<column name="rejectedon_date" type="DATE" />
<column name="withdrawnon_date" type="DATE" />
<column name="closedon_date" type="DATE" />
<column name="submittedon_userid" type="BIGINT" />
<column name="approvedon_userid" type="BIGINT" />
<column name="activatedon_userid" type="BIGINT" />
<column name="rejectedon_userid" type="BIGINT" />
<column name="withdrawnon_userid" type="BIGINT" />
<column name="closedon_userid" type="BIGINT" />
<column name="currency_code" type="VARCHAR(3)" />
<column name="currency_digits" type="INTEGER" />
<column name="currency_multiplesof" type="INTEGER" />
<column name="nominal_annual_interest_rate" type="DECIMAL(19,6)" />
<column name="interest_compounding_period_enum" type="INTEGER" />
<column name="interest_posting_period_enum" type="INTEGER" />
<column name="interest_calculation_type_enum" type="INTEGER" />
<column name="interest_calculation_days_in_year_type_enum" type="INTEGER" />
<column name="min_balance_for_interest_calculation" type="DECIMAL(19,6)" />
<column name="start_interest_calculation_date" type="DATE" />
<column name="accrued_till_date" type="DATE" />
<column name="last_interest_calculation_date" type="DATE" />
<column name="last_closed_business_date" type="DATE" />
<column name="withhold_tax" type="BOOLEAN" />
<column name="min_required_opening_balance" type="DECIMAL(19,6)" />
<column name="lockin_period_frequency" type="INTEGER" />
<column name="lockin_period_frequency_enum" type="INTEGER" />
<column name="lockedin_until_date_derived" type="DATE" />
<column name="enforce_min_required_balance" type="BOOLEAN" />
<column name="min_required_balance" type="DECIMAL(19,6)" />
<column name="allow_overdraft" type="BOOLEAN" />
<column name="overdraft_limit" type="DECIMAL(19,6)" />
<column name="nominal_annual_interest_rate_overdraft" type="DECIMAL(19,6)" />
<column name="min_overdraft_for_interest_calculation" type="DECIMAL(19,6)" />
<column name="is_lien_allowed" type="BOOLEAN" />
<column name="max_allowed_lien_limit" type="DECIMAL(19,6)" />
<column name="on_hold_funds_derived" type="DECIMAL(19,6)" />
<column name="account_balance_derived" type="DECIMAL(19,6)" />
<column name="total_deposits_derived" type="DECIMAL(19,6)" />
<column name="total_withdrawals_derived" type="DECIMAL(19,6)" />
<column name="total_interest_earned_derived" type="DECIMAL(19,6)" />
<column name="total_interest_posted_derived" type="DECIMAL(19,6)" />
<column name="total_overdraft_interest_derived" type="DECIMAL(19,6)" />
<column name="total_withhold_tax_derived" type="DECIMAL(19,6)" />
<column name="total_savings_amount_on_hold" type="DECIMAL(19,6)" />
<column name="withdrawal_fee_for_transfer" type="BOOLEAN" />
<column name="reason_for_block" type="VARCHAR(500)" />
<column name="msme" type="BOOLEAN" />
<column name="auto_debit_facility" type="BOOLEAN" />
<column name="balance_swap_option" type="BOOLEAN" />
<column name="establishment_date" type="DATE" />
<column name="start_operation_date" type="DATE" />
<column name="is_non_individual_client" type="BOOLEAN" />
<column name="signatory_rights" type="VARCHAR(100)" />
<column name="cbs_account_type" type="VARCHAR(100)" />
<column name="customer_segment" type="VARCHAR(100)" />
<column name="account_status" type="VARCHAR(50)" />
<column name="mode_of_operation" type="VARCHAR(50)" />
<column name="special_category" type="VARCHAR(100)" />
<column name="introducer_client_id" type="BIGINT" />
<column name="introducer_name" type="VARCHAR(200)" />
<column name="introducer_cif_number" type="VARCHAR(50)" />
<column name="nominee_data" type="TEXT" />
<column name="interest_calculate_selector" type="INTEGER" />
<column name="average_period" type="VARCHAR(20)" />
<column name="auto_post" type="BOOLEAN" />
<column name="frequency_fix_day" type="INTEGER" />
<column name="enable_lockin_period" type="BOOLEAN" />
<column name="balance_frequency" type="VARCHAR(50)" />
<column name="avg_balance_calc_period" type="VARCHAR(10)" />
<column name="account_closed_zero_balance_in_day_closing" type="BOOLEAN" />
<column name="valid_for_period" type="VARCHAR(10)" />
<column name="od_account_closed_zero_balance_in_day_closing" type="BOOLEAN" />
<column name="is_linking_enable" type="BOOLEAN" />
<column name="terminate_true" type="BOOLEAN" />
</createTable>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,85 @@
<?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">
<!--
Permissions gating the Nominee + Digital & Account Features maker-checker edit flow for
current accounts. SUBMITEDIT lets a maker propose the edit (this is the "Edit" action shown
on the account view); APPROVEEDIT/REJECTEDIT let a checker resolve it. The _CHECKER permission
follows the "<ACTION>_<ENTITY>_CHECKER" convention so the pending request also surfaces in the
generic Checker Inbox for limited checkers. Grouping matches the rest of CURRENTACCOUNT's
permissions in 0225_add_current_account_support.xml ("portfolio").
-->
<changeSet author="fineract" id="0342-1">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'SUBMITEDIT_CURRENTACCOUNT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="portfolio" />
<column name="code" value="SUBMITEDIT_CURRENTACCOUNT" />
<column name="entity_name" value="CURRENTACCOUNT" />
<column name="action_name" value="SUBMITEDIT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="0342-2">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_CURRENTACCOUNT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="portfolio" />
<column name="code" value="APPROVEEDIT_CURRENTACCOUNT" />
<column name="entity_name" value="CURRENTACCOUNT" />
<column name="action_name" value="APPROVEEDIT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="0342-3">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECTEDIT_CURRENTACCOUNT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="portfolio" />
<column name="code" value="REJECTEDIT_CURRENTACCOUNT" />
<column name="entity_name" value="CURRENTACCOUNT" />
<column name="action_name" value="REJECTEDIT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="0342-4">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_CURRENTACCOUNT_CHECKER'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="portfolio" />
<column name="code" value="APPROVEEDIT_CURRENTACCOUNT_CHECKER" />
<column name="entity_name" value="CURRENTACCOUNT" />
<column name="action_name" value="APPROVEEDIT_CHECKER" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,143 @@
<?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">
<!-- Per sponsor-bank GL account configuration: which GL account is credited when a DD is
issued (liability owed to the sponsor bank), and which suspense GL account is debited
when the customer pays by cheque instead of an account debit. -->
<changeSet author="fineract" id="0344-1-create-gl-mapping">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_sponsor_dd_gl_mapping" />
</not>
</preConditions>
<createTable tableName="m_sponsor_dd_gl_mapping">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="payment_type_id" type="BIGINT">
<constraints nullable="false" unique="true" uniqueConstraintName="sdd_glmap_unq_payment_type"
foreignKeyName="fk_sdd_glmap_payment_type_id" references="m_payment_type(id)" />
</column>
<column name="dd_payable_gl_account_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sdd_glmap_payable_gl_id" references="acc_gl_account(id)" />
</column>
<column name="cheque_clearing_gl_account_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_clearing_gl_id" references="acc_gl_account(id)" />
</column>
<column name="created_date" type="DATETIME" defaultValueComputed="now()">
<constraints nullable="false" />
</column>
</createTable>
</changeSet>
<!-- Maker-checker queue: clerk submits a PENDING request when a customer wants a DD issued
(either by debiting their own savings/current account, or by handing over a cheque);
a manager/head-cashier approval performs the actual GL posting and flips the leaflet
to ISSUED via the existing SponsorDDWritePlatformService.issueDemandDraft(). -->
<changeSet author="fineract" id="0344-2-create-issue-request">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_sponsor_dd_issue_request" />
</not>
</preConditions>
<createTable tableName="m_sponsor_dd_issue_request">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="leaflet_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sdd_issuereq_leaflet_id" references="m_sponsor_dd_leaflet(id)" />
</column>
<column name="client_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sdd_issuereq_client_id" references="m_client(id)" />
</column>
<column name="beneficiary_name" type="VARCHAR(150)">
<constraints nullable="false" />
</column>
<column name="face_value" type="DECIMAL(19,6)">
<constraints nullable="false" />
</column>
<column name="issuing_branch_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sdd_issuereq_branch_id" references="m_office(id)" />
</column>
<column name="issued_date" type="DATE">
<constraints nullable="false" />
</column>
<!-- ACCOUNT_DEBIT or CHEQUE -->
<column name="payment_method" type="VARCHAR(20)">
<constraints nullable="false" />
</column>
<column name="savings_account_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_savings_id" references="m_savings_account(id)" />
</column>
<column name="current_account_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_current_id" references="m_current_account(id)" />
</column>
<column name="cheque_number" type="VARCHAR(30)">
<constraints nullable="true" />
</column>
<column name="cheque_bank_name" type="VARCHAR(100)">
<constraints nullable="true" />
</column>
<!-- PENDING, APPROVED, REJECTED -->
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
<constraints nullable="false" />
</column>
<column name="maker_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sdd_issuereq_maker_id" references="m_appuser(id)" />
</column>
<column name="created_date" type="DATETIME" defaultValueComputed="now()">
<constraints nullable="false" />
</column>
<column name="checker_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_checker_id" references="m_appuser(id)" />
</column>
<column name="checker_date" type="DATETIME">
<constraints nullable="true" />
</column>
<column name="rejection_reason" type="VARCHAR(255)">
<constraints nullable="true" />
</column>
<column name="resulting_savings_transaction_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_result_savings_txn" references="m_savings_account_transaction(id)" />
</column>
<column name="resulting_current_transaction_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_result_current_txn" references="m_current_account_transaction(id)" />
</column>
<column name="resulting_journal_entry_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_result_journal" references="acc_gl_journal_entry(id)" />
</column>
</createTable>
</changeSet>
<changeSet author="fineract" id="0344-3-index-issue-request-status">
<preConditions onFail="MARK_RAN">
<not>
<indexExists tableName="m_sponsor_dd_issue_request" indexName="idx_sdd_issuereq_status" />
</not>
</preConditions>
<createIndex indexName="idx_sdd_issuereq_status" tableName="m_sponsor_dd_issue_request">
<column name="status" />
</createIndex>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,73 @@
<?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">
<!-- Dedicated liability GL account for the Sponsor DD Payable balance owed to SBI, as a child
of the existing "OTHER LIABILITY" account (gl_code 9), matching the pattern already used
by "INTEREST PAYABLE" (gl_code 9.2). -->
<changeSet author="fineract" id="0345-1-insert-sponsor-dd-payable-sbi-gl-account">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.3'</sqlCheck>
</not>
</preConditions>
<insert tableName="acc_gl_account">
<column name="name" value="Sponsor DD Payable - SBI"/>
<column name="gl_code" value="9.3"/>
<column name="parent_id" valueNumeric="22"/>
<column name="hierarchy" value="."/>
<column name="account_usage" valueNumeric="1"/>
<column name="classification_enum" valueNumeric="2"/>
<column name="disabled" valueBoolean="false"/>
<column name="manual_journal_entries_allowed" valueBoolean="true"/>
<column name="description" value="Liability owed to SBI for sponsor bank demand drafts issued on their behalf, pending clearing."/>
</insert>
</changeSet>
<!-- Fineract's GL hierarchy path embeds the row's own auto-generated id, so it can only be
set after the insert above. -->
<changeSet author="fineract" id="0345-2-fix-sponsor-dd-payable-sbi-hierarchy">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.3' and hierarchy = '.'</sqlCheck>
</preConditions>
<sql>
UPDATE acc_gl_account SET hierarchy = CONCAT('.', id, '.') WHERE gl_code = '9.3' AND hierarchy = '.';
</sql>
</changeSet>
<!-- Wire the new GL account into the Sponsor DD GL mapping for the SBI payment type, so
DD issuance requests for SBI can be approved without manual configuration. -->
<changeSet author="fineract" id="0345-3-map-sponsor-dd-payable-sbi">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
select count(*) from m_sponsor_dd_gl_mapping m join m_payment_type pt on pt.id = m.payment_type_id where pt.value = 'SBI'
</sqlCheck>
</preConditions>
<sql>
INSERT INTO m_sponsor_dd_gl_mapping (payment_type_id, dd_payable_gl_account_id, created_date)
SELECT pt.id, gl.id, CURRENT_TIMESTAMP
FROM m_payment_type pt, acc_gl_account gl
WHERE pt.value = 'SBI' AND gl.gl_code = '9.3';
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,97 @@
<?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">
<!-- Support DD issuance to a walk-in person paying cash who is not a bank client at all
(not just a different-branch customer). client_id becomes optional; when absent, the
walk-in purchaser's own KYC details are captured instead. Aadhaar is stored masked only
(last 4 digits) per UIDAI/RBI guidance — the full number is never persisted. -->
<changeSet author="fineract" id="0347-1-issue-request-client-id-nullable">
<preConditions onFail="MARK_RAN">
<columnExists tableName="m_sponsor_dd_issue_request" columnName="client_id" />
</preConditions>
<dropNotNullConstraint tableName="m_sponsor_dd_issue_request" columnName="client_id" columnDataType="BIGINT" />
</changeSet>
<changeSet author="fineract" id="0347-2-issue-request-add-walkin-fields">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_sponsor_dd_issue_request" columnName="walkin_purchaser_name" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_issue_request">
<column name="walkin_purchaser_name" type="VARCHAR(150)">
<constraints nullable="true" />
</column>
<column name="walkin_purchaser_address" type="VARCHAR(255)">
<constraints nullable="true" />
</column>
<column name="walkin_purchaser_aadhaar_masked" type="VARCHAR(20)">
<constraints nullable="true" />
</column>
<column name="charges_amount" type="DECIMAL(19,6)" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="0347-3-leaflet-add-walkin-fields">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_sponsor_dd_leaflet" columnName="walkin_purchaser_name" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_leaflet">
<column name="walkin_purchaser_name" type="VARCHAR(150)">
<constraints nullable="true" />
</column>
<column name="walkin_purchaser_address" type="VARCHAR(255)">
<constraints nullable="true" />
</column>
<column name="walkin_purchaser_aadhaar_masked" type="VARCHAR(20)">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
<!-- GL accounts for a CASH payment: Dr Cash-in-Hand (face value + charges) / Cr Sponsor DD
Payable (face value) / Cr Charges Income (charges, if any). Both optional — CASH issuance
is blocked with a clear error until these are configured, matching the existing pattern
for cheque_clearing_gl_account_id. -->
<changeSet author="fineract" id="0347-4-gl-mapping-add-cash-and-charges-accounts">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="cash_gl_account_id" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_gl_mapping">
<column name="cash_gl_account_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_cash_gl_id" references="acc_gl_account(id)" />
</column>
<column name="charges_income_gl_account_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_charges_gl_id" references="acc_gl_account(id)" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,77 @@
<?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">
<!-- Dedicated income GL account for charges/commission collected on sponsor bank DD issuance,
as a child of the existing "INCOME HEAD" account (gl_code 10), matching the pattern
already used by "INCOME FROM LOAN" (gl_code 10.1) / "INCOME FROM ALL" (gl_code 10.2). -->
<changeSet author="fineract" id="0348-1-insert-dd-charges-income-gl-account">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '10.3'</sqlCheck>
</not>
</preConditions>
<insert tableName="acc_gl_account">
<column name="name" value="DD Issuance Charges Income"/>
<column name="gl_code" value="10.3"/>
<column name="parent_id" valueNumeric="25"/>
<column name="hierarchy" value="."/>
<column name="account_usage" valueNumeric="1"/>
<column name="classification_enum" valueNumeric="4"/>
<column name="disabled" valueBoolean="false"/>
<column name="manual_journal_entries_allowed" valueBoolean="true"/>
<column name="description" value="Commission/charges income collected from customers and walk-in purchasers when issuing a sponsor bank demand draft."/>
</insert>
</changeSet>
<!-- Fineract's GL hierarchy path embeds the row's own auto-generated id, so it can only be
set after the insert above. -->
<changeSet author="fineract" id="0348-2-fix-dd-charges-income-hierarchy">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '10.3' and hierarchy = '.'</sqlCheck>
</preConditions>
<sql>
UPDATE acc_gl_account SET hierarchy = CONCAT('.', id, '.') WHERE gl_code = '10.3' AND hierarchy = '.';
</sql>
</changeSet>
<!-- Wire "Cash at Tellers" (the standard Fineract GL account for cash physically received at
the teller counter, gl_code 2) and the new charges income account into the existing
Sponsor DD GL mapping row(s), so CASH-method walk-in DD issuance can be approved without
further manual configuration. -->
<changeSet author="fineract" id="0348-3-map-dd-cash-and-charges-accounts">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="0">
select count(*) from m_sponsor_dd_gl_mapping where cash_gl_account_id is null or charges_income_gl_account_id is null
</sqlCheck>
</not>
</preConditions>
<sql>
UPDATE m_sponsor_dd_gl_mapping
SET cash_gl_account_id = (select id from acc_gl_account where gl_code = '2'),
charges_income_gl_account_id = (select id from acc_gl_account where gl_code = '10.3')
WHERE cash_gl_account_id is null or charges_income_gl_account_id is null;
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet author="fineract" id="0349-0-drop-old-sponsor-bank-table">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_bank" />
<not>
<columnExists tableName="m_sponsor_bank" columnName="createdby_id" />
</not>
</preConditions>
<sql>
DROP TABLE IF EXISTS m_sponsor_bank CASCADE;
</sql>
</changeSet>
<changeSet author="fineract" id="0349-1-create-sponsor-bank-table-v3">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_sponsor_bank" />
</not>
</preConditions>
<createTable tableName="m_sponsor_bank">
<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="sdd_bank_unq_name" />
</column>
<column name="description" type="VARCHAR(255)">
<constraints nullable="true" />
</column>
<column name="createdby_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sdd_bank_maker_id" references="m_appuser(id)" />
</column>
<column name="created_date" type="DATETIME" defaultValueComputed="now()">
<constraints nullable="false" />
</column>
</createTable>
</changeSet>
<changeSet author="fineract" id="0349-2-migrate-existing-payment-types">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_bank" />
<tableExists tableName="m_payment_type" />
</preConditions>
<sql>
-- Insert unique sponsor banks from payment types into m_sponsor_bank
INSERT INTO m_sponsor_bank (name, description, createdby_id, created_date)
SELECT DISTINCT pt.value, pt.description, 1, CURRENT_TIMESTAMP
FROM m_payment_type pt
WHERE (pt.description = 'Sponsor Bank'
OR pt.id IN (SELECT DISTINCT payment_type_id FROM m_sponsor_dd_inventory))
AND pt.value NOT IN (SELECT name FROM m_sponsor_bank);
</sql>
</changeSet>
<changeSet author="fineract" id="0349-3-add-sponsor-bank-id-to-inventory">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_inventory" />
<not>
<columnExists tableName="m_sponsor_dd_inventory" columnName="sponsor_bank_id" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_inventory">
<column name="sponsor_bank_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_inv_sponsor_bank_id" references="m_sponsor_bank(id)" />
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="0349-4-update-inventory-sponsor-bank-ids">
<preConditions onFail="MARK_RAN">
<columnExists tableName="m_sponsor_dd_inventory" columnName="sponsor_bank_id" />
<columnExists tableName="m_sponsor_dd_inventory" columnName="payment_type_id" />
</preConditions>
<sql>
-- Update sponsor_bank_id based on name matching from m_payment_type
UPDATE m_sponsor_dd_inventory
SET sponsor_bank_id = (
SELECT sb.id
FROM m_sponsor_bank sb
JOIN m_payment_type pt ON pt.value = sb.name
WHERE pt.id = m_sponsor_dd_inventory.payment_type_id
LIMIT 1
)
WHERE sponsor_bank_id IS NULL;
</sql>
</changeSet>
<changeSet author="fineract" id="0349-5-set-sponsor-bank-id-not-null">
<preConditions onFail="MARK_RAN">
<columnExists tableName="m_sponsor_dd_inventory" columnName="sponsor_bank_id" />
</preConditions>
<addNotNullConstraint tableName="m_sponsor_dd_inventory" columnName="sponsor_bank_id" columnDataType="BIGINT" />
</changeSet>
<changeSet author="fineract" id="0349-6-drop-payment-type-from-inventory">
<preConditions onFail="MARK_RAN">
<columnExists tableName="m_sponsor_dd_inventory" columnName="payment_type_id" />
</preConditions>
<dropForeignKeyConstraint baseTableName="m_sponsor_dd_inventory" constraintName="fk_sdd_inv_payment_type_id" />
<dropColumn tableName="m_sponsor_dd_inventory" columnName="payment_type_id" />
</changeSet>
<changeSet author="fineract" id="0349-7-decouple-gl-mapping-payment-type">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_gl_mapping" />
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="payment_type_id" />
</preConditions>
<dropForeignKeyConstraint baseTableName="m_sponsor_dd_gl_mapping" constraintName="fk_sdd_glmap_payment_type_id" />
<dropUniqueConstraint tableName="m_sponsor_dd_gl_mapping" constraintName="sdd_glmap_unq_payment_type" />
<dropColumn tableName="m_sponsor_dd_gl_mapping" columnName="payment_type_id" />
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,119 @@
<?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">
<!-- Interim liability GL account for cash physically received from a walk-in DD purchaser at
submission time, before the manager has approved the request. Real-world equivalent of
"advance received against demand draft application" - keeps the cash movement auditable
immediately (the teller's cash-in-hand balance is correct as soon as cash is taken),
without prematurely crediting the Sponsor DD Payable liability, which is only actually
owed to the sponsor bank once the draft is approved/issued. -->
<changeSet author="fineract" id="0350-1-insert-dd-cash-suspense-gl-account">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.4'</sqlCheck>
</not>
</preConditions>
<insert tableName="acc_gl_account">
<column name="name" value="DD Cash Received - Pending Approval"/>
<column name="gl_code" value="9.4"/>
<column name="parent_id" valueNumeric="22"/>
<column name="hierarchy" value="."/>
<column name="account_usage" valueNumeric="1"/>
<column name="classification_enum" valueNumeric="2"/>
<column name="disabled" valueBoolean="false"/>
<column name="manual_journal_entries_allowed" valueBoolean="true"/>
<column name="description" value="Interim liability for cash physically received from a walk-in DD purchaser at submission time, pending manager approval of the issuance request. Cleared into Sponsor DD Payable + Charges Income on approval, or reversed back to Cash at Tellers on rejection."/>
</insert>
</changeSet>
<changeSet author="fineract" id="0350-2-fix-dd-cash-suspense-hierarchy">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.4' and hierarchy = '.'</sqlCheck>
</preConditions>
<sql>
UPDATE acc_gl_account SET hierarchy = CONCAT('.', id, '.') WHERE gl_code = '9.4' AND hierarchy = '.';
</sql>
</changeSet>
<changeSet author="fineract" id="0350-3-add-cash-suspense-to-gl-mapping">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_gl_mapping" />
<not>
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="cash_suspense_gl_account_id" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_gl_mapping">
<column name="cash_suspense_gl_account_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_cash_suspense_gl_id" references="acc_gl_account(id)" />
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="0350-4-map-dd-cash-suspense-account">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="0">
select count(*) from m_sponsor_dd_gl_mapping where cash_suspense_gl_account_id is null
</sqlCheck>
</not>
</preConditions>
<sql>
UPDATE m_sponsor_dd_gl_mapping
SET cash_suspense_gl_account_id = (select id from acc_gl_account where gl_code = '9.4')
WHERE cash_suspense_gl_account_id is null;
</sql>
</changeSet>
<!-- Audit trail: which journal entry recorded the initial cash receipt (posted at submission),
and which one reversed it (posted only if the request is later rejected). Both are nullable
since ACCOUNT_DEBIT/CHEQUE requests never populate them, and an approved CASH request only
ever gets a receipt entry (never a refund one). -->
<changeSet author="fineract" id="0350-5-add-cash-receipt-journal-entry-column">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_issue_request" />
<not>
<columnExists tableName="m_sponsor_dd_issue_request" columnName="cash_receipt_journal_entry_id" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_issue_request">
<column name="cash_receipt_journal_entry_id" type="BIGINT">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="0350-6-add-cash-refund-journal-entry-column">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_issue_request" />
<not>
<columnExists tableName="m_sponsor_dd_issue_request" columnName="cash_refund_journal_entry_id" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_issue_request">
<column name="cash_refund_journal_entry_id" type="BIGINT">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,84 @@
<?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">
<!-- A DD leaflet is a negotiable instrument - printing it a second time is a control risk
(duplicate physical drafts). The first print is free; any print after that requires a
reprint request approved by a higher authority (maker-checker), same pattern as issuance. -->
<changeSet author="fineract" id="0351-1-add-print-tracking-to-leaflet">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_leaflet" />
<not>
<columnExists tableName="m_sponsor_dd_leaflet" columnName="print_count" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_leaflet">
<column name="print_count" type="INT" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
<column name="last_printed_date" type="DATETIME">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="0351-2-create-reprint-request-table">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_sponsor_dd_reprint_request" />
</not>
</preConditions>
<createTable tableName="m_sponsor_dd_reprint_request">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="leaflet_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sdd_reprint_leaflet_id" references="m_sponsor_dd_leaflet(id)" />
</column>
<column name="reason" type="VARCHAR(500)">
<constraints nullable="false" />
</column>
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
<constraints nullable="false" />
</column>
<column name="consumed" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
<column name="maker_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sdd_reprint_maker_id" references="m_appuser(id)" />
</column>
<column name="created_date" type="DATETIME" defaultValueComputed="now()">
<constraints nullable="false" />
</column>
<column name="checker_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_reprint_checker_id" references="m_appuser(id)" />
</column>
<column name="checker_date" type="DATETIME">
<constraints nullable="true" />
</column>
<column name="rejection_reason" type="VARCHAR(1000)">
<constraints nullable="true" />
</column>
</createTable>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,61 @@
<?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">
<!-- "Payable at [Branch], [City]" - standard field on a demand draft telling the beneficiary
where it can be encashed. Nullable at the DB level (existing rows have neither); the
write service enforces it as required for new submissions. -->
<changeSet author="fineract" id="0352-1-add-payable-at-to-issue-request">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_issue_request" />
<not>
<columnExists tableName="m_sponsor_dd_issue_request" columnName="payable_at_branch" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_issue_request">
<column name="payable_at_branch" type="VARCHAR(150)">
<constraints nullable="true" />
</column>
<column name="payable_at_city" type="VARCHAR(100)">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="0352-2-add-payable-at-to-leaflet">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_leaflet" />
<not>
<columnExists tableName="m_sponsor_dd_leaflet" columnName="payable_at_branch" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_leaflet">
<column name="payable_at_branch" type="VARCHAR(150)">
<constraints nullable="true" />
</column>
<column name="payable_at_city" type="VARCHAR(100)">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,109 @@
<?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">
<!-- GST collected on DD issuance charges is money owed to the government, not bank income -
it gets its own liability GL account, separate from Charges Income. -->
<changeSet author="fineract" id="0353-1-insert-gst-payable-gl-account">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.5'</sqlCheck>
</not>
</preConditions>
<insert tableName="acc_gl_account">
<column name="name" value="GST Payable on DD Charges"/>
<column name="gl_code" value="9.5"/>
<column name="parent_id" valueNumeric="22"/>
<column name="hierarchy" value="."/>
<column name="account_usage" valueNumeric="1"/>
<column name="classification_enum" valueNumeric="2"/>
<column name="disabled" valueBoolean="false"/>
<column name="manual_journal_entries_allowed" valueBoolean="true"/>
<column name="description" value="GST collected from customers/purchasers on Sponsor DD issuance charges, held pending remittance to the government - not bank income."/>
</insert>
</changeSet>
<changeSet author="fineract" id="0353-2-fix-gst-payable-hierarchy">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.5' and hierarchy = '.'</sqlCheck>
</preConditions>
<sql>
UPDATE acc_gl_account SET hierarchy = CONCAT('.', id, '.') WHERE gl_code = '9.5' AND hierarchy = '.';
</sql>
</changeSet>
<!-- gst_percentage is deliberately a configurable column, not a hardcoded constant in Java -
it can be changed at any time (e.g. when the government revises the GST rate) via the
existing GL mapping admin endpoint, without a code deployment. Defaults to 18.00 (the
current GST rate on Indian banking service charges) for existing/new rows. -->
<changeSet author="fineract" id="0353-3-add-gst-columns-to-gl-mapping">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_gl_mapping" />
<not>
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="gst_percentage" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_gl_mapping">
<column name="gst_percentage" type="DECIMAL(5,2)" defaultValueNumeric="18.00">
<constraints nullable="false" />
</column>
<column name="gst_payable_gl_account_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_gst_gl_id" references="acc_gl_account(id)" />
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="0353-4-map-gst-payable-account">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="0">
select count(*) from m_sponsor_dd_gl_mapping where gst_payable_gl_account_id is null
</sqlCheck>
</not>
</preConditions>
<sql>
UPDATE m_sponsor_dd_gl_mapping
SET gst_payable_gl_account_id = (select id from acc_gl_account where gl_code = '9.5')
WHERE gst_payable_gl_account_id is null;
</sql>
</changeSet>
<!-- Locked in at submission time (not recomputed at approval), so a rate change between
submission and approval never silently alters a request already in flight. -->
<changeSet author="fineract" id="0353-5-add-gst-columns-to-issue-request">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_issue_request" />
<not>
<columnExists tableName="m_sponsor_dd_issue_request" columnName="gst_amount" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_issue_request">
<column name="gst_amount" type="DECIMAL(19,6)" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
<column name="gst_percentage_applied" type="DECIMAL(5,2)" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,48 @@
<?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">
<!-- Identification codes needed to route/reconcile settlement with the sponsor bank and
confirm its identity unambiguously - IFSC (settlement branch), RBI/NPCI bank code, and
SWIFT/BIC (only relevant if it's ever a foreign correspondent bank). All optional at the
DB level since existing rows have none, but the API validates IFSC format when supplied. -->
<changeSet author="fineract" id="0354-1-add-sponsor-bank-identification-codes">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_bank" />
<not>
<columnExists tableName="m_sponsor_bank" columnName="ifsc_code" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_bank">
<column name="ifsc_code" type="VARCHAR(11)">
<constraints nullable="true" />
</column>
<column name="bank_code" type="VARCHAR(20)">
<constraints nullable="true" />
</column>
<column name="swift_code" type="VARCHAR(11)">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,63 @@
<?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">
<!-- CASH DD requests no longer post their own "cash received" GL entry - the cashier records
the physical cash intake themselves via Transaction Entry (AC.TP=GL, target = the DD Cash
Suspense GL account), which is the existing, real cashier-position-tracked mechanism
(m_cashier_deposit_request + m_savings_txn_denomination). This column links a sponsor DD
issue request to the specific approved cashier deposit request that received its cash, so
we can validate the amount/account match and prevent the same cash being reused twice. -->
<changeSet author="fineract" id="0355-1-add-cashier-deposit-request-link">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_issue_request" />
<tableExists tableName="m_cashier_deposit_request" />
<not>
<columnExists tableName="m_sponsor_dd_issue_request" columnName="cashier_deposit_request_id" />
</not>
</preConditions>
<addColumn tableName="m_sponsor_dd_issue_request">
<column name="cashier_deposit_request_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_cashier_deposit_id"
references="m_cashier_deposit_request(id)" />
</column>
</addColumn>
</changeSet>
<!-- The cashier's Transaction Entry GL-deposit flow always contras cash against "Cash at Vault"
(gl_code 1), not "Cash at Teller" (gl_code 2) - see CashierDepositRequestApiResource's
executeGlJournalEntry / the frontend's loadCashVaultGl(). Repoint our cash_gl_account_id to
match, since the reject/refund leg (Dr DD Cash Suspense / Cr this account) must reverse
against the same account the cashier's approved deposit actually touched. -->
<changeSet author="fineract" id="0355-2-repoint-cash-account-to-vault">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_sponsor_dd_gl_mapping" />
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '1'</sqlCheck>
</preConditions>
<sql>
UPDATE m_sponsor_dd_gl_mapping
SET cash_gl_account_id = (select id from acc_gl_account where gl_code = '1')
WHERE cash_gl_account_id = (select id from acc_gl_account where gl_code = '2');
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,47 @@
<?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">
<!-- Transaction Entry's DD + Cash + GL row splits the single deposit amount into DD face value,
issuance charges and GST on those charges (Amount + Charges + GST, rounded up to the target
cash-collected total). Today m_cashier_deposit_request only stores that combined "amount" -
the breakdown is lost once the request is submitted, forcing whoever later issues the actual
DD leaflet in the Sponsor DD module to retype face value/charges from scratch. These columns
persist the breakdown at submit time (null for ordinary, non-DD deposit requests) so the DD
issuance screen can read it back via the linked cashier_deposit_request_id and pre-fill its
form instead of retyping. -->
<changeSet author="fineract" id="0356-1-add-dd-breakdown-to-cashier-deposit-request">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_cashier_deposit_request" />
<not>
<columnExists tableName="m_cashier_deposit_request" columnName="dd_face_value" />
</not>
</preConditions>
<addColumn tableName="m_cashier_deposit_request">
<column name="dd_face_value" type="DECIMAL(19,6)" />
<column name="dd_charges" type="DECIMAL(19,6)" />
<column name="dd_gst_percentage" type="DECIMAL(19,6)" />
<column name="dd_gst_amount" type="DECIMAL(19,6)" />
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,43 @@
<?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">
<!-- Admin-configurable ceiling for a single DD's face value, also used as the ceiling for the
combined face value of all DDs issued to the same beneficiary on the same day (structuring
control for walk-in/large-cash DD issuance). Null/unset means no limit is enforced. -->
<changeSet author="fineract" id="0357-1-add-dd-limit-amount">
<preConditions onFail="MARK_RAN">
<and>
<tableExists tableName="m_sponsor_dd_gl_mapping"/>
<not>
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="dd_limit_amount"/>
</not>
</and>
</preConditions>
<addColumn tableName="m_sponsor_dd_gl_mapping">
<column name="dd_limit_amount" type="DECIMAL(19,6)">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,60 @@
<?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">
<!-- Groups several DD issue requests submitted together as one "DD slip" (one customer account
debited/one cheque, several drafts for different beneficiaries/amounts). Set to the id of the
first row inserted for the slip - every row in the slip, including that first row, carries the
same batch_id, so the group can be queried/approved/rejected as one unit. Null for an ordinary
single-DD request (unchanged behaviour). No FK - it's a grouping tag, not a relationship to a
separate header row. -->
<changeSet author="fineract" id="0358-1-add-batch-id">
<preConditions onFail="MARK_RAN">
<and>
<tableExists tableName="m_sponsor_dd_issue_request"/>
<not>
<columnExists tableName="m_sponsor_dd_issue_request" columnName="batch_id"/>
</not>
</and>
</preConditions>
<addColumn tableName="m_sponsor_dd_issue_request">
<column name="batch_id" type="BIGINT">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="0358-2-index-batch-id">
<preConditions onFail="MARK_RAN">
<and>
<tableExists tableName="m_sponsor_dd_issue_request"/>
<not>
<indexExists tableName="m_sponsor_dd_issue_request" indexName="idx_sdd_issue_req_batch_id"/>
</not>
</and>
</preConditions>
<createIndex tableName="m_sponsor_dd_issue_request" indexName="idx_sdd_issue_req_batch_id">
<column name="batch_id"/>
</createIndex>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,41 @@
<?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">
<!-- Bank's own choice: book GST collected on DD charges as income rather than tracking it as a
payable owed to the government. Same GL account (gl_code 9.5), same id, so
m_sponsor_dd_gl_mapping.gst_payable_gl_account_id and the DD-issuance GL posting code need no
change at all - this only re-tags how the account is classified/grouped for reporting.
parent_id=25 matches the parent already used by "DD Issuance Charges Income" (gl_code 10.3,
see 0348), so both DD-related income accounts group together under the same income parent. -->
<changeSet author="fineract" id="0359-1-reclassify-gst-payable-as-income">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.5' and classification_enum = 2</sqlCheck>
</preConditions>
<sql>
UPDATE acc_gl_account
SET classification_enum = 4, parent_id = 25
WHERE gl_code = '9.5';
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,32 @@
<?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 author="fineract" id="1">
<addColumn tableName="m_share_account">
<column name="resolution_number" type="VARCHAR(100)" defaultValue="">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,81 @@
<?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">
<!--
Holds a pending edit request for a Share Account's "details" fields (linked savings account,
dividend-calculation-for-inactive-clients, lockin period, minimum active period). Presence of a
row here with status=PENDING (enforced via the unique pending_lock_key) is itself the lock -
nothing is applied to the real m_share_account row until a checker approves
(see ShareAccountEditWritePlatformServiceJpaRepositoryImpl).
-->
<changeSet author="fineract" id="1">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_share_account_pending_edit" />
</not>
</preConditions>
<createTable tableName="m_share_account_pending_edit">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="share_account_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_shaoe_share_account_id" references="m_share_account(id)" />
</column>
<column name="createdby_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_shaoe_maker_id" references="m_appuser(id)" />
</column>
<column name="lastmodifiedby_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_shaoe_checker_id" references="m_appuser(id)" />
</column>
<column name="created_date" type="DATETIME">
<constraints nullable="false" />
</column>
<column name="lastmodified_date" type="DATETIME">
<constraints nullable="true" />
</column>
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
<constraints nullable="false" />
</column>
<column name="reject_reason" type="VARCHAR(500)" />
<column name="pending_lock_key" type="VARCHAR(60)">
<constraints nullable="true" unique="true" uniqueConstraintName="shaoe_unq_pending_lock_key" />
</column>
<column name="command_source_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_shaoe_command_source_id" references="m_portfolio_command_source(id)" />
</column>
<!-- Proposed new values for the 6 editable "details" fields. NULL means the maker left that
field unchanged from the account's current value at submission time. -->
<column name="savings_account_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_shaoe_savings_account_id" references="m_savings_account(id)" />
</column>
<column name="allow_dividends_inactive_clients" type="BOOLEAN" />
<column name="lockin_period_frequency" type="INTEGER" />
<column name="lockin_period_frequency_enum" type="SMALLINT" />
<column name="minimum_active_period_frequency" type="INTEGER" />
<column name="minimum_active_period_frequency_enum" type="SMALLINT" />
</createTable>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,83 @@
<?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">
<!--
Permissions gating the Share Account "Edit Details" maker-checker flow. SUBMITEDIT lets a maker
propose the edit (the "Edit Details" action on the account view); APPROVEEDIT/REJECTEDIT let a
checker resolve it. The _CHECKER permission follows the "<ACTION>_<ENTITY>_CHECKER" convention so
the pending request also surfaces in the generic Checker Inbox for limited checkers.
-->
<changeSet author="fineract" id="1">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'SUBMITEDIT_SHAREACCOUNT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="SHAREACCOUNT" />
<column name="code" value="SUBMITEDIT_SHAREACCOUNT" />
<column name="entity_name" value="SHAREACCOUNT" />
<column name="action_name" value="SUBMITEDIT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_SHAREACCOUNT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="SHAREACCOUNT" />
<column name="code" value="APPROVEEDIT_SHAREACCOUNT" />
<column name="entity_name" value="SHAREACCOUNT" />
<column name="action_name" value="APPROVEEDIT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="3">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECTEDIT_SHAREACCOUNT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="SHAREACCOUNT" />
<column name="code" value="REJECTEDIT_SHAREACCOUNT" />
<column name="entity_name" value="SHAREACCOUNT" />
<column name="action_name" value="REJECTEDIT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="4">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_SHAREACCOUNT_CHECKER'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="SHAREACCOUNT" />
<column name="code" value="APPROVEEDIT_SHAREACCOUNT_CHECKER" />
<column name="entity_name" value="SHAREACCOUNT" />
<column name="action_name" value="APPROVEEDIT_CHECKER" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,35 @@
<?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.3.xsd">
<changeSet author="fineract" id="1">
<addColumn tableName="m_appuser">
<column name="otp_delivery_method" type="varchar(10)">
<constraints nullable="true"/>
</column>
<column name="first_login_gate_pending" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,106 @@
<?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">
<!--
Versioned admin-manageable "user agreement" text (bank policy/warning) that newly-created users
must accept during the first-login gate. Only one row may be active at a time - enforced in the
write service (UserAgreementWritePlatformServiceImpl), not a DB constraint, since flipping "active"
is a two-row transaction (deactivate old, activate new).
-->
<changeSet id="0416_create_m_user_agreement" author="fineract">
<preConditions onFail="MARK_RAN"><not><tableExists tableName="m_user_agreement"/></not></preConditions>
<createTable tableName="m_user_agreement">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="version" type="INT">
<constraints nullable="false"/>
</column>
<column name="title" type="VARCHAR(200)">
<constraints nullable="false"/>
</column>
<column name="body" type="TEXT">
<constraints nullable="false"/>
</column>
<column name="is_active" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on" type="datetime" defaultValueComputed="CURRENT_TIMESTAMP">
<constraints nullable="false"/>
</column>
</createTable>
<addUniqueConstraint tableName="m_user_agreement" columnNames="version"
constraintName="uq_user_agreement_version"/>
<addForeignKeyConstraint baseTableName="m_user_agreement" baseColumnNames="created_by"
constraintName="fk_user_agreement_created_by"
referencedTableName="m_appuser" referencedColumnNames="id"/>
</changeSet>
<!--
Audit-proof record of a user accepting a given version of the agreement during the first-login gate.
agreement_version is a denormalized snapshot of m_user_agreement.version at acceptance time, so the
acceptance record survives future version bumps unaffected.
-->
<changeSet id="0416_create_m_user_agreement_acceptance" author="fineract">
<preConditions onFail="MARK_RAN"><not><tableExists tableName="m_user_agreement_acceptance"/></not></preConditions>
<createTable tableName="m_user_agreement_acceptance">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="appuser_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="agreement_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="agreement_version" type="INT">
<constraints nullable="false"/>
</column>
<column name="accepted_on" type="datetime" defaultValueComputed="CURRENT_TIMESTAMP">
<constraints nullable="false"/>
</column>
<column name="ip_address" type="VARCHAR(45)"/>
<column name="user_agent" type="VARCHAR(500)"/>
</createTable>
<addUniqueConstraint tableName="m_user_agreement_acceptance" columnNames="appuser_id, agreement_id"
constraintName="uq_user_agreement_acceptance"/>
<addForeignKeyConstraint baseTableName="m_user_agreement_acceptance" baseColumnNames="appuser_id"
constraintName="fk_user_agreement_acceptance_user"
referencedTableName="m_appuser" referencedColumnNames="id"/>
<addForeignKeyConstraint baseTableName="m_user_agreement_acceptance" baseColumnNames="agreement_id"
constraintName="fk_user_agreement_acceptance_agreement"
referencedTableName="m_user_agreement" referencedColumnNames="id"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,34 @@
<?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.3.xsd">
<changeSet author="fineract" id="1">
<insert tableName="c_configuration">
<column name="name" value="enable-first-login-otp-and-agreement-verification"/>
<column name="value" valueNumeric="0"/>
<column name="enabled" valueBoolean="false"/>
<column name="is_trap_door" valueBoolean="false"/>
<column name="description" value="If enabled, newly created users must verify OTP via their chosen channel and accept the active user agreement on first login before access is granted. Verification also auto-enrolls that channel as ongoing MFA. Existing users are unaffected. Value unused."/>
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,27 @@
<?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">
<!--
Permissions for the admin "user agreement" management API (/v1/useragreements), used to
publish the versioned agreement text every user must accept right after logging in.
-->
<changeSet id="0418-user-agreement-create-permission" author="CBS" runOnChange="false">
<sql>
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
SELECT 'organisation', 'CREATE_USERAGREEMENT', 'USERAGREEMENT', 'CREATE', false
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_USERAGREEMENT');
</sql>
</changeSet>
<changeSet id="0418-user-agreement-read-permission" author="CBS" runOnChange="false">
<sql>
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
SELECT 'organisation', 'READ_USERAGREEMENT', 'USERAGREEMENT', 'READ', false
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'READ_USERAGREEMENT');
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,55 @@
<?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">
<!--
Reverts 0415/0417: the first-login "gate" (its own status/complete API, pending-flag, and
dedicated toggle) was replaced by simply auto-enrolling the admin-chosen OTP channel as an
already-verified m_user_mfa_factor row at user-creation time - the existing 2FA system
(MfaFactorBackedTwoFactorServiceImpl + /twofactor + TwoFactorAuthenticationComponent) then
challenges the user for OTP on their very first login with no new code at all. Neither
column nor the config toggle are read by any code anymore.
-->
<changeSet id="0419-drop-otp-delivery-method-column" author="CBS">
<preConditions onFail="MARK_RAN">
<columnExists tableName="m_appuser" columnName="otp_delivery_method"/>
</preConditions>
<dropColumn tableName="m_appuser" columnName="otp_delivery_method"/>
</changeSet>
<changeSet id="0419-drop-first-login-gate-pending-column" author="CBS">
<preConditions onFail="MARK_RAN">
<columnExists tableName="m_appuser" columnName="first_login_gate_pending"/>
</preConditions>
<dropColumn tableName="m_appuser" columnName="first_login_gate_pending"/>
</changeSet>
<changeSet id="0419-remove-first-login-gate-config" author="CBS">
<sql>
DELETE FROM c_configuration WHERE name = 'enable-first-login-otp-and-agreement-verification';
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,106 @@
<?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">
<!--
0216_add_client_kyc_tables.xml seeded 5 CLIENTKYC permissions (READ/CREATE/UPDATE/VERIFY/
REJECT), but 8 more @CommandType(entity="CLIENTKYC", ...) actions were added to the Java
command handlers afterward (GenerateAadhaarOtpClientKycCommandHandler,
VerifyAadhaarClientKycCommandHandler, VerifyPanClientKycCommandHandler,
VerifyCinClientKycCommandHandler, VerifyDinClientKycCommandHandler,
VerifyGstinClientKycCommandHandler, UploadCkycClientKycCommandHandler,
CompleteRekycClientKycCommandHandler) with no matching permission ever seeded for them.
The command-processing pipeline looks up the permission row by code BEFORE checking
whether the caller has been granted it - if the row doesn't exist at all, every user
(including a Super User with ALL_FUNCTIONS) gets "Permission with Code X does not exist",
not a normal 403. This seeds the 8 missing rows so those actions become usable at all;
granting them to specific roles is a separate, ordinary Manage Roles and Permissions step.
-->
<changeSet author="CBS" id="0420-add-missing-clientkyc-action-permissions">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(*) FROM m_permission WHERE code = 'GENERATEAADHAROTP_CLIENTKYC'
</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="GENERATEAADHAROTP_CLIENTKYC"/>
<column name="entity_name" value="CLIENTKYC"/>
<column name="action_name" value="GENERATEAADHAROTP"/>
<column name="can_maker_checker" valueBoolean="false"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="VERIFYAADHAAR_CLIENTKYC"/>
<column name="entity_name" value="CLIENTKYC"/>
<column name="action_name" value="VERIFYAADHAAR"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="VERIFYPAN_CLIENTKYC"/>
<column name="entity_name" value="CLIENTKYC"/>
<column name="action_name" value="VERIFYPAN"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="VERIFYCIN_CLIENTKYC"/>
<column name="entity_name" value="CLIENTKYC"/>
<column name="action_name" value="VERIFYCIN"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="VERIFYDIN_CLIENTKYC"/>
<column name="entity_name" value="CLIENTKYC"/>
<column name="action_name" value="VERIFYDIN"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="VERIFYGSTIN_CLIENTKYC"/>
<column name="entity_name" value="CLIENTKYC"/>
<column name="action_name" value="VERIFYGSTIN"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="UPLOADCKYC_CLIENTKYC"/>
<column name="entity_name" value="CLIENTKYC"/>
<column name="action_name" value="UPLOADCKYC"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="COMPLETEREKYC_CLIENTKYC"/>
<column name="entity_name" value="CLIENTKYC"/>
<column name="action_name" value="COMPLETEREKYC"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,43 @@
<?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.13.xsd">
<!-- ==================== Add "Master" Salutation code value ==================== -->
<changeSet author="fineract" id="0421-1">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(*) FROM m_code_value
WHERE code_id = (SELECT id FROM m_code WHERE code_name = 'Salutation')
AND code_value = 'Master'
</sqlCheck>
</preConditions>
<sql>
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
SELECT id, 'Master', (SELECT COALESCE(MAX(order_position), 0) + 1 FROM m_code_value WHERE code_id = (SELECT id FROM m_code WHERE code_name = 'Salutation')), true, false
FROM m_code
WHERE code_name = 'Salutation';
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,73 @@
<?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.3.xsd">
<!--
m_client.image_id is a single-slot pointer into m_image: every photo upload overwrites the
same row, so every reader always sees the latest photo. The client signature had no
equivalent — every upload/re-crop created a brand-new m_document row named
'clientSignature' and never touched the previous one. Readers that don't explicitly
`order by id desc` (CKYC Documents tab, view-signature dialog, delete-signature dialog)
picked the FIRST matching row from an unordered query, i.e. the oldest signature, even
after the client re-cropped it — while CkycBatchBuilderServiceImpl (which does order by id
desc) correctly zipped up the latest one. This adds a signature_id pointer mirroring
image_id so signature storage becomes single-slot too, and backfills it from the latest
existing 'clientSignature' document per client so it starts out consistent with what the
CKYC ZIP builder already considered "current".
-->
<changeSet author="CBS" id="0421-1-add-signature-id-to-client">
<addColumn tableName="m_client">
<column name="signature_id" type="BIGINT">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
<changeSet author="CBS" id="0421-2-add-signature-id-index">
<createIndex indexName="FK_m_client_m_document_signature" tableName="m_client">
<column name="signature_id"/>
</createIndex>
</changeSet>
<changeSet author="CBS" id="0421-3-add-signature-id-fk">
<addForeignKeyConstraint baseColumnNames="signature_id" baseTableName="m_client"
constraintName="FK_m_client_m_document_signature" deferrable="false" initiallyDeferred="false"
onDelete="SET NULL" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_document" validate="true"/>
</changeSet>
<changeSet author="CBS" id="0421-4-backfill-signature-id">
<sql>
UPDATE m_client c
SET signature_id = (
SELECT MAX(d.id) FROM m_document d
WHERE d.parent_entity_type = 'clients' AND d.parent_entity_id = c.id AND d.name = 'clientSignature'
)
WHERE EXISTS (
SELECT 1 FROM m_document d
WHERE d.parent_entity_type = 'clients' AND d.parent_entity_id = c.id AND d.name = 'clientSignature'
)
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,32 @@
<?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.13.xsd">
<!-- Mirrors 0238_add_annual_turnover.xml — "Annual Turnover (Last FY)" is now labelled
"Yearly Turnover" in the client form, alongside this new "Monthly Turnover" field. -->
<changeSet author="CBS" id="0422-1">
<addColumn tableName="m_client">
<column name="monthly_turnover" type="DECIMAL(19,2)" defaultValue="null"/>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,32 @@
<?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.13.xsd">
<!-- Pairs with monthly_income the same way monthly_turnover pairs with annual_turnover
(0422_add_monthly_turnover_to_client.xml) — "Annual Income" alongside "Monthly Income". -->
<changeSet author="CBS" id="0423-1">
<addColumn tableName="m_client">
<column name="annual_income" type="DECIMAL(19,2)" defaultValue="null"/>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,111 @@
<?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.13.xsd">
<!-- Senior Citizen / Persons-with-Disability flags for RBI-mandated accessible-banking
provisions (doorstep banking eligibility for senior citizens 70+ and differently-abled/
infirm customers, per RBI's customer service circulars). isSeniorCitizen is auto-detected
client-side from Date of Birth (not user-editable); PWD is a manual flag. -->
<changeSet author="CBS" id="0424-1">
<addColumn tableName="m_client">
<column name="is_senior_citizen" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="is_pwd" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<!-- pwd_category_cv_id follows the same m_code_value FK pattern as occupation_category_cv_id /
marital_status_cv_id / religion_cv_id etc — a fixed free-text column would need a Java
change every time a new disability category is needed; a code value lets an admin add,
reorder, or deactivate categories from System -> Manage Codes with zero code changes. -->
<changeSet author="CBS" id="0424-2">
<addColumn tableName="m_client">
<column name="pwd_category_cv_id" type="BIGINT">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
<changeSet author="CBS" id="0424-3">
<createIndex indexName="FK_m_client_pwd_category_cv_id" tableName="m_client">
<column name="pwd_category_cv_id"/>
</createIndex>
</changeSet>
<changeSet author="CBS" id="0424-4">
<addForeignKeyConstraint baseColumnNames="pwd_category_cv_id" baseTableName="m_client"
constraintName="FK_m_client_pwd_category_cv_id" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_code_value" validate="true"/>
</changeSet>
<!-- ==================== PwdCategory code ====================
Categories per the Rights of Persons with Disabilities Act, 2016 (RPWD Act), the same
classification referenced by RBI's accessible-banking circulars. -->
<changeSet author="CBS" id="0424-5">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_code WHERE code_name = 'PwdCategory'</sqlCheck>
</preConditions>
<insert tableName="m_code">
<column name="code_name" value="PwdCategory"/>
<column name="is_system_defined" valueBoolean="true"/>
</insert>
</changeSet>
<changeSet author="CBS" id="0424-6">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_code_value WHERE code_id = (SELECT id FROM m_code WHERE code_name = 'PwdCategory')</sqlCheck>
</preConditions>
<sql>
INSERT INTO m_code_value (code_id, code_value, code_description, order_position, is_active, is_mandatory)
SELECT id, val, val, pos, true, false FROM m_code
CROSS JOIN (VALUES
('Blindness', 1),
('Low Vision', 2),
('Leprosy Cured Persons', 3),
('Hearing Impairment (Deaf and Hard of Hearing)', 4),
('Locomotor Disability', 5),
('Dwarfism', 6),
('Intellectual Disability', 7),
('Mental Illness', 8),
('Autism Spectrum Disorder', 9),
('Cerebral Palsy', 10),
('Muscular Dystrophy', 11),
('Chronic Neurological Conditions', 12),
('Specific Learning Disabilities', 13),
('Multiple Sclerosis', 14),
('Speech and Language Disability', 15),
('Thalassemia', 16),
('Hemophilia', 17),
('Sickle Cell Disease', 18),
('Multiple Disabilities', 19),
('Acid Attack Victim', 20),
('Parkinson''s Disease', 21)
) AS t(val, pos)
WHERE code_name = 'PwdCategory';
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,83 @@
<?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.13.xsd">
<!-- "Diploma" becomes its own Education Level (alongside School / Undergraduate / Postgraduate
/ Doctorate) rather than a single generic entry nested under either of those — polytechnic
diploma holders are neither "School" nor "Undergraduate" in the strict sense. The
"Degree / Qualification" dropdown then offers diploma-specific branches when this level
is selected. -->
<changeSet id="0425_01_insert_diploma_education_level" author="CBS">
<sql>
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
SELECT c.id, 'Diploma',
(SELECT COALESCE(MAX(cv.order_position), 0) FROM m_code_value cv WHERE cv.code_id = c.id) + 1,
true, false
FROM m_code c
WHERE c.code_name = 'EducationLevel'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv WHERE cv.code_id = c.id AND cv.code_value = 'Diploma'
);
</sql>
</changeSet>
<changeSet id="0425_02_insert_diploma_degree_values" author="CBS">
<sql>
INSERT INTO m_code_value (code_id, code_value, code_description, order_position, is_active, is_mandatory)
SELECT c.id, v.code_value, 'Diploma',
(SELECT COALESCE(MAX(cv.order_position), 0) FROM m_code_value cv WHERE cv.code_id = c.id) + v.rn,
true, false
FROM m_code c
CROSS JOIN (
VALUES
('Diploma in Mechanical Engineering', 1),
('Diploma in Civil Engineering', 2),
('Diploma in Electrical Engineering', 3),
('Diploma in Electronics &amp; Communication', 4),
('Diploma in Computer Science / IT', 5),
('Diploma in Automobile Engineering', 6),
('ITI Certificate', 7),
('Polytechnic Diploma (Other)', 8),
('Other Diploma', 9)
) AS v(code_value, rn)
WHERE c.code_name = 'DegreeQualification'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv
WHERE cv.code_id = c.id AND cv.code_value = v.code_value AND cv.code_description = 'Diploma'
);
</sql>
</changeSet>
<!-- The original "Diploma" DegreeQualification row (0256_add_education_level_degree_codes.xml)
was tagged under School — it's superseded by the standalone Diploma Education Level above,
so deactivate rather than delete (existing client records referencing it must keep resolving). -->
<changeSet id="0425_03_deactivate_school_diploma_degree_value" author="CBS">
<sql>
UPDATE m_code_value cv
SET is_active = false
WHERE cv.code_id = (SELECT id FROM m_code WHERE code_name = 'DegreeQualification')
AND cv.code_value = 'Diploma'
AND cv.code_description = 'School';
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,45 @@
<?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.13.xsd">
<!-- "Undergraduate" means currently pursuing a bachelor's degree (not yet completed);
"Graduate" means it has been completed. These are distinct statuses, so Graduate gets
its own Education Level rather than being folded into Undergraduate.
No frontend change needed for degree filtering: getEducationLevelCategory() in
client-general-step.component.ts already matches any name containing "graduate"
(Undergraduate OR Graduate) to the same 'undergraduate' bucket, so both levels correctly
show the same specific-degree list (B.Tech, B.A., B.Sc., ..., Other UG). -->
<changeSet id="0426_01_insert_graduate_education_level" author="CBS">
<sql>
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
SELECT c.id, 'Graduate',
(SELECT COALESCE(MAX(cv.order_position), 0) FROM m_code_value cv WHERE cv.code_id = c.id) + 1,
true, false
FROM m_code c
WHERE c.code_name = 'EducationLevel'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv WHERE cv.code_id = c.id AND cv.code_value = 'Graduate'
);
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,45 @@
<?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.13.xsd">
<!-- Family-member validation now requires either a Father or a Caretaker (see
client-family-members-step.component.ts familyRelationshipRulesMet) — CARETAKER didn't
exist as a RELATIONSHIP code value (0241_add_client_nominee_table.xml), so a client with
no living/available father had no valid way to satisfy that rule. -->
<changeSet author="CBS" id="0427-1">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(*) FROM m_code_value cv JOIN m_code c ON c.id = cv.code_id
WHERE c.code_name = 'RELATIONSHIP' AND cv.code_value = 'CARETAKER'
</sqlCheck>
</preConditions>
<sql>
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
SELECT c.id, 'CARETAKER',
(SELECT COALESCE(MAX(cv.order_position), 0) FROM m_code_value cv WHERE cv.code_id = c.id) + 1,
true, false
FROM m_code c
WHERE c.code_name = 'RELATIONSHIP';
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,151 @@
<?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.13.xsd">
<changeSet author="fineract" id="0428-1">
<preConditions onFail="MARK_RAN">
<not><tableExists tableName="m_client_risk_rule"/></not>
</preConditions>
<createTable tableName="m_client_risk_rule">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="rule_name" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column name="legal_form_id" type="INT">
<constraints nullable="true"/>
</column>
<column name="occupation_category_cv_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="occupation_cv_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="min_monthly_income" type="DECIMAL(19,6)">
<constraints nullable="true"/>
</column>
<column name="max_monthly_income" type="DECIMAL(19,6)">
<constraints nullable="true"/>
</column>
<column name="min_annual_income" type="DECIMAL(19,6)">
<constraints nullable="true"/>
</column>
<column name="max_annual_income" type="DECIMAL(19,6)">
<constraints nullable="true"/>
</column>
<column name="min_annual_turnover" type="DECIMAL(19,6)">
<constraints nullable="true"/>
</column>
<column name="max_annual_turnover" type="DECIMAL(19,6)">
<constraints nullable="true"/>
</column>
<column name="risk_category" type="INT">
<constraints nullable="false"/>
</column>
<column name="reason" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="order_position" type="INT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet author="fineract" id="0428-2">
<preConditions onFail="MARK_RAN">
<not><foreignKeyConstraintExists foreignKeyName="FK_m_client_risk_rule_occupation_category_cv_id"/></not>
</preConditions>
<addForeignKeyConstraint baseTableName="m_client_risk_rule" baseColumnNames="occupation_category_cv_id"
referencedTableName="m_code_value" referencedColumnNames="id"
constraintName="FK_m_client_risk_rule_occupation_category_cv_id" />
</changeSet>
<changeSet author="fineract" id="0428-3">
<preConditions onFail="MARK_RAN">
<not><foreignKeyConstraintExists foreignKeyName="FK_m_client_risk_rule_occupation_cv_id"/></not>
</preConditions>
<addForeignKeyConstraint baseTableName="m_client_risk_rule" baseColumnNames="occupation_cv_id"
referencedTableName="m_code_value" referencedColumnNames="id"
constraintName="FK_m_client_risk_rule_occupation_cv_id" />
</changeSet>
<changeSet author="fineract" id="0428-4">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_client_risk_rule</sqlCheck>
</preConditions>
<sql>
INSERT INTO m_client_risk_rule (rule_name, legal_form_id, occupation_category_cv_id, occupation_cv_id, min_monthly_income, min_annual_income, min_annual_turnover, risk_category, reason, order_position)
VALUES
('Politician / Elected Representative', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Politician / Elected Representative' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 3, 'Politically Exposed Person (PEP) / Elected Representative', 10),
('Real Estate Dealer', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Real Estate / Property Dealer' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 3, 'High Risk Industry (Real Estate / Property Dealing)', 20),
('Importer / Exporter', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Importer / Exporter' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 3, 'High Risk Trade (Import / Export)', 30),
('Commission Agent', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Commission Agent' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 3, 'High Risk Business Type (Commission Agent)', 40),
('Financial Advisor', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Financial Advisor' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 3, 'High Risk Professional (Financial Advisor)', 50),
('High Monthly Income (> 2 Lakhs)', 1, null, null, 200000.0, null, null, 3, 'High Income threshold (> ₹2L Monthly)', 60),
('High Annual Income (> 20 Lakhs)', 1, null, null, null, 2000000.0, null, 3, 'High Income threshold (> ₹20L Annual)', 70),
('High Business Turnover (> 1 Crore)', 1, null, null, null, null, 10000000.0, 3, 'High Business Turnover (> ₹1 Crore Annual)', 80),
('NRI Account Profile', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'NRI (Non-Resident Indian)' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Non-Resident Indian (NRI) account profile', 90),
('PIO / OCI Profile', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'PIO / OCI' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Foreign/Overseas Citizen profile (PIO / OCI)', 100),
('Business Owner / Proprietor', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Business Owner / Proprietor' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed / Business owner profile', 110),
('Trader / Merchant', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Trader / Merchant' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed / Business owner profile', 120),
('Manufacturer', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Manufacturer' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed / Business owner profile', 130),
('Contractor', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Contractor' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed / Business owner profile', 140),
('Doctor (Private Practice)', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Doctor (Private Practice)' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed Professional profile', 150),
('Lawyer / Advocate', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Lawyer / Advocate' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed Professional profile', 160),
('Chartered Accountant', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Chartered Accountant' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed Professional profile', 170),
('Company Secretary', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Company Secretary' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed Professional profile', 180),
('Architect', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Architect' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed Professional profile', 190),
('Consultant', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Consultant' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed Professional profile', 200),
('IT Professional / Freelancer', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'IT Professional / Freelancer' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed Professional profile', 210),
('Auditor', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Auditor' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 2, 'Self-Employed Professional profile', 220),
('Business / Self Employed Category', 1, (SELECT id FROM m_code_value WHERE code_value = 'Business / Self Employed' AND code_id = (SELECT id FROM m_code WHERE code_name = 'OccupationCategory')), null, null, null, null, 2, 'Business / Self-Employed Category', 230),
('Professional Category', 1, (SELECT id FROM m_code_value WHERE code_value = 'Professional' AND code_id = (SELECT id FROM m_code WHERE code_name = 'OccupationCategory')), null, null, null, null, 2, 'Professional Category', 240),
('Medium Monthly Income (> 50K)', 1, null, null, 50000.0, null, null, 2, 'Medium Income threshold (> ₹50K Monthly)', 250),
('Medium Annual Income (> 6 Lakhs)', 1, null, null, null, 600000.0, null, 2, 'Medium Income threshold (> ₹6L Annual)', 260),
('Medium Business Turnover (> 20 Lakhs)', 1, null, null, null, null, 2000000.0, 2, 'Medium Business Turnover (> ₹20L Annual)', 270),
('Student Profile', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Student' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Non-Earning profile (Student)', 280),
('Housewife / Homemaker Profile', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Housewife / Homemaker' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Non-Earning profile (Homemaker)', 290),
('Dependent / Minor Profile', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Dependent / Minor' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Non-Earning profile (Dependent / Minor)', 300),
('Unemployed Profile', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Unemployed' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Non-Earning profile (Unemployed)', 310),
('Government Employee', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Government Employee' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk employment (Government Employee)', 320),
('PSU Employee', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Public Sector Undertaking (PSU) Employee' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk employment (PSU Employee)', 330),
('Defence Personnel', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Defence / Armed Forces Personnel' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk employment (Defence Personnel)', 340),
('Police / Paramilitary', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Police / Paramilitary' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk employment (Police / Paramilitary)', 350),
('Teacher / Professor', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Teacher / Professor' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk employment (Teacher / Professor)', 360),
('Bank Employee', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Bank Employee' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk employment (Bank Employee)', 370),
('Railway Employee', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Railway Employee' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk employment (Railway Employee)', 380),
('Farmer (Own Land)', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Farmer (Own Land)' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk agricultural occupation', 390),
('Tenant Farmer', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Tenant Farmer' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk agricultural occupation', 400),
('Agricultural Labourer', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Agricultural Labourer' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk manual/agricultural labour', 410),
('Labourer / Daily Wage', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Labourer / Daily Wage' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk manual labour', 420),
('Domestic Worker', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Domestic Worker' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk manual/domestic service', 430),
('Driver / Operator', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Driver / Operator' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk manual/transport service', 440),
('Artisan / Craftsman', 1, null, (SELECT id FROM m_code_value WHERE code_value = 'Artisan / Craftsman' AND code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation')), null, null, null, 1, 'Low risk manual/artisan profile', 450),
('Non-Individual Default Medium Risk', 2, null, null, null, null, null, 2, 'Non-Individual / Entity profile (Default Medium Risk)', 460),
('Global Default Low Risk', null, null, null, null, null, null, 1, 'Default Low Risk profile (Salary / Service / Agriculture / Others)', 500);
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,65 @@
<?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.13.xsd">
<!-- ClientKycApiResource.getRequiredDocuments() has always offered "Birth Certificate" and
"School ID Card" as identity-proof options for minor clients, but neither was ever added
as a KYCIdProofType code value (0251_add_kyc_proof_type_codes.xml). Since
edit-client/create-client's saveKycDocProof() resolves the picked document type against
this code list (resolveDocumentTypeId), selecting either option for a minor silently
failed to save any identifier/document — the KYC record's own document-name/number fields
persisted fine, but the uploaded file never did. -->
<changeSet author="CBS" id="0428-1">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(*) FROM m_code_value cv JOIN m_code c ON c.id = cv.code_id
WHERE c.code_name = 'KYCIdProofType' AND cv.code_value = 'Birth Certificate'
</sqlCheck>
</preConditions>
<sql>
INSERT INTO m_code_value (code_id, code_value, code_description, order_position, is_active, is_mandatory)
SELECT c.id, 'Birth Certificate', 'BIRTH_CERTIFICATE',
(SELECT COALESCE(MAX(cv.order_position), 0) FROM m_code_value cv WHERE cv.code_id = c.id) + 1,
true, false
FROM m_code c
WHERE c.code_name = 'KYCIdProofType';
</sql>
</changeSet>
<changeSet author="CBS" id="0428-2">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(*) FROM m_code_value cv JOIN m_code c ON c.id = cv.code_id
WHERE c.code_name = 'KYCIdProofType' AND cv.code_value = 'School ID Card'
</sqlCheck>
</preConditions>
<sql>
INSERT INTO m_code_value (code_id, code_value, code_description, order_position, is_active, is_mandatory)
SELECT c.id, 'School ID Card', 'SCHOOL_ID_CARD',
(SELECT COALESCE(MAX(cv.order_position), 0) FROM m_code_value cv WHERE cv.code_id = c.id) + 1,
true, false
FROM m_code c
WHERE c.code_name = 'KYCIdProofType';
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,77 @@
<?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.13.xsd">
<!-- Maker-checker for Client edits: the maker's PUT /clients/{id}/edit payload is staged here
(raw JSON, replayed through the existing ClientWritePlatformService.updateClient() on
approval, so all existing validation/business logic is reused unchanged) rather than
applied immediately. `pending_lock_key` is a nullable-but-unique column (NULL while not
pending) used to prevent a second maker from submitting another edit while one is already
awaiting approval for the same client — see m_share_account_pending_edit /
m_savings_product_temp for the same pattern elsewhere in this codebase. Resolved rows are
kept (status APPROVED/REJECTED) for audit history rather than deleted, which is exactly
why the lock can't be a plain UNIQUE(client_id) — a client could never be edited a second
time after its first edit was ever resolved. -->
<changeSet author="CBS" id="0429-1-create-client-pending-edit-table">
<createTable tableName="m_client_pending_edit">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="client_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_client_pending_edit_client" references="m_client(id)"/>
</column>
<column name="payload_json" type="TEXT">
<constraints nullable="false"/>
</column>
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
<constraints nullable="false"/>
</column>
<column name="maker_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_client_pending_edit_maker" references="m_appuser(id)"/>
</column>
<column name="submitted_date" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="checker_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_client_pending_edit_checker" references="m_appuser(id)"/>
</column>
<column name="checked_date" type="TIMESTAMP WITH TIME ZONE"/>
<column name="reject_reason" type="VARCHAR(500)"/>
<column name="pending_lock_key" type="VARCHAR(60)">
<constraints nullable="true" unique="true" uniqueConstraintName="uq_client_pending_edit_lock_key"/>
</column>
</createTable>
</changeSet>
<changeSet author="CBS" id="0429-2-index-client-pending-edit-client-id">
<createIndex indexName="idx_client_pending_edit_client_id" tableName="m_client_pending_edit">
<column name="client_id"/>
</createIndex>
</changeSet>
<changeSet author="CBS" id="0429-3-index-client-pending-edit-status">
<createIndex indexName="idx_client_pending_edit_status" tableName="m_client_pending_edit">
<column name="status"/>
</createIndex>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,64 @@
<?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.13.xsd">
<!-- Permissions for the Client edit maker-checker workflow (ClientEditApiResource /
m_client_pending_edit). can_maker_checker=false on all four — these rows ARE the bespoke
gate itself, so layering Fineract's native command-hold on top would be redundant.
APPROVEEDIT_CLIENT_CHECKER (action_name ending "_CHECKER") makes pending Client edits also
show up in the generic Checker Inbox (GET /v1/makercheckers) for limited checkers whose
role holds it, mirroring 2022_add_product_checker_permissions.xml elsewhere in this repo. -->
<changeSet author="CBS" id="0430-1-add-client-edit-approval-permissions">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'SUBMITEDIT_CLIENT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="SUBMITEDIT_CLIENT"/>
<column name="entity_name" value="CLIENT"/>
<column name="action_name" value="SUBMITEDIT"/>
<column name="can_maker_checker" valueBoolean="false"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="APPROVEEDIT_CLIENT"/>
<column name="entity_name" value="CLIENT"/>
<column name="action_name" value="APPROVEEDIT"/>
<column name="can_maker_checker" valueBoolean="false"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="REJECTEDIT_CLIENT"/>
<column name="entity_name" value="CLIENT"/>
<column name="action_name" value="REJECTEDIT"/>
<column name="can_maker_checker" valueBoolean="false"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="APPROVEEDIT_CLIENT_CHECKER"/>
<column name="entity_name" value="CLIENT"/>
<column name="action_name" value="APPROVEEDIT_CHECKER"/>
<column name="can_maker_checker" valueBoolean="false"/>
</insert>
</changeSet>
</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.13.xsd">
<!-- Point-in-time "before" snapshot of the client's own values, captured by the frontend from
the record it loaded when the edit page was opened (see buildOriginalValuesSnapshot() in
client-edit-diff.util.ts) and submitted alongside the edit under the _originalValues key,
which ClientEditWritePlatformServiceImpl strips out before storing/replaying the rest as
payload_json. Needed because diffing the staged payload against the LIVE client (as the
checker UI originally did) shows nothing once an edit is approved — by then the live
client already equals the proposed values. Nullable: existing pending-edit rows created
before this column existed simply have no "before" snapshot to diff against. -->
<changeSet author="CBS" id="0431-1-add-original-json-to-client-pending-edit">
<addColumn tableName="m_client_pending_edit">
<column name="original_json" type="TEXT">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,86 @@
<?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.13.xsd">
<!-- ClientKycStepComponent's constitutionDocTypes / nonIndividualDefaultDocTypes (frontend-only
maps, one per legal-entity constitution: HUF/Private Ltd/Public Ltd/LLP/Partnership/Trust/
Society/Proprietorship/Association/Govt-PSU/Cooperative) were never backed by a KYCIdProofType
code value the way individual OVDs (0251_add_kyc_proof_type_codes.xml) and minor OVDs
(0428_add_minor_kyc_id_proof_codes.xml) are. Since edit-client/create-client's
saveKycDocProof() -> resolveDocumentTypeId() resolves the picked document NAME against this
code list before creating the client_identifier, every non-individual client's KYC document
upload silently failed to save the identifier/document (no error surfaced — the KYC record's
own document-name/number fields persisted fine via the KYC entity, but the uploaded file
never did, so it vanished on refresh). This adds every distinct entity/constitution document
name used by the frontend as a KYCIdProofType code value so resolution succeeds. -->
<changeSet author="CBS" id="0432-1">
<sql>
INSERT INTO m_code_value (code_id, code_value, code_description, order_position, is_active, is_mandatory)
SELECT c.id, v.code_value, v.code_value,
(SELECT COALESCE(MAX(cv.order_position), 0) FROM m_code_value cv WHERE cv.code_id = c.id)
+ ROW_NUMBER() OVER (ORDER BY v.code_value),
true, false
FROM m_code c
CROSS JOIN (VALUES
('Certificate of Incorporation / Registration Certificate'),
('PAN Card of Entity'),
('Memorandum &amp; Articles of Association / Partnership Deed / Trust Deed'),
('Board Resolution / Authorization Letter'),
('KYC of Authorized Signatory / Power of Attorney Holder'),
('GST Registration Certificate'),
('PAN Card of HUF'),
('Declaration by Karta (with name, address &amp; HUF member details)'),
('KYC of Karta (OVDs + PAN + Photo)'),
('Proof of Address of HUF'),
('Certificate of Incorporation'),
('Memorandum &amp; Articles of Association'),
('PAN of Company'),
('Board Resolution (authorizing signatories)'),
('KYC of Authorized Signatories'),
('LLP Agreement'),
('PAN of LLP'),
('Designated Partners Resolution'),
('KYC of Designated Partners / Authorized Signatories'),
('Registration Certificate'),
('Partnership Deed'),
('PAN of Firm'),
('Trust Deed'),
('PAN of Trust'),
('KYC of Trustees / Authorized Signatories'),
('PAN of Society'),
('Proof of Business Existence (GST Certificate / Shop License)'),
('PAN of Proprietor'),
('Proprietor''s KYC (OVDs + PAN + Photo)'),
('PAN'),
('Resolution authorizing signatories'),
('Government Notification / Order'),
('PAN (if applicable)'),
('Authorized Signatory KYC')
) AS v(code_value)
WHERE c.code_name = 'KYCIdProofType'
AND NOT EXISTS (
SELECT 1 FROM m_code_value cv2 WHERE cv2.code_id = c.id AND cv2.code_value = v.code_value
);
</sql>
</changeSet>
</databaseChangeLog>