# Workflow Settings

&#x20; Workflow                                                                                                                                                         &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; When an admin changes KYC document requirements:                                                                                                                 &#x20;

&#x20; 1\. Config controller detects requiredDocuments changed                                                                                                           &#x20;

&#x20; 2\. Dispatches KycComplianceReEvalJob to Bull queue                                                                                                               &#x20;

&#x20; 3\. Job evaluates all tenant users with verified/pending KYC                                                                                                      &#x20;

&#x20; 4\. For each user, compares their valid documents against new requirements                                                                                        &#x20;

&#x20; 5\. Users missing required docs get kycPendingDocuments populated                                                                                                 &#x20;

&#x20; 6\. Verified users with missing docs are downgraded to 'pending'                                                                                                  &#x20;

&#x20; 7\. Affected users receive in-app notification + email                                                                                                            &#x20;

&#x20; 8\. Audit log records the batch re-evaluation                                                                                                                     &#x20;

&#x20;User Model (80+ columns)                                                                                                                                          &#x20;

&#x20;├── fullName          ← Used for BOTH personal name AND company name (the problem!)                                                                               &#x20;

&#x20;├── type              ← super\_admin | company | user | client                                                                                                     &#x20;

&#x20;├── createdBy         ← Multi-tenancy: 0 for tenants, tenant.id for employees                                                                                     &#x20;

&#x20;└── \[KYC, auth, SSO, DID fields...] &#x20;

&#x20;TenantSettings Model (70+ columns)                                                                                                                                &#x20;

&#x20;├── tenantId (FK → User.id)                                                                                                                                       &#x20;

&#x20;├── taxId, taxIdType, taxJurisdiction  ← Business tax info exists                                                                                                 &#x20;

&#x20;└── \[Feature toggles, prefixes, etc.]                                                                                                                             &#x20;

&#x20;└── ❌ NO company identity fields (companyName, legalName, logo)  &#x20;

Database Structure Analysis & Recommendations                                                                                                                    &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; Current State Summary                                                                                                                                            &#x20;

&#x20; ┌─────────────────┬─────────┬───────────────────────────────────────────────┐                                                                                    &#x20;

&#x20; │      Table      │ Columns │                    Status                     │                                                                                    &#x20;

&#x20; ├─────────────────┼─────────┼───────────────────────────────────────────────┤                                                                                    &#x20;

&#x20; │ tenant\_settings │ 85      │ High - needs refactoring                      │                                                                                    &#x20;

&#x20; ├─────────────────┼─────────┼───────────────────────────────────────────────┤                                                                                    &#x20;

&#x20; │ users           │ 81      │ Medium - well-organized with vault protection │                                                                                    &#x20;

&#x20; ├─────────────────┼─────────┼───────────────────────────────────────────────┤                                                                                    &#x20;

&#x20; │ kyc\_submissions │ 65      │ OK - complex but justified by workflow        │                                                                                    &#x20;

&#x20; ├─────────────────┼─────────┼───────────────────────────────────────────────┤                                                                                    &#x20;

&#x20; │ invoices/bills  │ 35-40   │ OK - document tables are inherently wide      │                                                                                    &#x20;

&#x20; └─────────────────┴─────────┴───────────────────────────────────────────────┘                                                                                    &#x20;

&#x20; \---                                                                                                                                                              &#x20;

&#x20; Recommendations                                                                                                                                                  &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; 1\. TenantSettings Consolidation (Priority: HIGH)                                                                                                                 &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; This table has grown organically with each new module. I recommend consolidating related fields into JSON objects:                                               &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; Current: 85 columns                                                                                                                                              &#x20;

&#x20; Target: \~55 columns                                                                                                                                              &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; // BEFORE: 15 individual boolean columns                                                                                                                         &#x20;

&#x20; enableKyc, enableCrypto, enableInventory, enableRepairShop,                                                                                                      &#x20;

