File size: 7,445 Bytes
1313d86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
-- ==========================================
-- SpendWise Full Database Setup Script
-- Paste this into your Supabase SQL Editor
-- ==========================================

-- 1. Enable Extensions
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- 2. Users Profile Table
CREATE TABLE IF NOT EXISTS public.profiles (
  id            UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  email         TEXT UNIQUE NOT NULL,
  display_name  TEXT,
  currency      TEXT DEFAULT 'INR',
  timezone      TEXT DEFAULT 'Asia/Kolkata',
  created_at    TIMESTAMPTZ DEFAULT NOW(),
  updated_at    TIMESTAMPTZ DEFAULT NOW()
);

ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_own_profile" ON public.profiles
  FOR ALL USING (auth.uid() = id);

-- 3. Categories Table
CREATE TABLE IF NOT EXISTS public.categories (
  id          UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id     UUID REFERENCES public.profiles(id) ON DELETE CASCADE,
  name        TEXT NOT NULL,
  icon        TEXT,
  color       TEXT,
  is_system   BOOLEAN DEFAULT FALSE,
  parent_id   UUID REFERENCES public.categories(id),
  created_at  TIMESTAMPTZ DEFAULT NOW()
);

ALTER TABLE public.categories ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_own_categories" ON public.categories
  FOR ALL USING (auth.uid() = user_id OR is_system = TRUE);

INSERT INTO public.categories (name, icon, color, is_system, user_id) VALUES
  ('Food & Dining',        '🍽️',  '#FF6B6B', TRUE, NULL),
  ('Transport',            'πŸš—',  '#4ECDC4', TRUE, NULL),
  ('Shopping',             'πŸ›οΈ',  '#45B7D1', TRUE, NULL),
  ('Entertainment',        '🎬',  '#96CEB4', TRUE, NULL),
  ('Utilities',            '⚑',  '#FFEAA7', TRUE, NULL),
  ('Health & Medical',     'πŸ₯',  '#DDA0DD', TRUE, NULL),
  ('Education',            'πŸ“š',  '#98D8C8', TRUE, NULL),
  ('Travel',               '✈️',  '#F7DC6F', TRUE, NULL),
  ('Financial Services',   'πŸ’³',  '#82E0AA', TRUE, NULL),
  ('Groceries',            'πŸ›’',  '#F8C471', TRUE, NULL),
  ('Peer Transfer',        'πŸ‘€',  '#AED6F1', TRUE, NULL),
  ('Others',               'πŸ“¦',  '#D5DBDB', TRUE, NULL)
ON CONFLICT DO NOTHING;

-- 4. Transactions Table
CREATE TABLE IF NOT EXISTS public.transactions (
  id                    UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id               UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
  raw_description       TEXT NOT NULL,
  amount                NUMERIC(14, 2) NOT NULL,
  direction             TEXT CHECK (direction IN ('debit', 'credit')) NOT NULL,
  balance               NUMERIC(14, 2),
  transaction_date      TIMESTAMPTZ NOT NULL,
  payment_method        TEXT CHECK (payment_method IN ('UPI', 'IMPS', 'NEFT', 'ATM', 'OTHER')),
  cleaned_description   TEXT,
  merchant_name         TEXT,
  is_low_descriptiveness BOOLEAN DEFAULT FALSE,
  category_id           UUID REFERENCES public.categories(id),
  category_source       TEXT CHECK (category_source IN ('semantic','behavioural','fused','zero_shot','manual','pending')),
  confidence_score      NUMERIC(4, 3),
  gating_alpha          NUMERIC(4, 3),
  is_recurring          BOOLEAN DEFAULT FALSE,
  recurrence_strength   NUMERIC(4, 3),
  needs_review          BOOLEAN DEFAULT FALSE,
  user_corrected        BOOLEAN DEFAULT FALSE,
  user_category_id      UUID REFERENCES public.categories(id),
  processing_status     TEXT DEFAULT 'pending' CHECK (
    processing_status IN ('pending', 'processing', 'completed', 'failed')
  ),
  processed_at          TIMESTAMPTZ,
  created_at            TIMESTAMPTZ DEFAULT NOW(),
  updated_at            TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_transactions_user_date ON public.transactions(user_id, transaction_date DESC);
CREATE INDEX idx_transactions_category ON public.transactions(user_id, category_id);
CREATE INDEX idx_transactions_status ON public.transactions(user_id, processing_status);
CREATE INDEX idx_transactions_merchant ON public.transactions(user_id, merchant_name);

ALTER TABLE public.transactions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_own_transactions" ON public.transactions
  FOR ALL USING (auth.uid() = user_id);

-- 5. Embeddings Table
CREATE TABLE IF NOT EXISTS public.transaction_embeddings (
  id              UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  transaction_id  UUID UNIQUE NOT NULL REFERENCES public.transactions(id) ON DELETE CASCADE,
  user_id         UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
  embedding       vector(768),
  created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX ON public.transaction_embeddings
  USING ivfflat (embedding vector_cosine_ops)
  WITH (lists = 100);

ALTER TABLE public.transaction_embeddings ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_own_embeddings" ON public.transaction_embeddings
  FOR ALL USING (auth.uid() = user_id);

-- 6. Cluster State Table
CREATE TABLE IF NOT EXISTS public.user_clusters (
  id              UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id         UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
  cluster_id      INTEGER NOT NULL,
  category_id     UUID REFERENCES public.categories(id),
  label_name      TEXT,
  transaction_count INTEGER DEFAULT 0,
  last_clustered  TIMESTAMPTZ DEFAULT NOW(),
  created_at      TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE (user_id, cluster_id)
);

ALTER TABLE public.user_clusters ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_own_clusters" ON public.user_clusters
  FOR ALL USING (auth.uid() = user_id);

-- 7. Budgets Table
CREATE TABLE IF NOT EXISTS public.budgets (
  id            UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id       UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
  category_id   UUID NOT NULL REFERENCES public.categories(id),
  amount        NUMERIC(14, 2) NOT NULL,
  period        TEXT CHECK (period IN ('monthly', 'weekly', 'annual')) DEFAULT 'monthly',
  start_date    DATE NOT NULL,
  end_date      DATE,
  created_at    TIMESTAMPTZ DEFAULT NOW(),
  updated_at    TIMESTAMPTZ DEFAULT NOW()
);

ALTER TABLE public.budgets ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_own_budgets" ON public.budgets
  FOR ALL USING (auth.uid() = user_id);

-- 8. Gating Model Weights Table
CREATE TABLE IF NOT EXISTS public.gating_model_state (
  id              UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id         UUID UNIQUE NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
  W1              JSONB,
  b1              JSONB,
  W2              JSONB,
  b2              FLOAT,
  training_samples INTEGER DEFAULT 0,
  val_mse         FLOAT,
  trained_at      TIMESTAMPTZ DEFAULT NOW()
);

ALTER TABLE public.gating_model_state ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_own_model" ON public.gating_model_state
  FOR ALL USING (auth.uid() = user_id);

-- 9. Triggers
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_transactions_updated_at
  BEFORE UPDATE ON public.transactions
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_budgets_updated_at
  BEFORE UPDATE ON public.budgets
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

-- Note: Run this last manually or ensure publication exists
-- ALTER PUBLICATION supabase_realtime ADD TABLE public.transactions;