implemented a Maker-Checker workflow for Products, with approval-based updates and product locking to prevent concurrent edits

This commit is contained in:
keshav898966
2026-07-03 15:15:09 +05:30
parent 99c153830c
commit b0bfa386ce
71 changed files with 5031 additions and 26 deletions
@@ -39,4 +39,14 @@
<include file="parts/2014_add_is_mandatory_to_joint_holders.xml" relativeToChangelogFile="true" />
<include file="parts/2015_add_senior_citizen_to_savings_account.xml" relativeToChangelogFile="true" />
<include file="parts/2016_add_share_linking_enabled_to_savings_account.xml" relativeToChangelogFile="true" />
<include file="parts/2017_add_saving_product_temp_table.xml" relativeToChangelogFile="true" />
<include file="parts/2018_fix_saving_product_temp_lock_constraint.xml" relativeToChangelogFile="true" />
<include file="parts/2019_add_command_source_link_to_saving_product_temp.xml" relativeToChangelogFile="true" />
<include file="parts/2020_generalize_product_temp_table.xml" relativeToChangelogFile="true" />
<include file="parts/2021_add_fd_rd_approval_permissions.xml" relativeToChangelogFile="true" />
<include file="parts/2022_add_product_checker_permissions.xml" relativeToChangelogFile="true" />
<include file="parts/2023_fix_product_approval_permissions.xml" relativeToChangelogFile="true" />
<include file="parts/2025_recreate_savings_product_temp_table.xml" relativeToChangelogFile="true" />
<include file="parts/2026_add_pending_edit_typed_columns.xml" relativeToChangelogFile="true" />
<include file="parts/2027_add_pending_edit_satellite_tables.xml" relativeToChangelogFile="true" />
</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">
<changeSet author="fineract" id="2017-1">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_savings_product_temp" />
</not>
</preConditions>
<createTable tableName="m_savings_product_temp">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="savings_product_id" type="BIGINT">
<constraints nullable="false" unique="true" uniqueConstraintName="sp_temp_unq_savings_product_id"
foreignKeyName="fk_sp_temp_savings_product_id" references="m_savings_product(id)" />
</column>
<column name="maker_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sp_temp_maker_id" references="m_appuser(id)" />
</column>
<column name="checker_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sp_temp_checker_id" references="m_appuser(id)" />
</column>
<column name="created_on" type="DATETIME">
<constraints nullable="false" />
</column>
<column name="checked_on" 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)">
<constraints nullable="true" />
</column>
<column name="proposed_payload_json" type="CLOB">
<constraints nullable="false" />
</column>
</createTable>
</changeSet>
<changeSet author="fineract" id="2017-2">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVE_SAVINGSPRODUCT'</sqlCheck>
</not>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_SAVINGSPRODUCT" />
<column name="entity_name" value="SAVINGSPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_SAVINGSPRODUCT" />
<column name="entity_name" value="SAVINGSPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,54 @@
<?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">
<!--
The original unique constraint on savings_product_id blocked ANY future edit on a product forever, once a
single pending edit had been resolved - because resolved rows are kept (not deleted) for audit history.
The lock must only apply while status = PENDING. Unique constraints treat NULL as distinct in both
PostgreSQL and MySQL/MariaDB, so a nullable "lock key" column - set to the product id while pending, and
cleared back to NULL on approve/reject - gives a correct, cross-database concurrency-safe lock.
-->
<changeSet author="fineract" id="2018-1">
<preConditions onFail="MARK_RAN">
<columnExists tableName="m_savings_product_temp" columnName="savings_product_id" />
<not>
<columnExists tableName="m_savings_product_temp" columnName="pending_lock_key" />
</not>
</preConditions>
<dropUniqueConstraint tableName="m_savings_product_temp" constraintName="sp_temp_unq_savings_product_id" />
<addColumn tableName="m_savings_product_temp">
<column name="pending_lock_key" type="BIGINT">
<constraints nullable="true" />
</column>
</addColumn>
<sql>UPDATE m_savings_product_temp SET pending_lock_key = savings_product_id WHERE status = 'PENDING'</sql>
<addUniqueConstraint tableName="m_savings_product_temp" columnNames="pending_lock_key"
constraintName="sp_temp_unq_pending_lock_key" />
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,44 @@
<?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">
<!--
Links each pending edit to the audit-only m_portfolio_command_source row created for it, so that
submit/approve/reject also appear in System -> Audit Trails even though this feature bypasses the native
maker-checker execution pipeline.
-->
<changeSet author="fineract" id="2019-1">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="command_source_id" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="command_source_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sp_temp_command_source_id"
references="m_portfolio_command_source(id)" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,107 @@
<?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">
<!--
Generalizes m_savings_product_temp (kept as one shared table, per requirement) so it can hold pending edits
for Current Account Products (m_current_product) and Share Products (m_share_product) as well as Savings
Products (m_savings_product) - not just savings. A single BIGINT "product_id" can't carry a normal FK
anymore since it now points at three different tables depending on product_type, so the old FK is dropped
(application code, not the DB, is now responsible for referential validity here).
pending_lock_key changes from BIGINT to VARCHAR because a raw numeric id is no longer unique across product
types (e.g. a current account product id=3 and a savings product id=3 must not collide). The new format is
"<PRODUCT_TYPE>:<product_id>", e.g. "SAVINGS:3" / "CURRENT:3" / "SHARE:3".
-->
<changeSet author="fineract" id="2020-1">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="product_type" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="product_type" type="VARCHAR(20)" defaultValue="SAVINGS">
<constraints nullable="false" />
</column>
</addColumn>
<dropForeignKeyConstraint baseTableName="m_savings_product_temp" constraintName="fk_sp_temp_savings_product_id" />
<renameColumn tableName="m_savings_product_temp" oldColumnName="savings_product_id" newColumnName="product_id"
columnDataType="BIGINT" />
<dropUniqueConstraint tableName="m_savings_product_temp" constraintName="sp_temp_unq_pending_lock_key" />
<addColumn tableName="m_savings_product_temp">
<column name="pending_lock_key_new" type="VARCHAR(40)">
<constraints nullable="true" />
</column>
</addColumn>
<sql dbms="postgresql">UPDATE m_savings_product_temp SET pending_lock_key_new = 'SAVINGS:' || product_id WHERE status = 'PENDING'</sql>
<sql dbms="mysql,mariadb">UPDATE m_savings_product_temp SET pending_lock_key_new = CONCAT('SAVINGS:', product_id) WHERE status = 'PENDING'</sql>
<dropColumn tableName="m_savings_product_temp" columnName="pending_lock_key" />
<renameColumn tableName="m_savings_product_temp" oldColumnName="pending_lock_key_new" newColumnName="pending_lock_key"
columnDataType="VARCHAR(40)" />
<addUniqueConstraint tableName="m_savings_product_temp" columnNames="pending_lock_key"
constraintName="sp_temp_unq_pending_lock_key" />
</changeSet>
<changeSet author="fineract" id="2020-2">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVE_CURRENTPRODUCT'</sqlCheck>
</not>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_CURRENTPRODUCT" />
<column name="entity_name" value="CURRENTPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_CURRENTPRODUCT" />
<column name="entity_name" value="CURRENTPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_SHAREPRODUCT" />
<column name="entity_name" value="SHAREPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_SHAREPRODUCT" />
<column name="entity_name" value="SHAREPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</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">
<changeSet author="fineract" id="2021-1">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVE_FIXEDDEPOSITPRODUCT'</sqlCheck>
</not>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_FIXEDDEPOSITPRODUCT" />
<column name="entity_name" value="FIXEDDEPOSITPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_FIXEDDEPOSITPRODUCT" />
<column name="entity_name" value="FIXEDDEPOSITPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_RECURRINGDEPOSITPRODUCT" />
<column name="entity_name" value="RECURRINGDEPOSITPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_RECURRINGDEPOSITPRODUCT" />
<column name="entity_name" value="RECURRINGDEPOSITPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,103 @@
<?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">
<!--
AuditReadPlatformServiceImpl.retrieveAllEntriesToBeChecked() (backing GET /v1/makercheckers, the "Checker
Inbox" tab) shows a pending m_portfolio_command_source row to a "limited" checker only if their role holds
a permission whose action_name is "<ACTION>_CHECKER" and entity_name matches the row's entity_name (code
convention: "<ACTION>_<ENTITY>_CHECKER", matching how core Fineract permissions like UPDATE_ROLE_CHECKER
are seeded). Our custom pending-approval CommandSource rows for product edits (SAVINGSPRODUCT,
CURRENTPRODUCT, SHAREPRODUCT, FIXEDDEPOSITPRODUCT, RECURRINGDEPOSITPRODUCT) already satisfy the base
status=AWAITING_APPROVAL filter, but without these permission rows a limited checker's role has nothing to
match against, so the rows never show up in Checker Inbox - only in our own Pending Product Edits screen.
UPDATE_SAVINGSPRODUCT_CHECKER already exists as a native Fineract permission, so each row below is its own
changeset with its own precondition - one already-existing code must not block inserting the others.
-->
<changeSet author="fineract" id="2022-1">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'UPDATE_SAVINGSPRODUCT_CHECKER'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="UPDATE_SAVINGSPRODUCT_CHECKER" />
<column name="entity_name" value="SAVINGSPRODUCT" />
<column name="action_name" value="UPDATE_CHECKER" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2022-2">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'UPDATE_CURRENTPRODUCT_CHECKER'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="UPDATE_CURRENTPRODUCT_CHECKER" />
<column name="entity_name" value="CURRENTPRODUCT" />
<column name="action_name" value="UPDATE_CHECKER" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2022-3">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'UPDATE_SHAREPRODUCT_CHECKER'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="UPDATE_SHAREPRODUCT_CHECKER" />
<column name="entity_name" value="SHAREPRODUCT" />
<column name="action_name" value="UPDATE_CHECKER" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2022-4">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'UPDATE_FIXEDDEPOSITPRODUCT_CHECKER'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="UPDATE_FIXEDDEPOSITPRODUCT_CHECKER" />
<column name="entity_name" value="FIXEDDEPOSITPRODUCT" />
<column name="action_name" value="UPDATE_CHECKER" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2022-5">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'UPDATE_RECURRINGDEPOSITPRODUCT_CHECKER'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="UPDATE_RECURRINGDEPOSITPRODUCT_CHECKER" />
<column name="entity_name" value="RECURRINGDEPOSITPRODUCT" />
<column name="action_name" value="UPDATE_CHECKER" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,165 @@
<?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">
<!--
Changesets 2017-2, 2020-2 and 2021-1 used an inverted precondition - `<not><sqlCheck expectedResult="0">...`
actually means "run only if the row ALREADY exists", the opposite of the intended idempotent-insert guard.
On a fresh database (count=0) that precondition evaluates to false, so onFail="MARK_RAN" silently skipped
the insert entirely - the APPROVE_*/REJECT_* permissions for all five product types were marked as applied
in databasechangelog but never actually inserted. Those changesets are already recorded and must not be
edited (Liquibase would fail on checksum mismatch), so this migration re-inserts the missing rows under
fresh changeset ids, each with a correct (non-inverted) precondition and split per row so one already-
existing code can never block the others.
-->
<changeSet author="fineract" id="2023-1">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVE_SAVINGSPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_SAVINGSPRODUCT" />
<column name="entity_name" value="SAVINGSPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-2">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECT_SAVINGSPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_SAVINGSPRODUCT" />
<column name="entity_name" value="SAVINGSPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-3">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVE_CURRENTPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_CURRENTPRODUCT" />
<column name="entity_name" value="CURRENTPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-4">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECT_CURRENTPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_CURRENTPRODUCT" />
<column name="entity_name" value="CURRENTPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-5">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVE_SHAREPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_SHAREPRODUCT" />
<column name="entity_name" value="SHAREPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-6">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECT_SHAREPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_SHAREPRODUCT" />
<column name="entity_name" value="SHAREPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-7">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVE_FIXEDDEPOSITPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_FIXEDDEPOSITPRODUCT" />
<column name="entity_name" value="FIXEDDEPOSITPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-8">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECT_FIXEDDEPOSITPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_FIXEDDEPOSITPRODUCT" />
<column name="entity_name" value="FIXEDDEPOSITPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-9">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVE_RECURRINGDEPOSITPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="APPROVE_RECURRINGDEPOSITPRODUCT" />
<column name="entity_name" value="RECURRINGDEPOSITPRODUCT" />
<column name="action_name" value="APPROVE" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
<changeSet author="fineract" id="2023-10">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECT_RECURRINGDEPOSITPRODUCT'</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="organisation" />
<column name="code" value="REJECT_RECURRINGDEPOSITPRODUCT" />
<column name="entity_name" value="RECURRINGDEPOSITPRODUCT" />
<column name="action_name" value="REJECT" />
<column name="can_maker_checker" valueBoolean="false" />
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,78 @@
<?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">
<!--
m_savings_product_temp was dropped by an experimental changeset that was later removed from source without
restoring the table. Recreates it with the final shape from the 2017/2018/2019/2020 migrations (guarded so
it's a no-op wherever the table already exists) so ProductPendingEditRepository and the isPendingApproval
subqueries in the product read services stop failing with "relation does not exist".
-->
<changeSet author="fineract" id="2025-1">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_savings_product_temp" />
</not>
</preConditions>
<createTable tableName="m_savings_product_temp">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="product_id" type="BIGINT">
<constraints nullable="false" />
</column>
<column name="product_type" type="VARCHAR(20)" defaultValue="SAVINGS">
<constraints nullable="false" />
</column>
<column name="maker_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_sp_temp_maker_id" references="m_appuser(id)" />
</column>
<column name="checker_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sp_temp_checker_id" references="m_appuser(id)" />
</column>
<column name="created_on" type="DATETIME">
<constraints nullable="false" />
</column>
<column name="checked_on" 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)">
<constraints nullable="true" />
</column>
<column name="proposed_payload_json" type="CLOB">
<constraints nullable="false" />
</column>
<column name="pending_lock_key" type="VARCHAR(40)">
<constraints nullable="true" unique="true" uniqueConstraintName="sp_temp_unq_pending_lock_key" />
</column>
<column name="command_source_id" type="BIGINT">
<constraints nullable="true" foreignKeyName="fk_sp_temp_command_source_id"
references="m_portfolio_command_source(id)" />
</column>
</createTable>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,261 @@
<?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">
<!--
Adds typed columns to m_savings_product_temp so a pending product edit is stored as real,
individually-inspectable columns instead of only the opaque proposed_payload_json blob.
proposed_payload_json is kept (still written) for a safe deprecation window; nothing here
removes or alters any existing column. Every changeset is purely additive (addColumn only)
and guarded with a correctly-shaped precondition (<not><columnExists .../></not> - a
boolean precondition, unlike the historical bug in 2017-2/2020-2 which wrapped a numeric
<sqlCheck expectedResult="0"> in <not>, inverting its meaning).
Columns are grouped by which product type(s) apply them; unrelated columns are simply NULL
for a given row's product_type. Names are the snake_case of the JSON API parameter name the
column stores, not the source product table's own column name, since this table stores
proposed *edits* (parameter values), not a mirror of product-table storage.
-->
<!-- Group 1: fields shared across Savings, Current, FD and RD (FD/RD extend Savings) -->
<changeSet author="fineract" id="2026-1">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="name" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="name" type="VARCHAR(100)" />
<column name="short_name" type="VARCHAR(20)" />
<column name="description" type="VARCHAR(500)" />
<column name="currency_code" type="VARCHAR(3)" />
<column name="digits_after_decimal" type="INTEGER" />
<column name="in_multiples_of" type="INTEGER" />
<column name="accounting_rule" type="INTEGER" />
<column name="nominal_annual_interest_rate" type="DECIMAL(19,6)" />
<column name="interest_compounding_period_type" type="INTEGER" />
<column name="interest_posting_period_type" type="INTEGER" />
<column name="interest_calculation_type" type="INTEGER" />
<column name="interest_calculation_days_in_year_type" type="INTEGER" />
<column name="min_required_opening_balance" type="DECIMAL(19,6)" />
<column name="lockin_period_frequency" type="INTEGER" />
<column name="lockin_period_frequency_type" type="INTEGER" />
<column name="withdrawal_fee_for_transfers" type="BOOLEAN" />
<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="enforce_min_required_balance" type="BOOLEAN" />
<column name="min_required_balance" type="DECIMAL(19,6)" />
<column name="lien_allowed" type="BOOLEAN" />
<column name="max_allowed_lien_limit" type="DECIMAL(19,6)" />
<column name="min_balance_for_interest_calculation" type="DECIMAL(19,6)" />
<column name="with_hold_tax" type="BOOLEAN" />
<column name="tax_group_id" type="BIGINT" />
<column name="other_gl_mappings" type="CLOB" />
<column name="is_dormancy_tracking_active" type="BOOLEAN" />
<column name="days_to_inactive" type="INTEGER" />
<column name="days_to_dormancy" type="INTEGER" />
<column name="days_to_escheat" type="INTEGER" />
<column name="account_type" type="VARCHAR(50)" />
<column name="customer_segment" type="VARCHAR(50)" />
<column name="product_code" type="VARCHAR(20)" />
<column name="gl_account_id" type="BIGINT" />
<column name="effective_date" type="DATE" />
<column name="effective_to" type="DATE" />
<column name="end_date" type="DATE" />
<column name="introducer_required" type="BOOLEAN" />
<column name="share_linking_enabled" type="BOOLEAN" />
<column name="nomination_facility" type="BOOLEAN" />
<column name="dormancy_alert" type="BOOLEAN" />
<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="last_interest_date" type="DATE" />
<column name="enable_lockin_period" type="BOOLEAN" />
<column name="balance_frequency" type="VARCHAR(50)" />
<column name="account_closed_zero_balance_in_day_closing" type="BOOLEAN" />
<column name="balance_swap_option" type="BOOLEAN" />
<column name="msme" type="BOOLEAN" />
<column name="auto_debit_facility" type="BOOLEAN" />
<column name="loan_facility" type="BOOLEAN" />
<column name="premature_withdrawal_option" type="BOOLEAN" />
<column name="tenure_flexibility" type="BOOLEAN" />
<column name="lien_marked" type="BOOLEAN" />
<column name="maturity_instructions" type="BOOLEAN" />
<column name="credit_maturity_proceeds" type="BOOLEAN" />
<column name="penalty_grace_period_missed_installments" type="BOOLEAN" />
<column name="panel_request_applicable" type="BOOLEAN" />
<column name="no_penalty_nominee_heir_closure" type="BOOLEAN" />
<column name="deceased_settlement" type="BOOLEAN" />
<column name="allow_debit_txn_after_due_date" type="BOOLEAN" />
</addColumn>
</changeSet>
<!-- Group 2: Current-product-only fields -->
<changeSet author="fineract" id="2026-2">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="avg_balance_calc_period" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="avg_balance_calc_period" type="VARCHAR(10)" />
<column name="valid_for_period" type="VARCHAR(10)" />
<column name="od_account_closed_zero_balance_in_day_closing" type="BOOLEAN" />
</addColumn>
</changeSet>
<!-- Group 3: Share-product-only fields -->
<changeSet author="fineract" id="2026-3">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="total_shares" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="total_shares" type="BIGINT" />
<column name="total_shares_issued" type="BIGINT" />
<column name="unit_price" type="DECIMAL(19,6)" />
<column name="minimum_shares" type="BIGINT" />
<column name="nominal_shares" type="BIGINT" />
<column name="maximum_shares" type="BIGINT" />
<column name="allow_dividend_calc_for_inactive_clients" type="BOOLEAN" />
<column name="minimum_active_period_for_dividends" type="INTEGER" />
<column name="minimum_active_period_frequency_type" type="INTEGER" />
<column name="share_lockin_period" type="INTEGER" />
<column name="share_lockin_period_frequency_type" type="INTEGER" />
</addColumn>
</changeSet>
<!-- Group 4: Fixed-Deposit-only fields (also used by Recurring Deposit, which extends FD) -->
<changeSet author="fineract" id="2026-4">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="fd_type" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="fd_type" type="VARCHAR(20)" />
<column name="pre_closure_penal_applicable" type="BOOLEAN" />
<column name="pre_closure_penal_interest" type="DECIMAL(19,6)" />
<column name="pre_closure_penal_interest_on_type_id" type="INTEGER" />
<column name="min_deposit_term" type="INTEGER" />
<column name="max_deposit_term" type="INTEGER" />
<column name="min_deposit_term_type_id" type="INTEGER" />
<column name="max_deposit_term_type_id" type="INTEGER" />
<column name="in_multiples_of_deposit_term" type="INTEGER" />
<column name="in_multiples_of_deposit_term_type_id" type="INTEGER" />
<column name="min_deposit_amount" type="DECIMAL(19,6)" />
<column name="max_deposit_amount" type="DECIMAL(19,6)" />
<column name="deposit_amount" type="DECIMAL(19,6)" />
</addColumn>
</changeSet>
<!-- Group 5: Recurring-Deposit-only fields -->
<changeSet author="fineract" id="2026-5">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="is_mandatory_deposit" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="is_mandatory_deposit" type="BOOLEAN" />
<column name="allow_withdrawal" type="BOOLEAN" />
<column name="adjust_advance_towards_future_payments" type="BOOLEAN" />
</addColumn>
</changeSet>
<!-- Group 6: accountFacilityConfig (1:1 today - kept as flat columns, fc_ prefix) -->
<changeSet author="fineract" id="2026-6">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="fc_atm_facility" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="fc_atm_facility" type="BOOLEAN" />
<column name="fc_atm_withdrawal_facility" type="BOOLEAN" />
<column name="fc_upi_facility" type="BOOLEAN" />
<column name="fc_pos_transactions_facility" type="BOOLEAN" />
<column name="fc_cheque_book" type="BOOLEAN" />
<column name="fc_imps_facility" type="BOOLEAN" />
<column name="fc_sms_alerts" type="BOOLEAN" />
<column name="fc_bbps_facility" type="BOOLEAN" />
<column name="fc_insurance_cover" type="BOOLEAN" />
<column name="fc_bepg_facility" type="BOOLEAN" />
<column name="fc_neft_facility" type="BOOLEAN" />
<column name="fc_rtgs_facility" type="BOOLEAN" />
<column name="fc_inter_bank_total_cap_facility" type="BOOLEAN" />
<column name="fc_withdrawal_facility" type="BOOLEAN" />
<column name="fc_deposit_facility_kyc" type="BOOLEAN" />
<column name="fc_virtual_debit_card" type="BOOLEAN" />
</addColumn>
</changeSet>
<!-- Group 7: depositLimitKyc (1:1 today - kept as flat columns, kyc_ prefix) -->
<changeSet author="fineract" id="2026-7">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="kyc_general_type" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="kyc_general_type" type="VARCHAR(50)" />
<column name="kyc_general_limit" type="VARCHAR(50)" />
<column name="kyc_minor_type" type="VARCHAR(50)" />
<column name="kyc_minor_limit" type="BIGINT" />
<column name="kyc_small_saving_type" type="VARCHAR(50)" />
<column name="kyc_small_saving_limit" type="BIGINT" />
</addColumn>
</changeSet>
<!-- Group 8: GL account mapping scalar ids (1:1 today - kept as flat columns, gl_ prefix). No FK
constraints - the existing validator already checks GL account validity at submit time. -->
<changeSet author="fineract" id="2026-8">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_savings_product_temp" columnName="gl_savings_reference_account_id" />
</not>
</preConditions>
<addColumn tableName="m_savings_product_temp">
<column name="gl_savings_reference_account_id" type="BIGINT" />
<column name="gl_savings_control_account_id" type="BIGINT" />
<column name="gl_income_from_fee_account_id" type="BIGINT" />
<column name="gl_income_from_penalty_account_id" type="BIGINT" />
<column name="gl_interest_on_savings_account_id" type="BIGINT" />
<column name="gl_transfers_in_suspense_account_id" type="BIGINT" />
<column name="gl_overdraft_portfolio_control_id" type="BIGINT" />
<column name="gl_income_from_interest_id" type="BIGINT" />
<column name="gl_write_off_account_id" type="BIGINT" />
<column name="gl_escheat_liability_id" type="BIGINT" />
<column name="gl_penalties_receivable_account_id" type="BIGINT" />
<column name="gl_interest_receivable_account_id" type="BIGINT" />
<column name="gl_fees_receivable_account_id" type="BIGINT" />
<column name="gl_interest_payable_account_id" type="BIGINT" />
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,150 @@
<?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">
<!--
Satellite tables for the genuine one-to-many pieces of a pending product edit (charges,
facility limits, GL array mappings, share market prices) plus a "submitted field" manifest
that records which top-level JSON parameters were actually present in the maker's request.
The manifest table is not optional bookkeeping: raw JSON storage implicitly distinguishes
"field omitted" / "field explicitly null" / "field has a value", and every existing
command.hasParameter(...) branch in the write-services and assemblers depends on that
distinction. Typed nullable columns alone can't represent it, so this table is the
relational replacement for that information - one row per parameter name that was present
at submit time, per pending edit.
All tables reference m_savings_product_temp(id) - no cascade delete, since pending-edit
rows are never hard-deleted (only status-flipped) once resolved.
-->
<changeSet author="fineract" id="2027-1">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_product_temp_charge" />
</not>
</preConditions>
<createTable tableName="m_product_temp_charge">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="pending_edit_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_ptc_pending_edit_id" references="m_savings_product_temp(id)" />
</column>
<column name="charge_id" type="BIGINT">
<constraints nullable="false" />
</column>
</createTable>
</changeSet>
<changeSet author="fineract" id="2027-2">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_product_temp_facility_limit" />
</not>
</preConditions>
<createTable tableName="m_product_temp_facility_limit">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="pending_edit_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_ptfl_pending_edit_id" references="m_savings_product_temp(id)" />
</column>
<column name="limit_type" type="VARCHAR(100)">
<constraints nullable="false" />
</column>
<column name="general_limit" type="VARCHAR(50)" />
<column name="minor_account_limit" type="VARCHAR(50)" />
<column name="small_saving_limit" type="VARCHAR(50)" />
</createTable>
</changeSet>
<changeSet author="fineract" id="2027-3">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_product_temp_gl_mapping" />
</not>
</preConditions>
<createTable tableName="m_product_temp_gl_mapping">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="pending_edit_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_ptgm_pending_edit_id" references="m_savings_product_temp(id)" />
</column>
<!-- PAYMENT_CHANNEL / FEE / PENALTY -->
<column name="mapping_type" type="VARCHAR(30)">
<constraints nullable="false" />
</column>
<column name="payment_type_id" type="BIGINT" />
<column name="charge_id" type="BIGINT" />
<column name="gl_account_id" type="BIGINT">
<constraints nullable="false" />
</column>
</createTable>
</changeSet>
<changeSet author="fineract" id="2027-4">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_product_temp_share_market_price" />
</not>
</preConditions>
<createTable tableName="m_product_temp_share_market_price">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="pending_edit_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_ptsmp_pending_edit_id" references="m_savings_product_temp(id)" />
</column>
<column name="source_id" type="BIGINT" />
<column name="delete_flag" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
<column name="from_date" type="DATE" />
<column name="share_value" type="DECIMAL(19,6)" />
</createTable>
</changeSet>
<changeSet author="fineract" id="2027-5">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_product_temp_submitted_field" />
</not>
</preConditions>
<createTable tableName="m_product_temp_submitted_field">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="pending_edit_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_ptsf_pending_edit_id" references="m_savings_product_temp(id)" />
</column>
<column name="field_name" type="VARCHAR(100)">
<constraints nullable="false" />
</column>
</createTable>
<addUniqueConstraint tableName="m_product_temp_submitted_field" columnNames="pending_edit_id, field_name"
constraintName="ptsf_unq_pending_edit_field" />
</changeSet>
</databaseChangeLog>