&#x20; enableFundAccounting, enableEncumbrance, enableMultiCurrency, ...                                                                                                &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; // AFTER: 1 JSON column                                                                                                                                          &#x20;

&#x20; @column()                                                                                                                                                        &#x20;

&#x20; declare enabledModules: {                                                                                                                                        &#x20;

&#x20;   kyc: boolean                                                                                                                                                   &#x20;

&#x20;   crypto: boolean                                                                                                                                                &#x20;

&#x20;   inventory: boolean                                                                                                                                             &#x20;

&#x20;   repairShop: boolean                                                                                                                                            &#x20;

&#x20;   fundAccounting: boolean                                                                                                                                        &#x20;

&#x20;   encumbrance: boolean                                                                                                                                           &#x20;

&#x20;   multiCurrency: boolean                                                                                                                                         &#x20;

&#x20;   costCenters: boolean                                                                                                                                           &#x20;

&#x20;   recurring: boolean                                                                                                                                             &#x20;

&#x20;   budgeting: boolean                                                                                                                                             &#x20;

&#x20;   attachments: boolean                                                                                                                                           &#x20;

&#x20;   projects: boolean                                                                                                                                              &#x20;

&#x20;   electronicInvoicing: boolean                                                                                                                                   &#x20;

&#x20; }                                                                                                                                                                &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; Other consolidation opportunities:                                                                                                                               &#x20;

&#x20; ┌───────────────────┬────────────────────┬───────────────────────┐                                                                                               &#x20;

&#x20; │       Group       │  Current Columns   │ Proposed JSON Column  │                                                                                               &#x20;

&#x20; ├───────────────────┼────────────────────┼───────────────────────┤                                                                                               &#x20;

&#x20; │ Module toggles    │ 15 booleans        │ enabledModules: {}    │                                                                                               &#x20;

&#x20; ├───────────────────┼────────────────────┼───────────────────────┤                                                                                               &#x20;

&#x20; │ Document prefixes │ 7 strings          │ documentPrefixes: {}  │                                                                                               &#x20;

&#x20; ├───────────────────┼────────────────────┼───────────────────────┤                                                                                               &#x20;

&#x20; │ Default accounts  │ 5 FKs              │ defaultAccountIds: {} │                                                                                               &#x20;

&#x20; ├───────────────────┼────────────────────┼───────────────────────┤                                                                                               &#x20;

&#x20; │ Approval settings │ 6 booleans + 1 int │ approvalSettings: {}  │                                                                                               &#x20;

&#x20; ├───────────────────┼────────────────────┼───────────────────────┤                                                                                               &#x20;

&#x20; │ Repair shop       │ 5 mixed            │ repairShopConfig: {}  │                                                                                               &#x20;

&#x20; ├───────────────────┼────────────────────┼───────────────────────┤                                                                                               &#x20;

&#x20; │ Crypto            │ 3 mixed            │ cryptoConfig: {}      │                                                                                               &#x20;

&#x20; └───────────────────┴────────────────────┴───────────────────────┘                                                                                               &#x20;

&#x20; Trade-offs:                                                                                                                                                      &#x20;

&#x20; ┌──────────────────┬─────────────────────────────────────┬──────────────────────────────────────────────────┐                                                    &#x20;

&#x20; │     Approach     │                Pros                 │                       Cons                       │                                                    &#x20;

&#x20; ├──────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────┤                                                    &#x20;

&#x20; │ JSON columns     │ Fewer columns, easier to add fields │ Can't index individual fields, no FK constraints │                                                    &#x20;

&#x20; ├──────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────┤                                                    &#x20;

&#x20; │ Separate columns │ Indexable, type-safe, FK support    │ More migrations, wider tables                    │                                                    &#x20;

&#x20; └──────────────────┴─────────────────────────────────────┴──────────────────────────────────────────────────┘                                                    &#x20;

