Files
cbsnexora-backend/sql/kyc/create_kyc_tables_postgresql.sql
AdityaandClaude Opus 4.6 ba5365d4af Add all CBS custom modules: KYC, Sanctions, RBI Feed, OpenProject, Mobile Lookup, Charges, Avro schemas
Includes all prior work not yet committed on this branch:
- KYC module (LincPay eKYC: PAN, Aadhaar OTP, GSTIN verification)
- Sanctions screening (UN Security Council consolidated list)
- RBI Feed integration with scheduled jobs
- OpenProject API integration
- Mobile lookup service
- Charge module enhancements (current account support)
- Client family member nominee fields
- Avro schema updates
- Liquibase migrations 0216-0224
- Postman collections for OpenProject, RBI Feeds, Sanctions
- SQL seed scripts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:54:10 +05:30

156 lines
6.9 KiB
SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =============================================================================
-- KYC Module – Table Creation Script (PostgreSQL)
-- =============================================================================
-- DB Connection (from config/docker/env/postgresql.env & application.properties):
-- Host : localhost
-- Port : 5432
-- Database : fineract_default ← tenant database (your actual data lives here)
-- Username : postgres
-- Password : skdcnwauicn2ucnaecasdsajdnizucawencascdca
--
-- Run command:
-- psql -h localhost -p 5432 -U postgres -d fineract_default -f create_kyc_tables_postgresql.sql
--
-- (It will prompt for password: skdcnwauicn2ucnaecasdsajdnizucawencascdca)
-- =============================================================================
\connect fineract_default;
-- =============================================================================
-- TABLE: m_client_kyc
-- One KYC record per client (enforced by UNIQUE on client_id).
-- =============================================================================
CREATE TABLE IF NOT EXISTS m_client_kyc (
id BIGSERIAL NOT NULL,
client_id BIGINT NOT NULL,
-- kyc_status : 100=PENDING | 200=IN_PROGRESS | 300=VERIFIED | 400=REJECTED | 500=EXPIRED
kyc_status SMALLINT NOT NULL DEFAULT 100,
-- kyc_type : 1=INDIVIDUAL | 2=NON_INDIVIDUAL
kyc_type SMALLINT NOT NULL,
-- risk_category : 1=LOW (re-KYC 10yr) | 2=MEDIUM (8yr) | 3=HIGH (2yr)
risk_category SMALLINT NOT NULL DEFAULT 1,
-- PAN (Permanent Account Number – Income Tax Dept)
-- Format: 5 uppercase letters + 4 digits + 1 uppercase letter e.g. ABCDE1234F
pan_number VARCHAR(10),
pan_verified BOOLEAN NOT NULL DEFAULT FALSE,
pan_verified_on DATE,
-- Aadhaar – ONLY masked form stored (last 4 digits visible) per UIDAI directive
aadhaar_number_masked VARCHAR(12),
aadhaar_verified BOOLEAN NOT NULL DEFAULT FALSE,
aadhaar_verified_on DATE,
-- GSTIN (15-char GST Identification Number – mandatory for NON_INDIVIDUAL)
gstin_number VARCHAR(15),
gstin_verified BOOLEAN NOT NULL DEFAULT FALSE,
gstin_verified_on DATE,
-- CKYCRR – Central KYC Records Registry number (issued by CERSAI, 14 digits)
ckyc_number VARCHAR(14),
-- Lifecycle timestamps
initiated_on DATE,
verified_on DATE,
rejected_on DATE,
expiry_date DATE,
remarks VARCHAR(500),
-- Audit fields (AbstractAuditableWithUTCDateTimeCustom)
created_by BIGINT NOT NULL,
created_on_utc TIMESTAMP WITH TIME ZONE NOT NULL,
last_modified_by BIGINT NOT NULL,
last_modified_on_utc TIMESTAMP WITH TIME ZONE NOT NULL,
version BIGINT NOT NULL DEFAULT 0,
CONSTRAINT pk_m_client_kyc PRIMARY KEY (id),
CONSTRAINT uq_m_client_kyc_client_id UNIQUE (client_id),
CONSTRAINT fk_m_client_kyc_client_id
FOREIGN KEY (client_id) REFERENCES m_client(id)
ON DELETE RESTRICT ON UPDATE RESTRICT
);
COMMENT ON TABLE m_client_kyc IS 'RBI-mandated KYC record – one per client';
COMMENT ON COLUMN m_client_kyc.kyc_status IS '100=PENDING,200=IN_PROGRESS,300=VERIFIED,400=REJECTED,500=EXPIRED';
COMMENT ON COLUMN m_client_kyc.kyc_type IS '1=INDIVIDUAL, 2=NON_INDIVIDUAL';
COMMENT ON COLUMN m_client_kyc.risk_category IS '1=LOW(10yr), 2=MEDIUM(8yr), 3=HIGH(2yr) re-KYC period';
COMMENT ON COLUMN m_client_kyc.aadhaar_number_masked IS 'Masked Aadhaar: first 8 digits X, last 4 visible. Full number must NOT be stored per UIDAI directive.';
-- =============================================================================
-- TABLE: m_client_kyc_verification_log
-- Immutable audit trail for every 3rd-party API call.
-- =============================================================================
CREATE TABLE IF NOT EXISTS m_client_kyc_verification_log (
id BIGSERIAL NOT NULL,
kyc_id BIGINT NOT NULL,
-- verification_type: PAN | AADHAAR | GSTIN
verification_type VARCHAR(20) NOT NULL,
-- verification_status: SUCCESS | FAILURE | PENDING | ERROR
verification_status VARCHAR(20) NOT NULL,
request_reference VARCHAR(100),
api_response TEXT,
remarks VARCHAR(500),
-- Audit fields
created_by BIGINT NOT NULL,
created_on_utc TIMESTAMP WITH TIME ZONE NOT NULL,
last_modified_by BIGINT NOT NULL,
last_modified_on_utc TIMESTAMP WITH TIME ZONE NOT NULL,
version BIGINT NOT NULL DEFAULT 0,
CONSTRAINT pk_m_client_kyc_verification_log PRIMARY KEY (id),
CONSTRAINT fk_m_kyc_verification_log_kyc_id
FOREIGN KEY (kyc_id) REFERENCES m_client_kyc(id)
ON DELETE CASCADE ON UPDATE RESTRICT
);
COMMENT ON TABLE m_client_kyc_verification_log IS 'Audit log of all 3rd-party KYC API calls (PAN/Aadhaar/GSTIN)';
-- =============================================================================
-- PERMISSIONS
-- =============================================================================
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
SELECT 'portfolio', 'READ_CLIENTKYC', 'CLIENTKYC', 'READ', FALSE
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'READ_CLIENTKYC');
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
SELECT 'portfolio', 'CREATE_CLIENTKYC', 'CLIENTKYC', 'CREATE', TRUE
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_CLIENTKYC');
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
SELECT 'portfolio', 'UPDATE_CLIENTKYC', 'CLIENTKYC', 'UPDATE', TRUE
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'UPDATE_CLIENTKYC');
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
SELECT 'portfolio', 'VERIFY_CLIENTKYC', 'CLIENTKYC', 'VERIFY', TRUE
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'VERIFY_CLIENTKYC');
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
SELECT 'portfolio', 'REJECT_CLIENTKYC', 'CLIENTKYC', 'REJECT', TRUE
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'REJECT_CLIENTKYC');
-- =============================================================================
-- VERIFY CREATION
-- =============================================================================
SELECT tablename AS "Tables created"
FROM pg_tables
WHERE schemaname = 'public'
AND tablename LIKE 'm_client_kyc%';
SELECT code, entity_name, action_name
FROM m_permission
WHERE entity_name = 'CLIENTKYC';