Added Database
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
# CRM Database Schema — Enterprise RBAC + CRM
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Enterprise-grade PostgreSQL schema implementing **hierarchical Role-Based Access Control (RBAC)**
|
||||
with a complete CRM system. Designed for production deployments with strict security requirements,
|
||||
audit compliance, and scalability to millions of records.
|
||||
|
||||
---
|
||||
|
||||
## 1. Hierarchical RBAC Model
|
||||
|
||||
Four roles arranged in a strict hierarchy by `hierarchy_level` (lower = higher privilege):
|
||||
|
||||
| Role | Level | Authority |
|
||||
|------|-------|-----------|
|
||||
| **SUPER_ADMIN** | 1 | Unrestricted — create/delete users, assign roles, override all permissions |
|
||||
| **ADMIN** | 2 | Operational — ban/suspend users, review reports, moderate activity. **Cannot create accounts.** |
|
||||
| **SALES_USER** | 3 | CRM operations — leads, customers, opportunities, communications |
|
||||
| **DEVELOPER** | 4 | Technical + CRM read access for post-sale project visibility |
|
||||
|
||||
### Enforcement via PostgreSQL Triggers
|
||||
|
||||
- **`enforce_create_user()`** (BEFORE INSERT ON users): Only SUPER_ADMIN (`hierarchy_level = 1`)
|
||||
can create new user accounts. Bootstrap case (`created_by IS NULL`) is allowed for initial setup.
|
||||
- **`enforce_role_assignment()`** (BEFORE INSERT ON user_roles): Only SUPER_ADMIN can assign roles.
|
||||
Prevents privilege escalation by checking hierarchy levels.
|
||||
|
||||
### Permission Model
|
||||
|
||||
Granular permissions stored in `permissions` table with `(resource, action)` pairs.
|
||||
`role_permissions` maps roles to permissions. The `has_permission(user_id, resource, action)`
|
||||
function provides application-layer authorization checks.
|
||||
|
||||
---
|
||||
|
||||
## 2. Ban & Suspension System
|
||||
|
||||
Two separate tables for enforcement flexibility:
|
||||
|
||||
- **`banned_users`**: Permanent or time-limited bans. Supports reversal (by SUPER_ADMIN).
|
||||
Admins can create bans; SUPER_ADMIN can reverse or permanently delete banned users.
|
||||
- **`suspended_users`**: Time-limited suspensions (always has expiry). Admins can create
|
||||
and lift suspensions.
|
||||
|
||||
Both tables are fully audited via dedicated trigger functions.
|
||||
|
||||
---
|
||||
|
||||
## 3. Session & Login Security
|
||||
|
||||
- **`sessions`**: Stores hashed session tokens with expiry. Only one active session per
|
||||
token hash.
|
||||
- **`login_attempts`**: Captures every login attempt (successful or failed) with IP address
|
||||
and user agent. Used for brute-force detection and audit.
|
||||
- Users have `failed_login_attempts` and `locked_until` for account lockout policies.
|
||||
|
||||
---
|
||||
|
||||
## 4. Password Security
|
||||
|
||||
- All passwords hashed with **bcrypt (cost factor 12)**
|
||||
- `password_change_required` forces password change on first login (TRUE for all non-root
|
||||
test accounts)
|
||||
- Password hashes are opaque to the database — validation is application-layer only
|
||||
|
||||
---
|
||||
|
||||
## 5. UUID Primary Keys
|
||||
|
||||
- All tables use `UUID PRIMARY KEY DEFAULT uuid_generate_v4()`
|
||||
- Fixed UUIDs used for seed data and system entities for referential transparency
|
||||
- Prevents enumeration attacks and supports distributed ID generation
|
||||
|
||||
---
|
||||
|
||||
## 6. Audit Trail
|
||||
|
||||
Every mutation is logged to `audit_logs` via `audit_trigger_function()`:
|
||||
|
||||
- Captures `(table_name, record_id, action, old_data, new_data, changed_by, ip_address)`
|
||||
- Ban actions, suspension actions, and successful logins have dedicated audit triggers
|
||||
- Indexed on `(table_name, record_id)` for per-record lookup
|
||||
|
||||
Dedicated audit views:
|
||||
- `v_active_bans` — Currently active bans with user details
|
||||
- `v_active_suspensions` — Currently active suspensions
|
||||
- `v_user_permissions` — Flattened permission report per user
|
||||
|
||||
---
|
||||
|
||||
## 7. Third Normal Form (3NF)
|
||||
|
||||
Customer model uses the **discriminator pattern**:
|
||||
|
||||
```
|
||||
customers (parent — shared columns: status, owner, tags, score)
|
||||
├── customer_type = 'individual' → individual_customers
|
||||
└── customer_type = 'company' → company_customers
|
||||
```
|
||||
|
||||
All lookup tables are normalized. Contact information, addresses, and notes are
|
||||
separate 1:N child tables.
|
||||
|
||||
---
|
||||
|
||||
## 8. Indexing Strategy
|
||||
|
||||
- **Core indexes**: FK columns, status/sort/date columns, unique constraints
|
||||
- **Partial indexes**: `WHERE deleted_at IS NULL` on soft-delete tables,
|
||||
`WHERE is_active = TRUE` on bans/suspensions, `WHERE due_date < NOW()` on overdue invoices
|
||||
- **Full-text search**: GIN indexes on customers, leads, products, communications, tasks
|
||||
- **Composite indexes**: Common query patterns (e.g., `opportunities(owner_id, stage_id)`)
|
||||
|
||||
---
|
||||
|
||||
## 9. Soft Delete
|
||||
|
||||
- `deleted_at TIMESTAMPTZ` on all business tables
|
||||
- Partial unique indexes ignore soft-deleted rows (e.g., `WHERE deleted_at IS NULL`)
|
||||
- Audit logs capture the full row snapshot before soft-delete
|
||||
|
||||
---
|
||||
|
||||
## 10. Test Accounts
|
||||
|
||||
| Username | Password | Role |
|
||||
|----------|----------|------|
|
||||
| `superadmin_demo` | `SuperAdmin@2026` | SUPER_ADMIN |
|
||||
| `admin_demo` | `AdminAccess@2026` | ADMIN |
|
||||
| `sales_demo` | `SalesAccess@2026` | SALES_USER |
|
||||
| `dev_demo` | `DevTesting@2026` | DEVELOPER |
|
||||
|
||||
---
|
||||
|
||||
## 11. Migration Instructions
|
||||
|
||||
```bash
|
||||
# Create database
|
||||
psql -U postgres -c "CREATE DATABASE crm;"
|
||||
|
||||
# Run full migration
|
||||
psql -U postgres -d crm -f database/migrations/run_all.sql
|
||||
|
||||
# Or step by step:
|
||||
psql -U postgres -d crm -f database/migrations/001_schema.sql
|
||||
psql -U postgres -d crm -f database/migrations/002_seed.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Security Rules Summary
|
||||
|
||||
| Rule | Enforcement |
|
||||
|------|-------------|
|
||||
| Only SUPER_ADMIN creates accounts | Trigger `trg_enforce_create_user` |
|
||||
| Only SUPER_ADMIN assigns roles | Trigger `trg_enforce_role_assignment` |
|
||||
| No privilege escalation | Level check in `enforce_role_assignment()` |
|
||||
| bcrypt password hashing | Application-layer (cost=12) |
|
||||
| Unique usernames/emails | Partial unique indexes |
|
||||
| Force password change on first login | `password_change_required` column |
|
||||
| Audit every login/logout | Trigger `trg_audit_login` on `login_attempts` |
|
||||
| Audit every ban action | Trigger `trg_audit_ban` on `banned_users` |
|
||||
| Audit every suspension action | Trigger `trg_audit_suspension` on `suspended_users` |
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,401 @@
|
||||
-- ============================================================================
|
||||
-- CRM Database Seed Data — RBAC + Test Accounts + Reference Data
|
||||
-- ============================================================================
|
||||
|
||||
-- ============================================================================
|
||||
-- FIXED UUIDs FOR REFERENTIAL INTEGRITY
|
||||
-- ============================================================================
|
||||
|
||||
-- Role UUIDs
|
||||
-- SUPER_ADMIN=1, ADMIN=2, SALES_USER=3, DEVELOPER=4
|
||||
-- User UUIDs: SUPER_ADMIN=u0001, ADMIN=u0002, SALES=u0003, DEV=u0004
|
||||
|
||||
-- ============================================================================
|
||||
-- 1. ROLES
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO roles (id, name, display_name, description, hierarchy_level, is_system) VALUES
|
||||
('00000001-0000-0000-0000-000000000000', 'SUPER_ADMIN', 'Super Administrator',
|
||||
'Highest authority. Full system access. Can create/delete users, assign roles, override all permissions.',
|
||||
1, TRUE),
|
||||
('00000002-0000-0000-0000-000000000000', 'ADMIN', 'Administrator',
|
||||
'Operational administration. Can ban/suspend users, review reports, moderate activity. Cannot create accounts.',
|
||||
2, TRUE),
|
||||
('00000003-0000-0000-0000-000000000000', 'SALES_USER', 'Sales Representative',
|
||||
'Day-to-day CRM operations. Manage leads, customers, opportunities, and communications.',
|
||||
3, TRUE),
|
||||
('00000004-0000-0000-0000-000000000000', 'DEVELOPER', 'Developer',
|
||||
'Internal development/testing with CRM read access. API testing, diagnostics, debugging. Can view projects and customer data post-sale.',
|
||||
4, TRUE)
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- 2. PERMISSIONS — organised by category
|
||||
-- ============================================================================
|
||||
|
||||
-- USER MANAGEMENT (SUPER_ADMIN only)
|
||||
INSERT INTO permissions (id, resource, action, description, category) VALUES
|
||||
('p0010001-0000-0000-0000-000000000000', 'users', 'create', 'Create new user accounts', 'User Management'),
|
||||
('p0010002-0000-0000-0000-000000000000', 'users', 'read', 'View user details', 'User Management'),
|
||||
('p0010003-0000-0000-0000-000000000000', 'users', 'update', 'Update user details', 'User Management'),
|
||||
('p0010004-0000-0000-0000-000000000000', 'users', 'delete', 'Permanently delete user accounts', 'User Management'),
|
||||
('p0010005-0000-0000-0000-000000000000', 'users', 'assign_roles', 'Assign roles to users', 'User Management'),
|
||||
('p0010006-0000-0000-0000-000000000000', 'users', 'promote', 'Promote users to higher roles', 'User Management'),
|
||||
('p0010007-0000-0000-0000-000000000000', 'users', 'demote', 'Demote users to lower roles', 'User Management'),
|
||||
('p0010008-0000-0000-0000-000000000000', 'users', 'reset_password', 'Reset user passwords', 'User Management'),
|
||||
('p0010009-0000-0000-0000-000000000000', 'users', 'disable', 'Disable/disable user accounts', 'User Management'),
|
||||
('p0010010-0000-0000-0000-000000000000', 'users', 'permanently_delete', 'Permanently remove accounts from system', 'User Management')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- BAN & SUSPENSION (ADMIN + SUPER_ADMIN)
|
||||
INSERT INTO permissions (id, resource, action, description, category) VALUES
|
||||
('p0020001-0000-0000-0000-000000000000', 'bans', 'create', 'Ban a user account', 'Ban Management'),
|
||||
('p0020002-0000-0000-0000-000000000000', 'bans', 'reverse', 'Reverse a ban on a user', 'Ban Management'),
|
||||
('p0020003-0000-0000-0000-000000000000', 'bans', 'permanently_delete', 'Permanently delete banned users', 'Ban Management'),
|
||||
('p0020004-0000-0000-0000-000000000000', 'suspensions', 'create', 'Suspend a user account', 'Ban Management'),
|
||||
('p0020005-0000-0000-0000-000000000000', 'suspensions', 'lift', 'Lift a suspension', 'Ban Management'),
|
||||
('p0020006-0000-0000-0000-000000000000', 'suspensions', 'view', 'View suspension records', 'Ban Management')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- CRM CORE (SALES_USER + MANAGERS)
|
||||
INSERT INTO permissions (id, resource, action, description, category) VALUES
|
||||
('p0030001-0000-0000-0000-000000000000', 'customers', 'create', 'Create customer records', 'CRM'),
|
||||
('p0030002-0000-0000-0000-000000000000', 'customers', 'read', 'View customer records', 'CRM'),
|
||||
('p0030003-0000-0000-0000-000000000000', 'customers', 'update', 'Update customer records', 'CRM'),
|
||||
('p0030004-0000-0000-0000-000000000000', 'customers', 'delete', 'Delete customer records', 'CRM'),
|
||||
('p0030005-0000-0000-0000-000000000000', 'leads', 'create', 'Create sales leads', 'CRM'),
|
||||
('p0030006-0000-0000-0000-000000000000', 'leads', 'read', 'View sales leads', 'CRM'),
|
||||
('p0030007-0000-0000-0000-000000000000', 'leads', 'update', 'Update sales leads', 'CRM'),
|
||||
('p0030008-0000-0000-0000-000000000000', 'leads', 'convert', 'Convert leads to customers', 'CRM'),
|
||||
('p0030009-0000-0000-0000-000000000000', 'opportunities', 'create', 'Create sales opportunities', 'CRM'),
|
||||
('p0030010-0000-0000-0000-000000000000', 'opportunities', 'read', 'View sales opportunities', 'CRM'),
|
||||
('p0030011-0000-0000-0000-000000000000', 'opportunities', 'update', 'Update sales pipeline', 'CRM'),
|
||||
('p0030012-0000-0000-0000-000000000000', 'pipeline', 'update', 'Update sales pipeline stages', 'CRM'),
|
||||
('p0030013-0000-0000-0000-000000000000', 'communications', 'create', 'Log calls and meetings', 'CRM'),
|
||||
('p0030014-0000-0000-0000-000000000000', 'communications', 'read', 'View communication history', 'CRM'),
|
||||
('p0030015-0000-0000-0000-000000000000', 'contacts', 'manage', 'Manage customer contact details', 'CRM'),
|
||||
('p0030016-0000-0000-0000-000000000000', 'products', 'read', 'View product catalog', 'CRM'),
|
||||
('p0030017-0000-0000-0000-000000000000', 'invoices', 'read', 'View invoices', 'CRM')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- REPORTING & INCIDENTS (ADMIN)
|
||||
INSERT INTO permissions (id, resource, action, description, category) VALUES
|
||||
('p0040001-0000-0000-0000-000000000000', 'reports', 'customers_view', 'Review customer reports', 'Reporting'),
|
||||
('p0040002-0000-0000-0000-000000000000', 'reports', 'incidents_view', 'Review incident reports', 'Reporting'),
|
||||
('p0040003-0000-0000-0000-000000000000', 'reports', 'complaints_investigate', 'Investigate complaints', 'Reporting'),
|
||||
('p0040004-0000-0000-0000-000000000000', 'crm', 'dashboard_access', 'Access CRM dashboards', 'Reporting'),
|
||||
('p0040005-0000-0000-0000-000000000000', 'activity', 'logs_view', 'View user activity logs', 'Reporting'),
|
||||
('p0040006-0000-0000-0000-000000000000', 'suspicious', 'moderate', 'Moderate suspicious activity', 'Reporting'),
|
||||
('p0040007-0000-0000-0000-000000000000', 'accounts', 'lock_investigation', 'Lock accounts under investigation', 'Reporting')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- AUDIT & SYSTEM (SUPER_ADMIN + limited ADMIN)
|
||||
INSERT INTO permissions (id, resource, action, description, category) VALUES
|
||||
('p0050001-0000-0000-0000-000000000000', 'audit', 'full_access', 'Full audit log access', 'System'),
|
||||
('p0050002-0000-0000-0000-000000000000', 'audit', 'read', 'View audit logs', 'System'),
|
||||
('p0050003-0000-0000-0000-000000000000', 'settings', 'modify', 'Modify system settings', 'System'),
|
||||
('p0050004-0000-0000-0000-000000000000', 'database', 'full_access', 'Full database access', 'System'),
|
||||
('p0050005-0000-0000-0000-000000000000', 'crm', 'full_access', 'Full CRM access override', 'System'),
|
||||
('p0050006-0000-0000-0000-000000000000', 'permissions', 'override', 'Override all permissions', 'System')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- DEVELOPER PERMISSIONS
|
||||
INSERT INTO permissions (id, resource, action, description, category) VALUES
|
||||
('p0060001-0000-0000-0000-000000000000', 'api', 'testing', 'API testing access', 'Developer'),
|
||||
('p0060002-0000-0000-0000-000000000000', 'backend', 'diagnostics', 'Backend diagnostics', 'Developer'),
|
||||
('p0060003-0000-0000-0000-000000000000', 'system', 'logs_view', 'View system logs', 'Developer'),
|
||||
('p0060004-0000-0000-0000-000000000000', 'database', 'diagnostics', 'Database diagnostics', 'Developer'),
|
||||
('p0060005-0000-0000-0000-000000000000', 'debugging', 'access', 'Debugging access', 'Developer'),
|
||||
('p0060006-0000-0000-0000-000000000000', 'testing', 'internal', 'Internal testing access', 'Developer')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- 3. ROLE-PERMISSION MAPPING
|
||||
-- ============================================================================
|
||||
|
||||
-- SUPER_ADMIN — ALL permissions
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT '00000001-0000-0000-0000-000000000000', id FROM permissions
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ADMIN — Bans, Suspensions, Reporting, CRM read, Activity logs
|
||||
INSERT INTO role_permissions (role_id, permission_id, granted_by) VALUES
|
||||
-- Bans & Suspensions
|
||||
('00000002-0000-0000-0000-000000000000', 'p0020001-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0020004-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0020005-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0020006-0000-0000-0000-000000000000', NULL),
|
||||
-- User read + disable
|
||||
('00000002-0000-0000-0000-000000000000', 'p0010002-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-0000-000000000000', 'p0010009-0000-0000-0000-000000000000', NULL),
|
||||
-- Reporting & Incidents
|
||||
('00000002-0000-0000-0000-000000000000', 'p0040001-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0040002-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0040003-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0040004-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0040005-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0040006-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0040007-0000-0000-0000-000000000000', NULL),
|
||||
-- CRM read access
|
||||
('00000002-0000-0000-0000-000000000000', 'p0030002-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0030006-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0030010-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0030014-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0030016-0000-0000-0000-000000000000', NULL),
|
||||
('00000002-0000-0000-0000-000000000000', 'p0030017-0000-0000-0000-000000000000', NULL),
|
||||
-- Audit read
|
||||
('00000002-0000-0000-0000-000000000000', 'p0050002-0000-0000-0000-000000000000', NULL)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- SALES_USER — CRM operations
|
||||
INSERT INTO role_permissions (role_id, permission_id, granted_by) VALUES
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030001-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030002-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030003-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030005-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030006-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030007-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030008-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030009-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030010-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030011-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030012-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030013-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030014-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030015-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030016-0000-0000-0000-000000000000', NULL),
|
||||
('00000003-0000-0000-0000-000000000000', 'p0030017-0000-0000-0000-000000000000', NULL)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- DEVELOPER — Technical permissions + CRM read access (post-sale project visibility)
|
||||
INSERT INTO role_permissions (role_id, permission_id, granted_by) VALUES
|
||||
-- Developer technical permissions
|
||||
('00000004-0000-0000-0000-000000000000', 'p0060001-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0060002-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0060003-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0060004-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0060005-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0060006-0000-0000-0000-000000000000', NULL),
|
||||
-- User self-view
|
||||
('00000004-0000-0000-0000-000000000000', 'p0010002-0000-0000-0000-000000000000', NULL),
|
||||
-- CRM access (matching SALES_USER level for post-sale project visibility)
|
||||
('00000004-0000-0000-0000-000000000000', 'p0030002-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0030006-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0030010-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0030014-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0030016-0000-0000-0000-000000000000', NULL),
|
||||
('00000004-0000-0000-0000-000000000000', 'p0030017-0000-0000-0000-000000000000', NULL)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- 4. TEST ACCOUNTS
|
||||
-- ============================================================================
|
||||
-- Passwords (bcrypt, cost=12):
|
||||
-- superadmin_demo / SuperAdmin@2026
|
||||
-- admin_demo / AdminAccess@2026
|
||||
-- sales_demo / SalesAccess@2026
|
||||
-- dev_demo / DevTesting@2026
|
||||
|
||||
INSERT INTO users (id, username, email, password_hash, first_name, last_name, is_active, password_change_required) VALUES
|
||||
('00000000-0000-0000-0000-000000000001', 'superadmin_demo', 'superadmin@crm.demo',
|
||||
'$2b$12$C5hczK17I.bu6ILzmGW0U.UnFSdfTuDh42C8t16nxRKaUtXKkdWlC',
|
||||
'Super', 'Admin', TRUE, FALSE),
|
||||
('00000000-0000-0000-0000-000000000002', 'admin_demo', 'admin@crm.demo',
|
||||
'$2b$12$TCDq5.sXHA4kWelQPKO6DeQo.WW.NeTuNtOed57UdQ3lRs7.rdkNy',
|
||||
'System', 'Admin', TRUE, TRUE),
|
||||
('00000000-0000-0000-0000-000000000003', 'sales_demo', 'sales@crm.demo',
|
||||
'$2b$12$Xhh20UmTn.LTQAs4v4cHx.yQgvuYyNo6TkPaytQ1Q8o0oTPCtIj7W',
|
||||
'Sales', 'User', TRUE, TRUE),
|
||||
('00000000-0000-0000-0000-000000000004', 'dev_demo', 'dev@crm.demo',
|
||||
'$2b$12$ghyJFb17lXoFOCYUPB6Fk.q8wDNOJhq9OUPNzd5DKaZsDjCF2NBJa',
|
||||
'Dev', 'User', TRUE, TRUE)
|
||||
ON CONFLICT (username) DO NOTHING;
|
||||
|
||||
-- Update the SUPER_ADMIN to set created_by to self (post-bootstrap)
|
||||
UPDATE users SET created_by = '00000000-0000-0000-0000-000000000001'
|
||||
WHERE id = '00000000-0000-0000-0000-000000000001' AND created_by IS NULL;
|
||||
|
||||
-- Set created_by for other users (SUPER_ADMIN created them)
|
||||
UPDATE users SET created_by = '00000000-0000-0000-0000-000000000001'
|
||||
WHERE id IN ('00000000-0000-0000-0000-000000000002','00000000-0000-0000-0000-000000000003','00000000-0000-0000-0000-000000000004')
|
||||
AND created_by IS NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- 5. ROLE ASSIGNMENTS
|
||||
-- ============================================================================
|
||||
|
||||
-- Assign SUPER_ADMIN role to superadmin_demo (bootstrap — assigned_by NULL)
|
||||
INSERT INTO user_roles (user_id, role_id, assigned_by) VALUES
|
||||
('00000000-0000-0000-0000-000000000001', '00000001-0000-0000-0000-000000000000', NULL)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Assign remaining roles (SUPER_ADMIN assigned them)
|
||||
INSERT INTO user_roles (user_id, role_id, assigned_by) VALUES
|
||||
('00000000-0000-0000-0000-000000000002', '00000002-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'),
|
||||
('00000000-0000-0000-0000-000000000003', '00000003-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'),
|
||||
('00000000-0000-0000-0000-000000000004', '00000004-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- 6. REFERENCE DATA
|
||||
-- ============================================================================
|
||||
|
||||
-- Customer Statuses
|
||||
INSERT INTO customer_statuses (id, name, description, color, sort_order, is_default) VALUES
|
||||
('c0000000-0000-0000-0000-000000000001', 'Active', 'Customer in good standing', '#22C55E', 1, TRUE),
|
||||
('c0000000-0000-0000-0000-000000000002', 'Inactive', 'No recent activity', '#A0AEC0', 2, FALSE),
|
||||
('c0000000-0000-0000-0000-000000000003', 'At Risk', 'Showing signs of churn', '#F59E0B', 3, FALSE),
|
||||
('c0000000-0000-0000-0000-000000000004', 'Churned', 'Relationship ended', '#EF4444', 4, FALSE),
|
||||
('c0000000-0000-0000-0000-000000000005', 'VIP', 'High-value priority customer', '#8B5CF6', 0, FALSE)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Lead Sources
|
||||
INSERT INTO lead_sources (id, name, description) VALUES
|
||||
('d0000000-0000-0000-0000-000000000001', 'Website', 'Organic website traffic or web forms'),
|
||||
('d0000000-0000-0000-0000-000000000002', 'Referral', 'Referred by existing customer'),
|
||||
('d0000000-0000-0000-0000-000000000003', 'LinkedIn', 'LinkedIn outreach'),
|
||||
('d0000000-0000-0000-0000-000000000004', 'Email Campaign', 'Email marketing campaign'),
|
||||
('d0000000-0000-0000-0000-000000000005', 'Trade Show', 'Event or trade show'),
|
||||
('d0000000-0000-0000-0000-000000000006', 'Cold Call', 'Outbound cold calling'),
|
||||
('d0000000-0000-0000-0000-000000000007', 'Partner', 'Channel partner referral'),
|
||||
('d0000000-0000-0000-0000-000000000008', 'Social Media', 'Social media platforms'),
|
||||
('d0000000-0000-0000-0000-000000000009', 'Advertisement', 'Paid advertising'),
|
||||
('d0000000-0000-0000-0000-000000000010', 'Other', 'Other sources')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Lead Stages
|
||||
INSERT INTO lead_stages (id, name, description, sort_order, probability) VALUES
|
||||
('e0000000-0000-0000-0000-000000000001', 'New', 'Newly captured lead', 1, 5),
|
||||
('e0000000-0000-0000-0000-000000000002', 'Contacted', 'Initial contact made', 2, 10),
|
||||
('e0000000-0000-0000-0000-000000000003', 'Qualified', 'Meets qualification criteria', 3, 25),
|
||||
('e0000000-0000-0000-0000-000000000004', 'Interested', 'Shown interest', 4, 40),
|
||||
('e0000000-0000-0000-0000-000000000005', 'Demo Scheduled', 'Product demo scheduled', 5, 60),
|
||||
('e0000000-0000-0000-0000-000000000006', 'Negotiation', 'In price/terms negotiation', 6, 75),
|
||||
('e0000000-0000-0000-0000-000000000007', 'Closed Won', 'Successfully converted', 7, 100),
|
||||
('e0000000-0000-0000-0000-000000000008', 'Closed Lost', 'Lead was lost', 8, 0)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Deal Stages
|
||||
INSERT INTO deal_stages (id, name, description, sort_order, probability) VALUES
|
||||
('f0000000-0000-0000-0000-000000000001', 'Prospecting', 'Initial qualification', 1, 10),
|
||||
('f0000000-0000-0000-0000-000000000002', 'Discovery', 'Understanding needs', 2, 20),
|
||||
('f0000000-0000-0000-0000-000000000003', 'Proposal', 'Solution proposed', 3, 40),
|
||||
('f0000000-0000-0000-0000-000000000004', 'Negotiation', 'Commercial discussions', 4, 60),
|
||||
('f0000000-0000-0000-0000-000000000005', 'Closing', 'Final stage before decision', 5, 80),
|
||||
('f0000000-0000-0000-0000-000000000006', 'Won', 'Deal closed won', 6, 100),
|
||||
('f0000000-0000-0000-0000-000000000007', 'Lost', 'Deal closed lost', 7, 0)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Communication Types
|
||||
INSERT INTO communication_types (id, name, description, icon) VALUES
|
||||
('g0000000-0000-0000-0000-000000000001', 'Email', 'Email correspondence', 'mail'),
|
||||
('g0000000-0000-0000-0000-000000000002', 'Phone Call', 'Telephone conversation', 'phone'),
|
||||
('g0000000-0000-0000-0000-000000000003', 'Meeting', 'In-person or virtual meeting', 'users'),
|
||||
('g0000000-0000-0000-0000-000000000004', 'Chat', 'Instant messaging', 'message-circle'),
|
||||
('g0000000-0000-0000-0000-000000000005', 'SMS', 'Text message', 'message-square'),
|
||||
('g0000000-0000-0000-0000-000000000006', 'Video Call', 'Video conference', 'video'),
|
||||
('g0000000-0000-0000-0000-000000000007', 'Letter', 'Physical mail', 'file-text')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Task Priorities
|
||||
INSERT INTO task_priorities (id, name, color, sort_order) VALUES
|
||||
('h0000000-0000-0000-0000-000000000001', 'Critical', '#EF4444', 1),
|
||||
('h0000000-0000-0000-0000-000000000002', 'High', '#F59E0B', 2),
|
||||
('h0000000-0000-0000-0000-000000000003', 'Medium', '#3B82F6', 3),
|
||||
('h0000000-0000-0000-0000-000000000004', 'Low', '#A0AEC0', 4)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Invoice Statuses
|
||||
INSERT INTO invoice_statuses (id, name, description, color, sort_order) VALUES
|
||||
('i0000000-0000-0000-0000-000000000001', 'Draft', 'Invoice in draft state', '#A0AEC0', 1),
|
||||
('i0000000-0000-0000-0000-000000000002', 'Sent', 'Invoice sent to customer', '#3B82F6', 2),
|
||||
('i0000000-0000-0000-0000-000000000003', 'Paid', 'Payment received in full', '#22C55E', 3),
|
||||
('i0000000-0000-0000-0000-000000000004', 'Overdue', 'Payment past due date', '#EF4444', 4),
|
||||
('i0000000-0000-0000-0000-000000000005', 'Partially Paid', 'Partial payment received', '#F59E0B', 5),
|
||||
('i0000000-0000-0000-0000-000000000006', 'Cancelled', 'Invoice cancelled', '#6B7280', 6),
|
||||
('i0000000-0000-0000-0000-000000000007', 'Refunded', 'Payment refunded', '#8B5CF6', 7)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Product Categories
|
||||
INSERT INTO product_categories (id, name, description, sort_order) VALUES
|
||||
('j0000000-0000-0000-0000-000000000001', 'Software', 'Software products and licenses', 1),
|
||||
('j0000000-0000-0000-0000-000000000002', 'Hardware', 'Physical hardware products', 2),
|
||||
('j0000000-0000-0000-0000-000000000003', 'Services', 'Professional services', 3),
|
||||
('j0000000-0000-0000-0000-000000000004', 'Subscription', 'Recurring subscription plans', 4),
|
||||
('j0000000-0000-0000-0000-000000000005', 'Training', 'Training and certification', 5)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- 7. SAMPLE CRM DATA (for demo purposes)
|
||||
-- ============================================================================
|
||||
|
||||
-- Sample Products
|
||||
INSERT INTO products (id, category_id, name, description, sku, unit_price, cost_price) VALUES
|
||||
('k0000000-0000-0000-0000-000000000001', 'j0000000-0000-0000-0000-000000000001', 'CRM Pro License', 'Enterprise CRM license per user/year', 'SW-CRM-001', 1200.00, 300.00),
|
||||
('k0000000-0000-0000-0000-000000000002', 'j0000000-0000-0000-0000-000000000001', 'CRM Basic License', 'Basic CRM license per user/year', 'SW-CRM-002', 600.00, 150.00),
|
||||
('k0000000-0000-0000-0000-000000000003', 'j0000000-0000-0000-0000-000000000003', 'Implementation Service', 'CRM implementation and setup', 'SV-IMP-001', 15000.00, 7500.00),
|
||||
('k0000000-0000-0000-0000-000000000004', 'j0000000-0000-0000-0000-000000000003', 'Consulting Day Rate', 'Senior consultant daily rate', 'SV-CON-001', 2500.00, 1000.00),
|
||||
('k0000000-0000-0000-0000-000000000005', 'j0000000-0000-0000-0000-000000000005', 'CRM Training - Basic', 'Basic user training per person', 'TR-BAS-001', 500.00, 200.00),
|
||||
('k0000000-0000-0000-0000-000000000006', 'j0000000-0000-0000-0000-000000000005', 'CRM Training - Advanced', 'Advanced admin training per person', 'TR-ADV-001', 1000.00, 400.00)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Sample Customers
|
||||
INSERT INTO customers (id, customer_type, status_id, owner_id, source, tags, score) VALUES
|
||||
('a0000000-0000-0000-0000-000000000001', 'company', 'c0000000-0000-0000-0000-000000000001',
|
||||
'00000000-0000-0000-0000-000000000003', 'Website', ARRAY['tech','enterprise'], 85),
|
||||
('a0000000-0000-0000-0000-000000000002', 'individual', 'c0000000-0000-0000-0000-000000000001',
|
||||
'00000000-0000-0000-0000-000000000003', 'Referral', ARRAY['retail'], 60),
|
||||
('a0000000-0000-0000-0000-000000000003', 'company', 'c0000000-0000-0000-0000-000000000005',
|
||||
'00000000-0000-0000-0000-000000000003', 'LinkedIn', ARRAY['finance','enterprise','vip'], 95),
|
||||
('a0000000-0000-0000-0000-000000000004', 'individual', 'c0000000-0000-0000-0000-000000000002',
|
||||
'00000000-0000-0000-0000-000000000003', 'Email Campaign', ARRAY['education'], 15)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Company details
|
||||
INSERT INTO company_customers (customer_id, company_name, registration_number, tax_id, website, industry, company_size, annual_revenue) VALUES
|
||||
('a0000000-0000-0000-0000-000000000001', 'TechCorp International', 'REG-2024-001', 'TAX-987654', 'https://techcorp.example.com', 'Technology', '500-1000', 50000000.00),
|
||||
('a0000000-0000-0000-0000-000000000003', 'FinServ Group', 'REG-2024-002', 'TAX-123456', 'https://finserv.example.com', 'Financial Services', '1000-5000', 250000000.00)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Individual details
|
||||
INSERT INTO individual_customers (customer_id, first_name, last_name, job_title, company_name) VALUES
|
||||
('a0000000-0000-0000-0000-000000000002', 'Michael', 'Brown', 'Store Owner', 'Browns Retail Shop'),
|
||||
('a0000000-0000-0000-0000-000000000004', 'Sarah', 'Davis', 'Professor', 'State University')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Contact Information
|
||||
INSERT INTO contact_information (customer_id, type, value, is_primary) VALUES
|
||||
('a0000000-0000-0000-0000-000000000001', 'email', 'contact@techcorp.example.com', TRUE),
|
||||
('a0000000-0000-0000-0000-000000000001', 'phone', '+1-555-0200', TRUE),
|
||||
('a0000000-0000-0000-0000-000000000002', 'email', 'michael.brown@email.example.com', TRUE),
|
||||
('a0000000-0000-0000-0000-000000000002', 'mobile', '+1-555-0201', TRUE),
|
||||
('a0000000-0000-0000-0000-000000000003', 'email', 'info@finserv.example.com', TRUE),
|
||||
('a0000000-0000-0000-0000-000000000003', 'phone', '+1-555-0202', TRUE),
|
||||
('a0000000-0000-0000-0000-000000000004', 'email', 'sarah.davis@email.example.com', TRUE)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Sample Leads
|
||||
INSERT INTO leads (id, source_id, stage_id, assigned_to, company_name, contact_name, email, interest_level, score) VALUES
|
||||
('l0000000-0000-0000-0000-000000000001', 'd0000000-0000-0000-0000-000000000001', 'e0000000-0000-0000-0000-000000000004',
|
||||
'00000000-0000-0000-0000-000000000003', 'DataFlow Analytics', 'David Miller', 'david@dataflow.example.com', 'high', 70),
|
||||
('l0000000-0000-0000-0000-000000000002', 'd0000000-0000-0000-0000-000000000004', 'e0000000-0000-0000-0000-000000000003',
|
||||
'00000000-0000-0000-0000-000000000003', 'GreenEarth Nonprofit', 'Emma Green', 'emma@greenearth.example.com', 'medium', 45)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Sample Opportunities
|
||||
INSERT INTO opportunities (id, customer_id, stage_id, owner_id, name, estimated_revenue, probability, expected_close_date) VALUES
|
||||
('m0000000-0000-0000-0000-000000000001', 'a0000000-0000-0000-0000-000000000001', 'f0000000-0000-0000-0000-000000000005',
|
||||
'00000000-0000-0000-0000-000000000003', 'TechCorp - Annual Renewal', 120000.00, 80, '2024-12-31'),
|
||||
('m0000000-0000-0000-0000-000000000002', 'a0000000-0000-0000-0000-000000000003', 'f0000000-0000-0000-0000-000000000003',
|
||||
'00000000-0000-0000-0000-000000000003', 'FinServ - Enterprise Upgrade', 250000.00, 40, '2025-03-15')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Sample Tasks
|
||||
INSERT INTO tasks (id, customer_id, opportunity_id, assigned_to, assigned_by, title, priority_id, status, due_date) VALUES
|
||||
('n0000000-0000-0000-0000-000000000001', 'a0000000-0000-0000-0000-000000000001', 'm0000000-0000-0000-0000-000000000001',
|
||||
'00000000-0000-0000-0000-000000000003', '00000000-0000-0000-0000-000000000001',
|
||||
'Prepare renewal proposal for TechCorp', 'h0000000-0000-0000-0000-000000000001', 'in_progress', NOW() + INTERVAL '7 days'),
|
||||
('n0000000-0000-0000-0000-000000000002', 'a0000000-0000-0000-0000-000000000003', 'm0000000-0000-0000-0000-000000000002',
|
||||
'00000000-0000-0000-0000-000000000003', '00000000-0000-0000-0000-000000000001',
|
||||
'Schedule FinServ executive meeting', 'h0000000-0000-0000-0000-000000000002', 'pending', NOW() + INTERVAL '3 days')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -0,0 +1,19 @@
|
||||
-- ============================================================================
|
||||
-- CRM Database — Full Migration Runner
|
||||
-- ============================================================================
|
||||
-- Usage: psql -U postgres -d crm -f run_all.sql
|
||||
-- ============================================================================
|
||||
|
||||
\echo '=== Running 001_schema.sql (Tables + Constraints + Functions + Views) ==='
|
||||
\i 001_schema.sql
|
||||
|
||||
\echo '=== Running 002_seed.sql (RBAC + Test Accounts + Reference Data) ==='
|
||||
\i 002_seed.sql
|
||||
|
||||
\echo '=== Migration Complete ==='
|
||||
\echo ''
|
||||
\echo 'Test accounts created:'
|
||||
\echo ' superadmin_demo / SuperAdmin@2026'
|
||||
\echo ' admin_demo / AdminAccess@2026'
|
||||
\echo ' sales_demo / SalesAccess@2026'
|
||||
\echo ' dev_demo / DevTesting@2026'
|
||||
Generated
+9
@@ -27,6 +27,7 @@
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@tanstack/react-table": "^8.20.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"emoji-mart": "^5.6.0",
|
||||
@@ -2985,6 +2986,14 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz",
|
||||
"integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==",
|
||||
"bin": {
|
||||
"bcrypt": "bin/bcrypt"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@tanstack/react-table": "^8.20.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"emoji-mart": "^5.6.0",
|
||||
|
||||
Reference in New Issue
Block a user