&#x20; My recommendation: Use JSON for configuration objects (module toggles, prefixes, repair shop settings) but keep FKs as separate columns (defaultAccountIds should&#x20;

&#x20; stay as individual columns for referential integrity).                                                                                                           &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; \---                                                                                                                                                              &#x20;

&#x20; 2\. Users Table (Priority: MEDIUM)                                                                                                                                &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; The 81 columns are actually well-organized into logical domains:                                                                                                 &#x20;

&#x20; ┌─────────────────────┬─────────┬────────────────────────────────────────┐                                                                                       &#x20;

&#x20; │       Domain        │ Columns │             Recommendation             │                                                                                       &#x20;

&#x20; ├─────────────────────┼─────────┼────────────────────────────────────────┤                                                                                       &#x20;

&#x20; │ Core identity       │ 7       │ Keep                                   │                                                                                       &#x20;

&#x20; ├─────────────────────┼─────────┼────────────────────────────────────────┤                                                                                       &#x20;

&#x20; │ Auth (passkey/MFA)  │ 10      │ Keep - frequently accessed             │                                                                                       &#x20;

&#x20; ├─────────────────────┼─────────┼────────────────────────────────────────┤                                                                                       &#x20;

&#x20; │ SSO                 │ 8       │ Consider: Move to user\_sso\_profiles    │                                                                                       &#x20;

&#x20; ├─────────────────────┼─────────┼────────────────────────────────────────┤                                                                                       &#x20;

&#x20; │ DID/Domain          │ 12      │ Consider: Move to user\_domains         │                                                                                       &#x20;

&#x20; ├─────────────────────┼─────────┼────────────────────────────────────────┤                                                                                       &#x20;

&#x20; │ KYC status          │ 7       │ Keep - frequently joined               │                                                                                       &#x20;

&#x20; ├─────────────────────┼─────────┼────────────────────────────────────────┤                                                                                       &#x20;

&#x20; │ Vault-protected PII │ 10      │ Keep - encryption requires co-location │                                                                                       &#x20;

&#x20; └─────────────────────┴─────────┴────────────────────────────────────────┘                                                                                       &#x20;

&#x20; Recommendation: Extract SSO and DID/Domain fields to separate tables:                                                                                            &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; \-- New table: user\_sso\_profiles                                                                                                                                  &#x20;

&#x20; CREATE TABLE user\_sso\_profiles (                                                                                                                                 &#x20;

&#x20;   user\_id INT PRIMARY KEY REFERENCES users(id),                                                                                                                  &#x20;

&#x20;   provider VARCHAR(50),                                                                                                                                          &#x20;

&#x20;   provider\_id VARCHAR(255),                                                                                                                                      &#x20;

&#x20;   tenant\_id VARCHAR(255),                                                                                                                                        &#x20;

&#x20;   session\_id VARCHAR(255),                                                                                                                                       &#x20;

&#x20;   authenticated\_at TIMESTAMP,                                                                                                                                    &#x20;

&#x20;   provisioned BOOLEAN,                                                                                                                                           &#x20;

&#x20;   first\_login\_at TIMESTAMP,                                                                                                                                      &#x20;

&#x20;   last\_sync\_at TIMESTAMP                                                                                                                                         &#x20;

&#x20; );                                                                                                                                                               &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; \-- New table: user\_domains                                                                                                                                       &#x20;

&#x20; CREATE TABLE user\_domains (                                                                                                                                      &#x20;

&#x20;   user\_id INT PRIMARY KEY REFERENCES users(id),                                                                                                                  &#x20;

&#x20;   subdomain\_slug VARCHAR(50) UNIQUE,                                                                                                                             &#x20;

&#x20;   custom\_domain VARCHAR(255),                                                                                                                                    &#x20;

&#x20;   custom\_domain\_verified BOOLEAN,                                                                                                                                &#x20;

&#x20;   verification\_token VARCHAR(255),                                                                                                                               &#x20;

