-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsupabase_schema.sql
More file actions
276 lines (232 loc) · 10.5 KB
/
supabase_schema.sql
File metadata and controls
276 lines (232 loc) · 10.5 KB
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
-- Enable UUID extension
create extension if not exists "uuid-ossp";
-- 1. PROFILES (Extends auth.users)
DO $$ BEGIN
CREATE TYPE user_role AS ENUM ('admin', 'controller', 'cfo', 'cashier', 'requester');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
create table if not exists public.profiles (
id uuid references auth.users on delete cascade not null primary key,
full_name text,
role user_role default 'requester',
avatar_url text,
updated_at timestamptz
);
-- 2. PROJECTS
create table if not exists public.projects (
id uuid default uuid_generate_v4() primary key,
name text not null,
code text not null unique,
description text,
active boolean default true,
odoo_id integer unique,
created_at timestamptz default now()
);
-- 3. ANALYTICAL ACCOUNTS
create table if not exists public.analytical_accounts (
id uuid default uuid_generate_v4() primary key,
project_id uuid references public.projects(id) on delete cascade not null,
name text not null,
code text not null,
created_at timestamptz default now(),
odoo_id integer unique,
unique(project_id, code)
);
-- 4. CASH REQUESTS
DO $$ BEGIN
CREATE TYPE request_status AS ENUM ('pending_controller', 'pending_cfo', 'approved', 'rejected', 'disbursed');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
create table if not exists public.cash_requests (
id uuid default uuid_generate_v4() primary key,
requester_id uuid references public.profiles(id) not null,
amount numeric not null check (amount > 0),
description text not null,
analytical_account_id uuid references public.analytical_accounts(id) not null,
status request_status default 'pending_controller',
controller_approval_at timestamptz,
cfo_approval_at timestamptz,
rejection_reason text,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
-- 5. CASH TRANSACTIONS
DO $$ BEGIN
CREATE TYPE transaction_type AS ENUM ('inflow', 'outflow');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
create table if not exists public.cash_transactions (
id uuid default uuid_generate_v4() primary key,
type transaction_type not null,
amount numeric not null check (amount > 0),
description text not null,
date timestamptz default now(),
created_by uuid references public.profiles(id) not null,
analytical_account_id uuid references public.analytical_accounts(id), -- Nullable for inflows
request_id uuid references public.cash_requests(id), -- Nullable for inflows or direct outflows (if allowed)
proof_document_url text,
proof_submitted_at timestamptz,
is_validated boolean default false,
created_at timestamptz default now()
);
-- 6. DAILY CLOSURES
DO $$ BEGIN
CREATE TYPE closure_status AS ENUM ('open', 'closed');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
create table if not exists public.daily_closures (
id uuid default uuid_generate_v4() primary key,
date date not null unique,
opening_balance numeric not null,
closing_balance numeric,
closed_by uuid references public.profiles(id),
closed_at timestamptz,
status closure_status default 'open',
created_at timestamptz default now()
);
-- 7. APP SETTINGS
create table if not exists public.app_settings (
key text primary key,
value jsonb not null
);
-- RLS POLICIES (Basic Setup - Needs refinement based on strict security requirements)
alter table public.profiles enable row level security;
alter table public.projects enable row level security;
alter table public.analytical_accounts enable row level security;
alter table public.cash_requests enable row level security;
alter table public.cash_transactions enable row level security;
alter table public.daily_closures enable row level security;
alter table public.app_settings enable row level security;
-- Helper to safely drop policies
DO $$
BEGIN
-- Profiles
DROP POLICY IF EXISTS "Public profiles are viewable by everyone." ON public.profiles;
DROP POLICY IF EXISTS "Users can update own profile." ON public.profiles;
-- Projects/Accounts
DROP POLICY IF EXISTS "Projects viewable by authenticated" ON public.projects;
DROP POLICY IF EXISTS "Analytical Accounts viewable by authenticated" ON public.analytical_accounts;
-- Requests
DROP POLICY IF EXISTS "Requester see own requests" ON public.cash_requests;
DROP POLICY IF EXISTS "Staff see all requests" ON public.cash_requests;
DROP POLICY IF EXISTS "Requester can insert requests" ON public.cash_requests;
-- Transactions
DROP POLICY IF EXISTS "Transactions viewable by authenticated" ON public.cash_transactions;
-- Admin Policies - Projects
DROP POLICY IF EXISTS "Admin can insert projects" ON public.projects;
DROP POLICY IF EXISTS "Admin can update projects" ON public.projects;
DROP POLICY IF EXISTS "Admin can delete projects" ON public.projects;
-- Admin Policies - Analytical Accounts
DROP POLICY IF EXISTS "Admin can insert analytical accounts" ON public.analytical_accounts;
DROP POLICY IF EXISTS "Admin can update analytical accounts" ON public.analytical_accounts;
DROP POLICY IF EXISTS "Admin can delete analytical accounts" ON public.analytical_accounts;
-- Admin Policies - App Settings
DROP POLICY IF EXISTS "Admin can view app settings" ON public.app_settings;
DROP POLICY IF EXISTS "Admin can update app settings" ON public.app_settings;
DROP POLICY IF EXISTS "Admin can insert app settings" ON public.app_settings;
-- Admin Policies - User Roles
DROP POLICY IF EXISTS "Admin can update user roles" ON public.profiles;
END $$;
-- Profiles: Everyone can read profiles. Users can update their own.
create policy "Public profiles are viewable by everyone." on public.profiles for select using (true);
create policy "Users can update own profile." on public.profiles for update using (auth.uid() = id);
-- Projects/Accounts: Readable by authenticated users. Only Admin can manage.
create policy "Projects viewable by authenticated" on public.projects for select using (auth.role() = 'authenticated');
create policy "Analytical Accounts viewable by authenticated" on public.analytical_accounts for select using (auth.role() = 'authenticated');
-- Requests:
-- Requester can see own.
-- Controller/CFO/Cashier/Admin can see all.
create policy "Requester see own requests" on public.cash_requests for select using (auth.uid() = requester_id);
create policy "Staff see all requests" on public.cash_requests for select using (
exists (select 1 from public.profiles where id = auth.uid() and role in ('admin', 'controller', 'cfo', 'cashier'))
);
create policy "Requester can insert requests" on public.cash_requests for insert with check (auth.uid() = requester_id);
-- Transactions:
-- Viewable by authenticated.
-- Insertable by Cashier/Admin.
create policy "Transactions viewable by authenticated" on public.cash_transactions for select using (auth.role() = 'authenticated');
-- Admin policies for managing projects
create policy "Admin can insert projects" on public.projects for insert with check (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
create policy "Admin can update projects" on public.projects for update using (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
create policy "Admin can delete projects" on public.projects for delete using (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
-- Admin policies for managing analytical accounts
create policy "Admin can insert analytical accounts" on public.analytical_accounts for insert with check (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
create policy "Admin can update analytical accounts" on public.analytical_accounts for update using (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
create policy "Admin can delete analytical accounts" on public.analytical_accounts for delete using (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
-- Admin policies for app settings
create policy "Admin can view app settings" on public.app_settings for select using (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
create policy "Admin can update app settings" on public.app_settings for update using (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
create policy "Admin can insert app settings" on public.app_settings for insert with check (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
-- Admin can update user roles
create policy "Admin can update user roles" on public.profiles for update using (
exists (select 1 from public.profiles where id = auth.uid() and role = 'admin')
);
-- Storage for proof documents
insert into storage.buckets (id, name, public)
values ('proof-documents', 'proof-documents', false)
on conflict (id) do nothing;
-- Storage policies
DO $$
BEGIN
DROP POLICY IF EXISTS "Authenticated users can view proof documents" ON storage.objects;
DROP POLICY IF EXISTS "Cashiers can upload proof documents" ON storage.objects;
DROP POLICY IF EXISTS "Cashiers can update proof documents" ON storage.objects;
END $$;
create policy "Authenticated users can view proof documents"
on storage.objects for select
using (bucket_id = 'proof-documents' and auth.role() = 'authenticated');
create policy "Cashiers can upload proof documents"
on storage.objects for insert
with check (
bucket_id = 'proof-documents' and
exists (select 1 from public.profiles where id = auth.uid() and role in ('cashier', 'admin'))
);
create policy "Cashiers can update proof documents"
on storage.objects for update
using (
bucket_id = 'proof-documents' and
exists (select 1 from public.profiles where id = auth.uid() and role in ('cashier', 'admin'))
);
-- Functions & Triggers
-- Handle new user signup -> create profile
create or replace function public.handle_new_user()
returns trigger as $$
begin
insert into public.profiles (id, full_name, role)
values (new.id, new.raw_user_meta_data->>'full_name', 'requester')
on conflict (id) do nothing;
return new;
end;
$$ language plpgsql security definer;
-- Drop trigger if exists to allow recreation
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();
-- Seed Initial Settings
insert into public.app_settings (key, value) values
('max_outflow_limit', '250000'),
('alert_threshold', '50000')
on conflict (key) do nothing;