Spaces:
Sleeping
Sleeping
update the metadata field in all tables to additional_metadata
Browse files
docs/dev/METADATA_MIGRATION.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Metadata Column Migration Guide
|
| 2 |
+
|
| 3 |
+
## Problem
|
| 4 |
+
|
| 5 |
+
The column name `metadata` conflicts with SQLAlchemy's internal `metadata` attribute, causing this error:
|
| 6 |
+
|
| 7 |
+
```
|
| 8 |
+
sqlalchemy.exc.InvalidRequestError: Attribute name 'metadata' is reserved when using the Declarative API.
|
| 9 |
+
```
|
| 10 |
+
|
| 11 |
+
## Solution
|
| 12 |
+
|
| 13 |
+
Rename `metadata` → `additional_metadata` across all tables.
|
| 14 |
+
|
| 15 |
+
## Migration Steps
|
| 16 |
+
|
| 17 |
+
### 1. Run SQL Migration
|
| 18 |
+
|
| 19 |
+
**Location:** `migrations/001_rename_metadata_to_additional_metadata.sql`
|
| 20 |
+
|
| 21 |
+
**How to run:**
|
| 22 |
+
1. Open Supabase Dashboard
|
| 23 |
+
2. Go to SQL Editor
|
| 24 |
+
3. Copy the migration file content
|
| 25 |
+
4. Execute the SQL
|
| 26 |
+
5. Verify success
|
| 27 |
+
|
| 28 |
+
**Verification query:**
|
| 29 |
+
```sql
|
| 30 |
+
SELECT
|
| 31 |
+
table_name,
|
| 32 |
+
column_name,
|
| 33 |
+
data_type
|
| 34 |
+
FROM information_schema.columns
|
| 35 |
+
WHERE table_schema = 'public'
|
| 36 |
+
AND column_name IN ('metadata', 'additional_metadata')
|
| 37 |
+
ORDER BY
|
| 38 |
+
CASE WHEN column_name = 'metadata' THEN 0 ELSE 1 END,
|
| 39 |
+
table_name;
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
**Expected result:**
|
| 43 |
+
- 0 rows with `metadata`
|
| 44 |
+
- 32 rows with `additional_metadata`
|
| 45 |
+
|
| 46 |
+
### 2. Update Application Code
|
| 47 |
+
|
| 48 |
+
**Before:**
|
| 49 |
+
```python
|
| 50 |
+
# Workaround (don't use this)
|
| 51 |
+
user_metadata = Column('metadata', JSONB, default={})
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
**After:**
|
| 55 |
+
```python
|
| 56 |
+
# Clean approach
|
| 57 |
+
additional_metadata = Column(JSONB, default={})
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
### 3. Update All Models
|
| 61 |
+
|
| 62 |
+
When creating new models, use `additional_metadata`:
|
| 63 |
+
|
| 64 |
+
```python
|
| 65 |
+
from sqlalchemy import Column
|
| 66 |
+
from sqlalchemy.dialects.postgresql import JSONB
|
| 67 |
+
from app.models.base import BaseModel
|
| 68 |
+
|
| 69 |
+
class MyModel(BaseModel):
|
| 70 |
+
__tablename__ = "my_table"
|
| 71 |
+
|
| 72 |
+
# ... other columns ...
|
| 73 |
+
|
| 74 |
+
# Use additional_metadata (not metadata!)
|
| 75 |
+
additional_metadata = Column(JSONB, default={})
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
### 4. Usage in Code
|
| 79 |
+
|
| 80 |
+
```python
|
| 81 |
+
# Create user with metadata
|
| 82 |
+
user = User(
|
| 83 |
+
name="John Doe",
|
| 84 |
+
email="john@example.com",
|
| 85 |
+
additional_metadata={
|
| 86 |
+
"source": "mobile_app",
|
| 87 |
+
"referral_code": "ABC123"
|
| 88 |
+
}
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
# Access metadata
|
| 92 |
+
print(user.additional_metadata)
|
| 93 |
+
# Output: {"source": "mobile_app", "referral_code": "ABC123"}
|
| 94 |
+
|
| 95 |
+
# Update metadata
|
| 96 |
+
user.additional_metadata["last_login"] = "2025-11-15"
|
| 97 |
+
db.commit()
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
## Tables Affected
|
| 101 |
+
|
| 102 |
+
32 tables have the `additional_metadata` column:
|
| 103 |
+
|
| 104 |
+
**Organizations & Users:**
|
| 105 |
+
- clients
|
| 106 |
+
- contractors
|
| 107 |
+
- users
|
| 108 |
+
- user_financial_accounts
|
| 109 |
+
- user_asset_assignments
|
| 110 |
+
- user_document_links
|
| 111 |
+
|
| 112 |
+
**Projects & Teams:**
|
| 113 |
+
- projects
|
| 114 |
+
- project_regions
|
| 115 |
+
- project_subcontractors
|
| 116 |
+
- timesheets
|
| 117 |
+
|
| 118 |
+
**Customers & Sales:**
|
| 119 |
+
- customers
|
| 120 |
+
- sales_orders
|
| 121 |
+
- subscriptions
|
| 122 |
+
- incidents
|
| 123 |
+
|
| 124 |
+
**Tickets & Work:**
|
| 125 |
+
- tickets
|
| 126 |
+
- ticket_status_history
|
| 127 |
+
- ticket_expenses
|
| 128 |
+
- ticket_comments
|
| 129 |
+
- customer_communications
|
| 130 |
+
|
| 131 |
+
**Tasks:**
|
| 132 |
+
- tasks
|
| 133 |
+
|
| 134 |
+
**Inventory:**
|
| 135 |
+
- inventory_assignments
|
| 136 |
+
|
| 137 |
+
**Financial:**
|
| 138 |
+
- project_finance
|
| 139 |
+
- payment_logs
|
| 140 |
+
- user_payroll
|
| 141 |
+
|
| 142 |
+
**Documents & Notifications:**
|
| 143 |
+
- documents
|
| 144 |
+
- notifications
|
| 145 |
+
|
| 146 |
+
**System & Audit:**
|
| 147 |
+
- audit_logs
|
| 148 |
+
|
| 149 |
+
**Platform Billing:**
|
| 150 |
+
- organization_subscriptions
|
| 151 |
+
- usage_metrics
|
| 152 |
+
|
| 153 |
+
## Rollback
|
| 154 |
+
|
| 155 |
+
If you need to rollback (not recommended):
|
| 156 |
+
|
| 157 |
+
```sql
|
| 158 |
+
BEGIN;
|
| 159 |
+
|
| 160 |
+
-- Reverse all renames
|
| 161 |
+
ALTER TABLE clients RENAME COLUMN additional_metadata TO metadata;
|
| 162 |
+
-- ... (repeat for all tables)
|
| 163 |
+
|
| 164 |
+
COMMIT;
|
| 165 |
+
```
|
| 166 |
+
|
| 167 |
+
## Benefits
|
| 168 |
+
|
| 169 |
+
1. ✅ **No SQLAlchemy Conflicts** - Clean model definitions
|
| 170 |
+
2. ✅ **Better Naming** - More descriptive column name
|
| 171 |
+
3. ✅ **Consistent** - All tables follow same pattern
|
| 172 |
+
4. ✅ **Future-Proof** - No workarounds needed
|
| 173 |
+
|
| 174 |
+
## Checklist
|
| 175 |
+
|
| 176 |
+
- [ ] Run SQL migration in Supabase
|
| 177 |
+
- [ ] Verify all columns renamed (0 `metadata`, 32 `additional_metadata`)
|
| 178 |
+
- [ ] Update User model (already done)
|
| 179 |
+
- [ ] Test application startup
|
| 180 |
+
- [ ] Update schema.sql documentation
|
| 181 |
+
- [ ] Create future models with `additional_metadata`
|
| 182 |
+
|
| 183 |
+
## Notes
|
| 184 |
+
|
| 185 |
+
- This is a **one-time migration**
|
| 186 |
+
- Run it **before** you have production data
|
| 187 |
+
- The migration is **transactional** (all or nothing)
|
| 188 |
+
- **No data loss** - just column rename
|
docs/schema/schema.sql
CHANGED
|
@@ -138,7 +138,7 @@ CREATE TABLE clients (
|
|
| 138 |
website TEXT,
|
| 139 |
is_active BOOLEAN DEFAULT TRUE,
|
| 140 |
default_sla_days INTEGER, -- Default SLA window (days) for jobs under this client
|
| 141 |
-
|
| 142 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 143 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 144 |
deleted_at TIMESTAMP WITH TIME ZONE NULL
|
|
@@ -156,7 +156,7 @@ CREATE TABLE contractors (
|
|
| 156 |
main_phone TEXT,
|
| 157 |
is_active BOOLEAN DEFAULT TRUE,
|
| 158 |
competencies JSONB DEFAULT '{}', -- Array of specializations (e.g., ["FTTH", "Fixed Wireless", "Fiber Splicing"])
|
| 159 |
-
|
| 160 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 161 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 162 |
deleted_at TIMESTAMP WITH TIME ZONE NULL,
|
|
@@ -216,7 +216,7 @@ CREATE TABLE users (
|
|
| 216 |
current_location_updated_at TIMESTAMP WITH TIME ZONE,
|
| 217 |
|
| 218 |
-- Additional metadata for future flexibility
|
| 219 |
-
|
| 220 |
|
| 221 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 222 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -480,7 +480,7 @@ CREATE TABLE projects (
|
|
| 480 |
activation_requirements JSONB DEFAULT '[]',
|
| 481 |
|
| 482 |
-- Additional metadata for future flexibility
|
| 483 |
-
|
| 484 |
|
| 485 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 486 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -531,7 +531,7 @@ CREATE TABLE project_subcontractors (
|
|
| 531 |
|
| 532 |
-- Metadata
|
| 533 |
notes TEXT,
|
| 534 |
-
|
| 535 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 536 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 537 |
deleted_at TIMESTAMP WITH TIME ZONE NULL
|
|
@@ -580,7 +580,7 @@ CREATE TABLE project_regions (
|
|
| 580 |
|
| 581 |
-- Metadata
|
| 582 |
notes TEXT,
|
| 583 |
-
|
| 584 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 585 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 586 |
deleted_at TIMESTAMP WITH TIME ZONE NULL
|
|
@@ -696,7 +696,7 @@ CREATE TABLE timesheets (
|
|
| 696 |
|
| 697 |
-- Metadata
|
| 698 |
notes TEXT,
|
| 699 |
-
|
| 700 |
|
| 701 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 702 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -767,7 +767,7 @@ CREATE TABLE customers (
|
|
| 767 |
is_active BOOLEAN DEFAULT TRUE,
|
| 768 |
|
| 769 |
-- Additional metadata for future flexibility
|
| 770 |
-
|
| 771 |
|
| 772 |
-- Timestamps
|
| 773 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -845,7 +845,7 @@ CREATE TABLE sales_orders (
|
|
| 845 |
|
| 846 |
-- Metadata
|
| 847 |
notes TEXT,
|
| 848 |
-
|
| 849 |
submitted_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 850 |
submitted_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 851 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -932,7 +932,7 @@ CREATE TABLE subscriptions (
|
|
| 932 |
|
| 933 |
-- Metadata
|
| 934 |
notes TEXT,
|
| 935 |
-
|
| 936 |
activated_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 937 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 938 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -987,7 +987,7 @@ CREATE TABLE incidents (
|
|
| 987 |
cancellation_reason TEXT, -- Why incident was cancelled (if status='cancelled')
|
| 988 |
|
| 989 |
-- Metadata
|
| 990 |
-
|
| 991 |
|
| 992 |
-- Timestamps
|
| 993 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -1129,7 +1129,7 @@ CREATE TABLE tickets (
|
|
| 1129 |
|
| 1130 |
-- Metadata
|
| 1131 |
notes TEXT,
|
| 1132 |
-
|
| 1133 |
|
| 1134 |
-- Concurrency Control (Optimistic Locking)
|
| 1135 |
version INTEGER DEFAULT 1 NOT NULL,
|
|
@@ -1350,7 +1350,7 @@ CREATE TABLE ticket_status_history (
|
|
| 1350 |
|
| 1351 |
-- Metadata
|
| 1352 |
notes TEXT,
|
| 1353 |
-
|
| 1354 |
|
| 1355 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1356 |
deleted_at TIMESTAMP WITH TIME ZONE NULL,
|
|
@@ -1401,7 +1401,7 @@ CREATE TABLE ticket_comments (
|
|
| 1401 |
edited_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 1402 |
|
| 1403 |
-- Metadata
|
| 1404 |
-
|
| 1405 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1406 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1407 |
deleted_at TIMESTAMP WITH TIME ZONE NULL,
|
|
@@ -1455,7 +1455,7 @@ CREATE TABLE customer_communications (
|
|
| 1455 |
followup_notes TEXT,
|
| 1456 |
|
| 1457 |
-- Metadata
|
| 1458 |
-
|
| 1459 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1460 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1461 |
|
|
@@ -1542,7 +1542,7 @@ CREATE TABLE ticket_expenses (
|
|
| 1542 |
|
| 1543 |
-- Metadata
|
| 1544 |
notes TEXT,
|
| 1545 |
-
|
| 1546 |
|
| 1547 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1548 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -1612,7 +1612,7 @@ CREATE TABLE tasks (
|
|
| 1612 |
|
| 1613 |
-- Metadata
|
| 1614 |
notes TEXT,
|
| 1615 |
-
|
| 1616 |
created_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 1617 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1618 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -1838,7 +1838,7 @@ CREATE TABLE inventory_assignments (
|
|
| 1838 |
|
| 1839 |
-- Notes (optional)
|
| 1840 |
notes TEXT,
|
| 1841 |
-
|
| 1842 |
|
| 1843 |
-- Concurrency Control (Optimistic Locking)
|
| 1844 |
version INTEGER DEFAULT 1 NOT NULL,
|
|
@@ -2001,7 +2001,7 @@ CREATE TABLE project_finance (
|
|
| 2001 |
|
| 2002 |
-- Metadata
|
| 2003 |
notes TEXT,
|
| 2004 |
-
|
| 2005 |
|
| 2006 |
-- Concurrency Control (Optimistic Locking)
|
| 2007 |
version INTEGER DEFAULT 1 NOT NULL,
|
|
@@ -2127,7 +2127,7 @@ CREATE TABLE payment_logs (
|
|
| 2127 |
|
| 2128 |
-- Additional context
|
| 2129 |
notes TEXT, -- Admin notes
|
| 2130 |
-
|
| 2131 |
|
| 2132 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 2133 |
|
|
@@ -2189,7 +2189,7 @@ CREATE TABLE user_payroll (
|
|
| 2189 |
paid_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 2190 |
|
| 2191 |
-- Metadata
|
| 2192 |
-
|
| 2193 |
|
| 2194 |
-- Concurrency Control (Optimistic Locking)
|
| 2195 |
version INTEGER DEFAULT 1 NOT NULL,
|
|
@@ -2272,7 +2272,7 @@ CREATE TABLE documents (
|
|
| 2272 |
-- Metadata
|
| 2273 |
description TEXT,
|
| 2274 |
tags JSONB DEFAULT '[]', -- Array of tags for search
|
| 2275 |
-
|
| 2276 |
|
| 2277 |
-- Access Control
|
| 2278 |
uploaded_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
|
@@ -2322,7 +2322,7 @@ CREATE TABLE user_document_links (
|
|
| 2322 |
issued_at DATE,
|
| 2323 |
expires_at DATE,
|
| 2324 |
notes TEXT,
|
| 2325 |
-
|
| 2326 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 2327 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 2328 |
|
|
@@ -2364,7 +2364,7 @@ CREATE TABLE notifications (
|
|
| 2364 |
failure_reason TEXT,
|
| 2365 |
|
| 2366 |
-- Metadata
|
| 2367 |
-
|
| 2368 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now())
|
| 2369 |
);
|
| 2370 |
|
|
@@ -2459,7 +2459,7 @@ CREATE TABLE audit_logs (
|
|
| 2459 |
longitude DOUBLE PRECISION,
|
| 2460 |
|
| 2461 |
-- Metadata
|
| 2462 |
-
|
| 2463 |
|
| 2464 |
-- Timestamp
|
| 2465 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -2550,7 +2550,7 @@ CREATE TABLE contractor_invoices (
|
|
| 2550 |
-- Metadata
|
| 2551 |
notes TEXT,
|
| 2552 |
terms_and_conditions TEXT,
|
| 2553 |
-
|
| 2554 |
created_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 2555 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 2556 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
@@ -2957,14 +2957,14 @@ ON CONFLICT (config_key) DO NOTHING;
|
|
| 2957 |
-- These are critical for performance when filtering or searching within JSONB data
|
| 2958 |
|
| 2959 |
-- clients & contractors
|
| 2960 |
-
CREATE INDEX idx_clients_metadata_gin ON clients USING GIN(
|
| 2961 |
CREATE INDEX idx_contractors_competencies_gin ON contractors USING GIN(competencies);
|
| 2962 |
-
CREATE INDEX idx_contractors_metadata_gin ON contractors USING GIN(
|
| 2963 |
|
| 2964 |
-- users
|
| 2965 |
CREATE INDEX idx_users_health_info_gin ON users USING GIN(health_info);
|
| 2966 |
CREATE INDEX idx_users_ppe_sizes_gin ON users USING GIN(ppe_sizes);
|
| 2967 |
-
CREATE INDEX idx_users_metadata_gin ON users USING GIN(
|
| 2968 |
|
| 2969 |
-- User Preferences
|
| 2970 |
CREATE INDEX idx_user_preferences_additional_settings_gin ON user_preferences USING GIN(additional_settings);
|
|
@@ -2974,39 +2974,39 @@ CREATE INDEX idx_projects_budget_gin ON projects USING GIN(budget);
|
|
| 2974 |
CREATE INDEX idx_projects_inventory_requirements_gin ON projects USING GIN(inventory_requirements);
|
| 2975 |
CREATE INDEX idx_projects_photo_requirements_gin ON projects USING GIN(photo_requirements);
|
| 2976 |
CREATE INDEX idx_projects_activation_requirements_gin ON projects USING GIN(activation_requirements);
|
| 2977 |
-
CREATE INDEX idx_projects_metadata_gin ON projects USING GIN(
|
| 2978 |
|
| 2979 |
-- Project Regions
|
| 2980 |
-
CREATE INDEX idx_project_regions_metadata_gin ON project_regions USING GIN(
|
| 2981 |
|
| 2982 |
-- timesheets
|
| 2983 |
-
CREATE INDEX idx_timesheets_metadata_gin ON timesheets USING GIN(
|
| 2984 |
|
| 2985 |
-- customers
|
| 2986 |
-
CREATE INDEX idx_customers_metadata_gin ON customers USING GIN(
|
| 2987 |
|
| 2988 |
-- Sales Orders
|
| 2989 |
-
CREATE INDEX idx_sales_orders_metadata_gin ON sales_orders USING GIN(
|
| 2990 |
|
| 2991 |
-- subscriptions
|
| 2992 |
CREATE INDEX idx_subscriptions_equipment_details_gin ON subscriptions USING GIN(equipment_details);
|
| 2993 |
CREATE INDEX idx_subscriptions_activation_details_gin ON subscriptions USING GIN(activation_details);
|
| 2994 |
-
CREATE INDEX idx_subscriptions_metadata_gin ON subscriptions USING GIN(
|
| 2995 |
|
| 2996 |
-- incidents
|
| 2997 |
-
CREATE INDEX idx_incidents_metadata_gin ON incidents USING GIN(
|
| 2998 |
|
| 2999 |
-- tickets
|
| 3000 |
-
CREATE INDEX idx_tickets_metadata_gin ON tickets USING GIN(
|
| 3001 |
|
| 3002 |
-- Ticket Status History
|
| 3003 |
-
CREATE INDEX idx_ticket_status_history_metadata_gin ON ticket_status_history USING GIN(
|
| 3004 |
|
| 3005 |
-- Ticket Expenses
|
| 3006 |
-
CREATE INDEX idx_ticket_expenses_metadata_gin ON ticket_expenses USING GIN(
|
| 3007 |
|
| 3008 |
-- tasks
|
| 3009 |
-
CREATE INDEX idx_tasks_metadata_gin ON tasks USING GIN(
|
| 3010 |
|
| 3011 |
-- Project Inventory
|
| 3012 |
CREATE INDEX idx_project_inventory_serial_numbers_gin ON project_inventory USING GIN(serial_numbers);
|
|
@@ -3016,29 +3016,29 @@ CREATE INDEX idx_inventory_distribution_serial_numbers_gin ON project_inventory_
|
|
| 3016 |
|
| 3017 |
-- Inventory Assignments
|
| 3018 |
CREATE INDEX idx_inventory_assignments_unit_contents_gin ON inventory_assignments USING GIN(unit_contents);
|
| 3019 |
-
CREATE INDEX idx_inventory_assignments_metadata_gin ON inventory_assignments USING GIN(
|
| 3020 |
|
| 3021 |
-- Project Finance
|
| 3022 |
-
CREATE INDEX idx_project_finance_metadata_gin ON project_finance USING GIN(
|
| 3023 |
|
| 3024 |
-- Payment Logs
|
| 3025 |
CREATE INDEX idx_payment_logs_request_payload_gin ON payment_logs USING GIN(request_payload);
|
| 3026 |
CREATE INDEX idx_payment_logs_response_payload_gin ON payment_logs USING GIN(response_payload);
|
| 3027 |
CREATE INDEX idx_payment_logs_device_info_gin ON payment_logs USING GIN(device_info);
|
| 3028 |
-
CREATE INDEX idx_payment_logs_metadata_gin ON payment_logs USING GIN(
|
| 3029 |
|
| 3030 |
-- documents
|
| 3031 |
-
CREATE INDEX idx_documents_metadata_gin ON documents USING GIN(
|
| 3032 |
|
| 3033 |
-- notifications
|
| 3034 |
-
CREATE INDEX idx_notifications_metadata_gin ON notifications USING GIN(
|
| 3035 |
|
| 3036 |
-- System Configuration
|
| 3037 |
CREATE INDEX idx_system_configuration_validation_rules_gin ON system_configuration USING GIN(validation_rules);
|
| 3038 |
|
| 3039 |
-- Audit Logs
|
| 3040 |
CREATE INDEX idx_audit_logs_changes_gin ON audit_logs USING GIN(changes);
|
| 3041 |
-
CREATE INDEX idx_audit_logs_metadata_gin ON audit_logs USING GIN(
|
| 3042 |
|
| 3043 |
-- External Integrations: REMOVED (over-engineering for MVP)
|
| 3044 |
|
|
@@ -3046,13 +3046,13 @@ CREATE INDEX idx_audit_logs_metadata_gin ON audit_logs USING GIN(metadata);
|
|
| 3046 |
CREATE INDEX idx_billing_plans_features_gin ON billing_plans USING GIN(features);
|
| 3047 |
|
| 3048 |
-- Organization subscriptions
|
| 3049 |
-
CREATE INDEX idx_org_subscriptions_metadata_gin ON organization_subscriptions USING GIN(
|
| 3050 |
|
| 3051 |
-- invoices
|
| 3052 |
CREATE INDEX idx_invoices_line_items_gin ON invoices USING GIN(line_items);
|
| 3053 |
|
| 3054 |
-- Usage Metrics
|
| 3055 |
-
CREATE INDEX idx_usage_metrics_metadata_gin ON usage_metrics USING GIN(
|
| 3056 |
|
| 3057 |
COMMENT ON INDEX idx_clients_metadata_gin IS 'GIN index for fast JSONB queries on clients.metadata';
|
| 3058 |
COMMENT ON INDEX idx_projects_activation_requirements_gin IS 'GIN index for querying dynamic activation requirements per project';
|
|
|
|
| 138 |
website TEXT,
|
| 139 |
is_active BOOLEAN DEFAULT TRUE,
|
| 140 |
default_sla_days INTEGER, -- Default SLA window (days) for jobs under this client
|
| 141 |
+
additional_metadata JSONB DEFAULT '{}', -- Flexible storage for additional fields
|
| 142 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 143 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 144 |
deleted_at TIMESTAMP WITH TIME ZONE NULL
|
|
|
|
| 156 |
main_phone TEXT,
|
| 157 |
is_active BOOLEAN DEFAULT TRUE,
|
| 158 |
competencies JSONB DEFAULT '{}', -- Array of specializations (e.g., ["FTTH", "Fixed Wireless", "Fiber Splicing"])
|
| 159 |
+
additional_metadata JSONB DEFAULT '{}', -- Flexible storage for additional fields
|
| 160 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 161 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 162 |
deleted_at TIMESTAMP WITH TIME ZONE NULL,
|
|
|
|
| 216 |
current_location_updated_at TIMESTAMP WITH TIME ZONE,
|
| 217 |
|
| 218 |
-- Additional metadata for future flexibility
|
| 219 |
+
additional_metadata JSONB DEFAULT '{}',
|
| 220 |
|
| 221 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 222 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 480 |
activation_requirements JSONB DEFAULT '[]',
|
| 481 |
|
| 482 |
-- Additional metadata for future flexibility
|
| 483 |
+
additional_metadata JSONB DEFAULT '{}',
|
| 484 |
|
| 485 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 486 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 531 |
|
| 532 |
-- Metadata
|
| 533 |
notes TEXT,
|
| 534 |
+
additional_metadata JSONB DEFAULT '{}',
|
| 535 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 536 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 537 |
deleted_at TIMESTAMP WITH TIME ZONE NULL
|
|
|
|
| 580 |
|
| 581 |
-- Metadata
|
| 582 |
notes TEXT,
|
| 583 |
+
additional_metadata JSONB DEFAULT '{}',
|
| 584 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 585 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 586 |
deleted_at TIMESTAMP WITH TIME ZONE NULL
|
|
|
|
| 696 |
|
| 697 |
-- Metadata
|
| 698 |
notes TEXT,
|
| 699 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional data (e.g., device info, ticket IDs worked on)
|
| 700 |
|
| 701 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 702 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 767 |
is_active BOOLEAN DEFAULT TRUE,
|
| 768 |
|
| 769 |
-- Additional metadata for future flexibility
|
| 770 |
+
additional_metadata JSONB DEFAULT '{}',
|
| 771 |
|
| 772 |
-- Timestamps
|
| 773 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 845 |
|
| 846 |
-- Metadata
|
| 847 |
notes TEXT,
|
| 848 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional metadata for future flexibility
|
| 849 |
submitted_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 850 |
submitted_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 851 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 932 |
|
| 933 |
-- Metadata
|
| 934 |
notes TEXT,
|
| 935 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional metadata for future flexibility
|
| 936 |
activated_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 937 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 938 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 987 |
cancellation_reason TEXT, -- Why incident was cancelled (if status='cancelled')
|
| 988 |
|
| 989 |
-- Metadata
|
| 990 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional metadata for future flexibility
|
| 991 |
|
| 992 |
-- Timestamps
|
| 993 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 1129 |
|
| 1130 |
-- Metadata
|
| 1131 |
notes TEXT,
|
| 1132 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional metadata for future flexibility
|
| 1133 |
|
| 1134 |
-- Concurrency Control (Optimistic Locking)
|
| 1135 |
version INTEGER DEFAULT 1 NOT NULL,
|
|
|
|
| 1350 |
|
| 1351 |
-- Metadata
|
| 1352 |
notes TEXT,
|
| 1353 |
+
additional_metadata JSONB DEFAULT '{}', -- Device info, IP address, etc.
|
| 1354 |
|
| 1355 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1356 |
deleted_at TIMESTAMP WITH TIME ZONE NULL,
|
|
|
|
| 1401 |
edited_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 1402 |
|
| 1403 |
-- Metadata
|
| 1404 |
+
additional_metadata JSONB DEFAULT '{}',
|
| 1405 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1406 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1407 |
deleted_at TIMESTAMP WITH TIME ZONE NULL,
|
|
|
|
| 1455 |
followup_notes TEXT,
|
| 1456 |
|
| 1457 |
-- Metadata
|
| 1458 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional data (call duration, recording URL, etc.)
|
| 1459 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1460 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1461 |
|
|
|
|
| 1542 |
|
| 1543 |
-- Metadata
|
| 1544 |
notes TEXT,
|
| 1545 |
+
additional_metadata JSONB DEFAULT '{}', -- Can store split details: {"split_with": ["user-id-1", "user-id-2"], "split_amount": 500}
|
| 1546 |
|
| 1547 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1548 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 1612 |
|
| 1613 |
-- Metadata
|
| 1614 |
notes TEXT,
|
| 1615 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional metadata for future flexibility
|
| 1616 |
created_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 1617 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 1618 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 1838 |
|
| 1839 |
-- Notes (optional)
|
| 1840 |
notes TEXT,
|
| 1841 |
+
additional_metadata JSONB DEFAULT '{}',
|
| 1842 |
|
| 1843 |
-- Concurrency Control (Optimistic Locking)
|
| 1844 |
version INTEGER DEFAULT 1 NOT NULL,
|
|
|
|
| 2001 |
|
| 2002 |
-- Metadata
|
| 2003 |
notes TEXT,
|
| 2004 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional data (e.g., split payments, installment info)
|
| 2005 |
|
| 2006 |
-- Concurrency Control (Optimistic Locking)
|
| 2007 |
version INTEGER DEFAULT 1 NOT NULL,
|
|
|
|
| 2127 |
|
| 2128 |
-- Additional context
|
| 2129 |
notes TEXT, -- Admin notes
|
| 2130 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional data (webhook headers, etc.)
|
| 2131 |
|
| 2132 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 2133 |
|
|
|
|
| 2189 |
paid_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 2190 |
|
| 2191 |
-- Metadata
|
| 2192 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional data (e.g., ticket IDs, bonus reasons)
|
| 2193 |
|
| 2194 |
-- Concurrency Control (Optimistic Locking)
|
| 2195 |
version INTEGER DEFAULT 1 NOT NULL,
|
|
|
|
| 2272 |
-- Metadata
|
| 2273 |
description TEXT,
|
| 2274 |
tags JSONB DEFAULT '[]', -- Array of tags for search
|
| 2275 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional metadata (e.g., GPS coordinates for photos, OCR text)
|
| 2276 |
|
| 2277 |
-- Access Control
|
| 2278 |
uploaded_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
|
|
|
| 2322 |
issued_at DATE,
|
| 2323 |
expires_at DATE,
|
| 2324 |
notes TEXT,
|
| 2325 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional metadata (e.g., GPS coordinates for photos, OCR text)
|
| 2326 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 2327 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 2328 |
|
|
|
|
| 2364 |
failure_reason TEXT,
|
| 2365 |
|
| 2366 |
-- Metadata
|
| 2367 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional data (e.g., action buttons, deep links)
|
| 2368 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now())
|
| 2369 |
);
|
| 2370 |
|
|
|
|
| 2459 |
longitude DOUBLE PRECISION,
|
| 2460 |
|
| 2461 |
-- Metadata
|
| 2462 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional context
|
| 2463 |
|
| 2464 |
-- Timestamp
|
| 2465 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 2550 |
-- Metadata
|
| 2551 |
notes TEXT,
|
| 2552 |
terms_and_conditions TEXT,
|
| 2553 |
+
additional_metadata JSONB DEFAULT '{}', -- Additional metadata for future flexibility
|
| 2554 |
created_by_user_id UUID REFERENCES users (id) ON DELETE SET NULL,
|
| 2555 |
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
| 2556 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
|
|
|
|
| 2957 |
-- These are critical for performance when filtering or searching within JSONB data
|
| 2958 |
|
| 2959 |
-- clients & contractors
|
| 2960 |
+
CREATE INDEX idx_clients_metadata_gin ON clients USING GIN(additional_metadata);
|
| 2961 |
CREATE INDEX idx_contractors_competencies_gin ON contractors USING GIN(competencies);
|
| 2962 |
+
CREATE INDEX idx_contractors_metadata_gin ON contractors USING GIN(additional_metadata);
|
| 2963 |
|
| 2964 |
-- users
|
| 2965 |
CREATE INDEX idx_users_health_info_gin ON users USING GIN(health_info);
|
| 2966 |
CREATE INDEX idx_users_ppe_sizes_gin ON users USING GIN(ppe_sizes);
|
| 2967 |
+
CREATE INDEX idx_users_metadata_gin ON users USING GIN(additional_metadata);
|
| 2968 |
|
| 2969 |
-- User Preferences
|
| 2970 |
CREATE INDEX idx_user_preferences_additional_settings_gin ON user_preferences USING GIN(additional_settings);
|
|
|
|
| 2974 |
CREATE INDEX idx_projects_inventory_requirements_gin ON projects USING GIN(inventory_requirements);
|
| 2975 |
CREATE INDEX idx_projects_photo_requirements_gin ON projects USING GIN(photo_requirements);
|
| 2976 |
CREATE INDEX idx_projects_activation_requirements_gin ON projects USING GIN(activation_requirements);
|
| 2977 |
+
CREATE INDEX idx_projects_metadata_gin ON projects USING GIN(additional_metadata);
|
| 2978 |
|
| 2979 |
-- Project Regions
|
| 2980 |
+
CREATE INDEX idx_project_regions_metadata_gin ON project_regions USING GIN(additional_metadata);
|
| 2981 |
|
| 2982 |
-- timesheets
|
| 2983 |
+
CREATE INDEX idx_timesheets_metadata_gin ON timesheets USING GIN(additional_metadata);
|
| 2984 |
|
| 2985 |
-- customers
|
| 2986 |
+
CREATE INDEX idx_customers_metadata_gin ON customers USING GIN(additional_metadata);
|
| 2987 |
|
| 2988 |
-- Sales Orders
|
| 2989 |
+
CREATE INDEX idx_sales_orders_metadata_gin ON sales_orders USING GIN(additional_metadata);
|
| 2990 |
|
| 2991 |
-- subscriptions
|
| 2992 |
CREATE INDEX idx_subscriptions_equipment_details_gin ON subscriptions USING GIN(equipment_details);
|
| 2993 |
CREATE INDEX idx_subscriptions_activation_details_gin ON subscriptions USING GIN(activation_details);
|
| 2994 |
+
CREATE INDEX idx_subscriptions_metadata_gin ON subscriptions USING GIN(additional_metadata);
|
| 2995 |
|
| 2996 |
-- incidents
|
| 2997 |
+
CREATE INDEX idx_incidents_metadata_gin ON incidents USING GIN(additional_metadata);
|
| 2998 |
|
| 2999 |
-- tickets
|
| 3000 |
+
CREATE INDEX idx_tickets_metadata_gin ON tickets USING GIN(additional_metadata);
|
| 3001 |
|
| 3002 |
-- Ticket Status History
|
| 3003 |
+
CREATE INDEX idx_ticket_status_history_metadata_gin ON ticket_status_history USING GIN(additional_metadata);
|
| 3004 |
|
| 3005 |
-- Ticket Expenses
|
| 3006 |
+
CREATE INDEX idx_ticket_expenses_metadata_gin ON ticket_expenses USING GIN(additional_metadata);
|
| 3007 |
|
| 3008 |
-- tasks
|
| 3009 |
+
CREATE INDEX idx_tasks_metadata_gin ON tasks USING GIN(additional_metadata);
|
| 3010 |
|
| 3011 |
-- Project Inventory
|
| 3012 |
CREATE INDEX idx_project_inventory_serial_numbers_gin ON project_inventory USING GIN(serial_numbers);
|
|
|
|
| 3016 |
|
| 3017 |
-- Inventory Assignments
|
| 3018 |
CREATE INDEX idx_inventory_assignments_unit_contents_gin ON inventory_assignments USING GIN(unit_contents);
|
| 3019 |
+
CREATE INDEX idx_inventory_assignments_metadata_gin ON inventory_assignments USING GIN(additional_metadata);
|
| 3020 |
|
| 3021 |
-- Project Finance
|
| 3022 |
+
CREATE INDEX idx_project_finance_metadata_gin ON project_finance USING GIN(additional_metadata);
|
| 3023 |
|
| 3024 |
-- Payment Logs
|
| 3025 |
CREATE INDEX idx_payment_logs_request_payload_gin ON payment_logs USING GIN(request_payload);
|
| 3026 |
CREATE INDEX idx_payment_logs_response_payload_gin ON payment_logs USING GIN(response_payload);
|
| 3027 |
CREATE INDEX idx_payment_logs_device_info_gin ON payment_logs USING GIN(device_info);
|
| 3028 |
+
CREATE INDEX idx_payment_logs_metadata_gin ON payment_logs USING GIN(additional_metadata);
|
| 3029 |
|
| 3030 |
-- documents
|
| 3031 |
+
CREATE INDEX idx_documents_metadata_gin ON documents USING GIN(additional_metadata);
|
| 3032 |
|
| 3033 |
-- notifications
|
| 3034 |
+
CREATE INDEX idx_notifications_metadata_gin ON notifications USING GIN(additional_metadata);
|
| 3035 |
|
| 3036 |
-- System Configuration
|
| 3037 |
CREATE INDEX idx_system_configuration_validation_rules_gin ON system_configuration USING GIN(validation_rules);
|
| 3038 |
|
| 3039 |
-- Audit Logs
|
| 3040 |
CREATE INDEX idx_audit_logs_changes_gin ON audit_logs USING GIN(changes);
|
| 3041 |
+
CREATE INDEX idx_audit_logs_metadata_gin ON audit_logs USING GIN(additional_metadata);
|
| 3042 |
|
| 3043 |
-- External Integrations: REMOVED (over-engineering for MVP)
|
| 3044 |
|
|
|
|
| 3046 |
CREATE INDEX idx_billing_plans_features_gin ON billing_plans USING GIN(features);
|
| 3047 |
|
| 3048 |
-- Organization subscriptions
|
| 3049 |
+
CREATE INDEX idx_org_subscriptions_metadata_gin ON organization_subscriptions USING GIN(additional_metadata);
|
| 3050 |
|
| 3051 |
-- invoices
|
| 3052 |
CREATE INDEX idx_invoices_line_items_gin ON invoices USING GIN(line_items);
|
| 3053 |
|
| 3054 |
-- Usage Metrics
|
| 3055 |
+
CREATE INDEX idx_usage_metrics_metadata_gin ON usage_metrics USING GIN(additional_metadata);
|
| 3056 |
|
| 3057 |
COMMENT ON INDEX idx_clients_metadata_gin IS 'GIN index for fast JSONB queries on clients.metadata';
|
| 3058 |
COMMENT ON INDEX idx_projects_activation_requirements_gin IS 'GIN index for querying dynamic activation requirements per project';
|
migrations/001_rename_metadata_to_additional_metadata.sql
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- ============================================
|
| 2 |
+
-- Migration: Rename 'metadata' to 'additional_metadata'
|
| 3 |
+
-- Date: 2025-11-15
|
| 4 |
+
-- Reason: 'metadata' is a reserved word in SQLAlchemy ORM
|
| 5 |
+
-- ============================================
|
| 6 |
+
-- This migration renames the 'metadata' JSONB column to 'additional_metadata'
|
| 7 |
+
-- across all tables to avoid conflicts with SQLAlchemy's internal metadata attribute.
|
| 8 |
+
--
|
| 9 |
+
-- INSTRUCTIONS:
|
| 10 |
+
-- 1. Review this migration carefully
|
| 11 |
+
-- 2. Run in Supabase SQL Editor
|
| 12 |
+
-- 3. Verify all tables updated successfully
|
| 13 |
+
-- 4. Update application code to use 'additional_metadata'
|
| 14 |
+
--
|
| 15 |
+
-- ROLLBACK:
|
| 16 |
+
-- To rollback, replace 'additional_metadata' with 'metadata' in all ALTER statements below
|
| 17 |
+
-- ============================================
|
| 18 |
+
|
| 19 |
+
-- Start transaction
|
| 20 |
+
BEGIN;
|
| 21 |
+
|
| 22 |
+
-- 1. ORGANIZATIONS & USERS
|
| 23 |
+
ALTER TABLE clients RENAME COLUMN metadata TO additional_metadata;
|
| 24 |
+
ALTER TABLE contractors RENAME COLUMN metadata TO additional_metadata;
|
| 25 |
+
ALTER TABLE users RENAME COLUMN metadata TO additional_metadata;
|
| 26 |
+
ALTER TABLE user_financial_accounts RENAME COLUMN metadata TO additional_metadata;
|
| 27 |
+
ALTER TABLE user_asset_assignments RENAME COLUMN metadata TO additional_metadata;
|
| 28 |
+
ALTER TABLE user_document_links RENAME COLUMN metadata TO additional_metadata;
|
| 29 |
+
|
| 30 |
+
-- 2. PROJECTS & TEAMS
|
| 31 |
+
ALTER TABLE projects RENAME COLUMN metadata TO additional_metadata;
|
| 32 |
+
ALTER TABLE project_regions RENAME COLUMN metadata TO additional_metadata;
|
| 33 |
+
ALTER TABLE project_subcontractors RENAME COLUMN metadata TO additional_metadata;
|
| 34 |
+
ALTER TABLE timesheets RENAME COLUMN metadata TO additional_metadata;
|
| 35 |
+
|
| 36 |
+
-- 3. CUSTOMERS & SALES
|
| 37 |
+
ALTER TABLE customers RENAME COLUMN metadata TO additional_metadata;
|
| 38 |
+
ALTER TABLE sales_orders RENAME COLUMN metadata TO additional_metadata;
|
| 39 |
+
ALTER TABLE subscriptions RENAME COLUMN metadata TO additional_metadata;
|
| 40 |
+
ALTER TABLE incidents RENAME COLUMN metadata TO additional_metadata;
|
| 41 |
+
|
| 42 |
+
-- 4. TICKETS & WORK ORDERS
|
| 43 |
+
ALTER TABLE tickets RENAME COLUMN metadata TO additional_metadata;
|
| 44 |
+
ALTER TABLE ticket_status_history RENAME COLUMN metadata TO additional_metadata;
|
| 45 |
+
ALTER TABLE ticket_expenses RENAME COLUMN metadata TO additional_metadata;
|
| 46 |
+
ALTER TABLE ticket_comments RENAME COLUMN metadata TO additional_metadata;
|
| 47 |
+
ALTER TABLE customer_communications RENAME COLUMN metadata TO additional_metadata;
|
| 48 |
+
|
| 49 |
+
-- 5. TASKS
|
| 50 |
+
ALTER TABLE tasks RENAME COLUMN metadata TO additional_metadata;
|
| 51 |
+
|
| 52 |
+
-- 6. INVENTORY
|
| 53 |
+
ALTER TABLE inventory_assignments RENAME COLUMN metadata TO additional_metadata;
|
| 54 |
+
|
| 55 |
+
-- 7. FINANCIAL MANAGEMENT
|
| 56 |
+
ALTER TABLE project_finance RENAME COLUMN metadata TO additional_metadata;
|
| 57 |
+
ALTER TABLE payment_logs RENAME COLUMN metadata TO additional_metadata;
|
| 58 |
+
ALTER TABLE user_payroll RENAME COLUMN metadata TO additional_metadata;
|
| 59 |
+
|
| 60 |
+
-- 8. DOCUMENTS & NOTIFICATIONS
|
| 61 |
+
ALTER TABLE documents RENAME COLUMN metadata TO additional_metadata;
|
| 62 |
+
ALTER TABLE notifications RENAME COLUMN metadata TO additional_metadata;
|
| 63 |
+
|
| 64 |
+
-- 9. SYSTEM & AUDIT
|
| 65 |
+
ALTER TABLE audit_logs RENAME COLUMN metadata TO additional_metadata;
|
| 66 |
+
|
| 67 |
+
-- 10. PLATFORM BILLING
|
| 68 |
+
ALTER TABLE organization_subscriptions RENAME COLUMN metadata TO additional_metadata;
|
| 69 |
+
ALTER TABLE usage_metrics RENAME COLUMN metadata TO additional_metadata;
|
| 70 |
+
|
| 71 |
+
-- Commit transaction
|
| 72 |
+
COMMIT;
|
| 73 |
+
|
| 74 |
+
-- ============================================
|
| 75 |
+
-- Verification Query
|
| 76 |
+
-- ============================================
|
| 77 |
+
-- Run this to verify all columns were renamed:
|
| 78 |
+
/*
|
| 79 |
+
SELECT
|
| 80 |
+
table_name,
|
| 81 |
+
column_name,
|
| 82 |
+
data_type
|
| 83 |
+
FROM information_schema.columns
|
| 84 |
+
WHERE table_schema = 'public'
|
| 85 |
+
AND column_name IN ('metadata', 'additional_metadata')
|
| 86 |
+
ORDER BY
|
| 87 |
+
CASE WHEN column_name = 'metadata' THEN 0 ELSE 1 END,
|
| 88 |
+
table_name;
|
| 89 |
+
|
| 90 |
+
-- Expected: 0 rows with 'metadata', 32 rows with 'additional_metadata'
|
| 91 |
+
*/
|
| 92 |
+
|
| 93 |
+
-- ============================================
|
| 94 |
+
-- Migration Complete
|
| 95 |
+
-- ============================================
|
| 96 |
+
-- Next steps:
|
| 97 |
+
-- 1. Update schema.sql documentation
|
| 98 |
+
-- 2. Update SQLAlchemy models to use 'additional_metadata'
|
| 99 |
+
-- 3. Test application startup
|
| 100 |
+
-- ============================================
|
migrations/001_rename_metadata_to_additional_metadata_safe.sql
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- ============================================
|
| 2 |
+
-- Migration: Rename 'metadata' to 'additional_metadata' (SAFE VERSION)
|
| 3 |
+
-- Date: 2025-11-15
|
| 4 |
+
-- Reason: 'metadata' is a reserved word in SQLAlchemy ORM
|
| 5 |
+
-- ============================================
|
| 6 |
+
-- This migration safely renames the 'metadata' JSONB column to 'additional_metadata'
|
| 7 |
+
-- It only attempts to rename columns that actually exist in the database.
|
| 8 |
+
--
|
| 9 |
+
-- INSTRUCTIONS:
|
| 10 |
+
-- 1. Run this in Supabase SQL Editor
|
| 11 |
+
-- 2. Check the output to see which tables were updated
|
| 12 |
+
-- 3. Verify with the verification query at the end
|
| 13 |
+
-- ============================================
|
| 14 |
+
|
| 15 |
+
-- Start transaction
|
| 16 |
+
BEGIN;
|
| 17 |
+
|
| 18 |
+
-- Use DO block to safely rename columns only if they exist
|
| 19 |
+
DO $$
|
| 20 |
+
DECLARE
|
| 21 |
+
table_record RECORD;
|
| 22 |
+
tables_to_check TEXT[] := ARRAY[
|
| 23 |
+
'clients',
|
| 24 |
+
'contractors',
|
| 25 |
+
'users',
|
| 26 |
+
'user_financial_accounts',
|
| 27 |
+
'user_asset_assignments',
|
| 28 |
+
'user_document_links',
|
| 29 |
+
'projects',
|
| 30 |
+
'project_regions',
|
| 31 |
+
'project_subcontractors',
|
| 32 |
+
'timesheets',
|
| 33 |
+
'customers',
|
| 34 |
+
'sales_orders',
|
| 35 |
+
'subscriptions',
|
| 36 |
+
'incidents',
|
| 37 |
+
'tickets',
|
| 38 |
+
'ticket_status_history',
|
| 39 |
+
'ticket_expenses',
|
| 40 |
+
'ticket_comments',
|
| 41 |
+
'customer_communications',
|
| 42 |
+
'tasks',
|
| 43 |
+
'inventory_assignments',
|
| 44 |
+
'project_finance',
|
| 45 |
+
'payment_logs',
|
| 46 |
+
'user_payroll',
|
| 47 |
+
'documents',
|
| 48 |
+
'notifications',
|
| 49 |
+
'audit_logs',
|
| 50 |
+
'organization_subscriptions',
|
| 51 |
+
'usage_metrics'
|
| 52 |
+
];
|
| 53 |
+
current_table TEXT;
|
| 54 |
+
column_exists BOOLEAN;
|
| 55 |
+
BEGIN
|
| 56 |
+
FOREACH current_table IN ARRAY tables_to_check
|
| 57 |
+
LOOP
|
| 58 |
+
-- Check if table exists and has metadata column
|
| 59 |
+
SELECT EXISTS (
|
| 60 |
+
SELECT 1
|
| 61 |
+
FROM information_schema.columns
|
| 62 |
+
WHERE table_schema = 'public'
|
| 63 |
+
AND table_name = current_table
|
| 64 |
+
AND column_name = 'metadata'
|
| 65 |
+
) INTO column_exists;
|
| 66 |
+
|
| 67 |
+
IF column_exists THEN
|
| 68 |
+
-- Rename the column
|
| 69 |
+
EXECUTE format('ALTER TABLE %I RENAME COLUMN metadata TO additional_metadata', current_table);
|
| 70 |
+
RAISE NOTICE 'Renamed metadata column in table: %', current_table;
|
| 71 |
+
ELSE
|
| 72 |
+
RAISE NOTICE 'Skipped table (no metadata column): %', current_table;
|
| 73 |
+
END IF;
|
| 74 |
+
END LOOP;
|
| 75 |
+
END $$;
|
| 76 |
+
|
| 77 |
+
-- Commit transaction
|
| 78 |
+
COMMIT;
|
| 79 |
+
|
| 80 |
+
-- ============================================
|
| 81 |
+
-- Verification Query
|
| 82 |
+
-- ============================================
|
| 83 |
+
-- Run this to see which tables have metadata vs additional_metadata:
|
| 84 |
+
SELECT
|
| 85 |
+
table_name,
|
| 86 |
+
column_name,
|
| 87 |
+
data_type
|
| 88 |
+
FROM information_schema.columns
|
| 89 |
+
WHERE table_schema = 'public'
|
| 90 |
+
AND column_name IN ('metadata', 'additional_metadata')
|
| 91 |
+
ORDER BY
|
| 92 |
+
CASE WHEN column_name = 'metadata' THEN 0 ELSE 1 END,
|
| 93 |
+
table_name;
|
| 94 |
+
|
| 95 |
+
-- Expected: 0 rows with 'metadata', N rows with 'additional_metadata' (where N = number of tables that had metadata)
|
migrations/002_fix_contractor_invoices_metadata.sql
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- ============================================
|
| 2 |
+
-- Migration: Fix contractor_invoices metadata column
|
| 3 |
+
-- Date: 2025-11-15
|
| 4 |
+
-- Reason: This table was missed in the first migration
|
| 5 |
+
-- ============================================
|
| 6 |
+
|
| 7 |
+
BEGIN;
|
| 8 |
+
|
| 9 |
+
-- Rename metadata to additional_metadata in contractor_invoices
|
| 10 |
+
ALTER TABLE contractor_invoices RENAME COLUMN metadata TO additional_metadata;
|
| 11 |
+
|
| 12 |
+
COMMIT;
|
| 13 |
+
|
| 14 |
+
-- Verification: Should return 0 rows with 'metadata'
|
| 15 |
+
SELECT
|
| 16 |
+
table_name,
|
| 17 |
+
column_name,
|
| 18 |
+
data_type
|
| 19 |
+
FROM information_schema.columns
|
| 20 |
+
WHERE table_schema = 'public'
|
| 21 |
+
AND column_name = 'metadata';
|
src/app/models/user.py
CHANGED
|
@@ -69,8 +69,8 @@ class User(BaseModel):
|
|
| 69 |
current_longitude = Column(String(50), nullable=True) # DOUBLE PRECISION in DB
|
| 70 |
current_location_updated_at = Column(DateTime(timezone=True), nullable=True)
|
| 71 |
|
| 72 |
-
# Metadata (JSONB in schema)
|
| 73 |
-
|
| 74 |
|
| 75 |
def __repr__(self):
|
| 76 |
return f"<User(email='{self.email}', name='{self.name}')>"
|
|
|
|
| 69 |
current_longitude = Column(String(50), nullable=True) # DOUBLE PRECISION in DB
|
| 70 |
current_location_updated_at = Column(DateTime(timezone=True), nullable=True)
|
| 71 |
|
| 72 |
+
# Additional Metadata (JSONB in schema)
|
| 73 |
+
additional_metadata = Column(JSONB, default={})
|
| 74 |
|
| 75 |
def __repr__(self):
|
| 76 |
return f"<User(email='{self.email}', name='{self.name}')>"
|