&#x20;   verified\_at TIMESTAMP,                                                                                                                                         &#x20;

&#x20;   ssl\_enabled BOOLEAN,                                                                                                                                           &#x20;

&#x20;   ssl\_issued\_at TIMESTAMP,                                                                                                                                       &#x20;

&#x20;   did\_document\_uri VARCHAR(500),                                                                                                                                 &#x20;

&#x20;   did\_document JSONB,                                                                                                                                            &#x20;

&#x20;   did\_document\_updated\_at TIMESTAMP,                                                                                                                             &#x20;

&#x20;   signing\_key\_public TEXT,                                                                                                                                       &#x20;

&#x20;   signing\_key\_private\_encrypted TEXT,                                                                                                                            &#x20;

&#x20;   signing\_key\_id VARCHAR(255),                                                                                                                                   &#x20;

&#x20;   signing\_key\_version INT                                                                                                                                        &#x20;

&#x20; );                                                                                                                                                               &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; Impact: Reduces users table by \~20 columns, improves query performance for common operations that don't need SSO/DID data.                                       &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; \---                                                                                                                                                              &#x20;

&#x20; 3\. JSON vs Separate Columns Decision Matrix                                                                                                                      &#x20;

&#x20; ┌───────────────────────┬─────────────────────────────────────┬──────────────────────────────────────────────────────┐                                           &#x20;

&#x20; │       Use Case        │           Recommendation            │                        Reason                        │                                           &#x20;

&#x20; ├───────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤                                           &#x20;

&#x20; │ Feature toggles       │ JSON                                │ Rarely queried individually, often loaded as a group │                                           &#x20;

&#x20; ├───────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤                                           &#x20;

&#x20; │ Configuration objects │ JSON                                │ Self-contained settings that change together         │                                           &#x20;

&#x20; ├───────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤                                           &#x20;

&#x20; │ Foreign keys          │ Separate columns                    │ Referential integrity, indexing                      │                                           &#x20;

&#x20; ├───────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤                                           &#x20;

&#x20; │ Searchable fields     │ Separate columns (with blind index) │ Performance                                          │                                           &#x20;

&#x20; ├───────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤                                           &#x20;

&#x20; │ Audit trail data      │ JSON                                │ Already using JSONB for before/after state           │                                           &#x20;

&#x20; ├───────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤                                           &#x20;

&#x20; │ API responses         │ JSON                                │ Natural fit (Hacienda responses already use JSONB)   │                                           &#x20;

&#x20; ├───────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤                                           &#x20;

&#x20; │ Status enums          │ Separate columns                    │ Indexable, filterable                                │                                           &#x20;

&#x20; ├───────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤                                           &#x20;

&#x20; │ Timestamps            │ Separate columns                    │ Range queries, indexing                              │                                           &#x20;

&#x20; └───────────────────────┴─────────────────────────────────────┴──────────────────────────────────────────────────────┘                                           &#x20;

&#x20; \---                                                                                                                                                              &#x20;

&#x20; 4\. Performance Considerations                                                                                                                                    &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; Current Good Practices (Keep):                                                                                                                                   &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; \- Blind indexes for vault-protected PII fields (fullNameBidx, phoneNumberBidx)                                                                                   &#x20;

&#x20; \- JSONB for API responses (allows GIN indexing if needed)                                                                                                        &#x20;

&#x20; \- Proper FK constraints with ON DELETE CASCADE/SET NULL                                                                                                          &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; Recommended Improvements:                                                                                                                                        &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; A. Partial Indexes for Status Columns                                                                                                                            &#x20;

&#x20; \-- Only index active users (most queries filter on this)                                                                                                         &#x20;

&#x20; CREATE INDEX idx\_users\_active ON users(id) WHERE is\_active = true;                                                                                               &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; \-- Only index pending KYC submissions                                                                                                                            &#x20;

&#x20; CREATE INDEX idx\_kyc\_pending ON kyc\_submissions(user\_id)                                                                                                         &#x20;

&#x20;   WHERE status IN ('pending', 'in\_review');                                                                                                                      &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; B. Composite Indexes for Common Queries                                                                                                                          &#x20;

&#x20; \-- Multi-tenant queries always filter by created\_by + status                                                                                                     &#x20;

&#x20; CREATE INDEX idx\_invoices\_tenant\_status ON invoices(created\_by, status);                                                                                         &#x20;

&#x20; CREATE INDEX idx\_bills\_tenant\_status ON bills(created\_by, status);                                                                                               &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; C. Consider Table Partitioning for Audit Logs                                                                                                                    &#x20;

&#x20; \-- Partition kyc\_audit\_log by month for scalability                                                                                                              &#x20;

&#x20; CREATE TABLE kyc\_audit\_log (                                                                                                                                     &#x20;

&#x20;   ...                                                                                                                                                            &#x20;

&#x20; ) PARTITION BY RANGE (created\_at);                                                                                                                               &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; CREATE TABLE kyc\_audit\_log\_2026\_01 PARTITION OF kyc\_audit\_log                                                                                                    &#x20;

&#x20;   FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');                                                                                                              &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; \---                                                                                                                                                              &#x20;

&#x20; 5\. Scalability Roadmap                                                                                                                                           &#x20;

&#x20; ┌─────────────┬──────────────────────────────────────────┬───────────────────────┐                                                                               &#x20;

&#x20; │    Phase    │                  Action                  │        Impact         │                                                                               &#x20;

&#x20; ├─────────────┼──────────────────────────────────────────┼───────────────────────┤                                                                               &#x20;

&#x20; │ Now         │ Add composite indexes on tenant+status   │ Query performance     │                                                                               &#x20;

&#x20; ├─────────────┼──────────────────────────────────────────┼───────────────────────┤                                                                               &#x20;

&#x20; │ Short-term  │ Consolidate tenant\_settings JSON columns │ Schema simplification │                                                                               &#x20;

&#x20; ├─────────────┼──────────────────────────────────────────┼───────────────────────┤                                                                               &#x20;

&#x20; │ Medium-term │ Extract user\_sso\_profiles, user\_domains  │ Reduced table width   │                                                                               &#x20;

&#x20; ├─────────────┼──────────────────────────────────────────┼───────────────────────┤                                                                               &#x20;

&#x20; │ Long-term   │ Partition audit/log tables by date       │ Storage scalability   │                                                                               &#x20;

&#x20; └─────────────┴──────────────────────────────────────────┴───────────────────────┘                                                                               &#x20;

&#x20; \---                                                                                                                                                              &#x20;

&#x20; 6\. Implementation Priority                                                                                                                                       &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; 1\. Immediate (this sprint):                                                                                                                                      &#x20;

&#x20;   \- Add missing composite indexes for multi-tenant queries                                                                                                       &#x20;

&#x20; 2\. Next sprint:                                                                                                                                                  &#x20;

&#x20;   \- Consolidate tenant\_settings module toggles into enabledModules JSON                                                                                          &#x20;

&#x20;   \- Consolidate tenant\_settings document prefixes into documentPrefixes JSON                                                                                     &#x20;

&#x20; 3\. Future (when SSO usage increases):                                                                                                                            &#x20;

&#x20;   \- Extract SSO fields to user\_sso\_profiles                                                                                                                      &#x20;

&#x20;   \- Extract DID/domain fields to user\_domains                                                                                                                    &#x20;

&#x20;                                                                                                                                                                  &#x20;

&#x20; \---                                                                                                                                                              &#x20;

&#x20; Would you like me to implement any of these recommendations? I'd suggest starting with the tenant\_settings JSON consolidation since we just added company identity

&#x20;  fields and it's a good time to restructure. &#x20;

&#x20;                                                 &#x20;
