@fayz-ai/plugin-reports 0.16.28 → 0.16.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,692 @@
1
+ // src/migrations/index.ts
2
+ var MIGRATION_000_BASELINE = `-- ============================================================================
3
+ -- plugins/plugin-reports/src/migrations/000_baseline.sql \u2014 what installing this provisions, as one file.
4
+ --
5
+ -- GENERATED by scripts/emit-unit-baselines.mjs (#338). Do not edit by hand:
6
+ -- change the schema, regenerate, and let scripts/check-chain-equivalence.mjs
7
+ -- prove the result still matches.
8
+ --
9
+ -- This replaced 3 migration files. They are kept, unapplied, in
10
+ -- migrations.archive/ \u2014 a year of decisions is worth reading even when it is no
11
+ -- longer worth replaying.
12
+ --
13
+ -- Object order is fixed rather than dependency-sorted: schemas, types,
14
+ -- sequences, tables, defaults, functions, constraints, views, indexes, foreign
15
+ -- keys, triggers, row security, policies, grants. Dependencies BETWEEN units
16
+ -- are carried by the order packages/db/chain.json declares.
17
+ -- ============================================================================
18
+
19
+ -- A LANGUAGE sql function binds its body at CREATE time, and 570 functions
20
+ -- sorted by name are not sorted by who calls whom. This is what lets them load
21
+ -- in any order; every body is still compiled on first call, so a genuinely
22
+ -- broken reference surfaces then rather than never. Session-scoped.
23
+ SET check_function_bodies = false;
24
+
25
+
26
+
27
+ -- \u2500\u2500 default privileges: reset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
28
+ -- Cleared BEFORE anything is created, so every object below gets the ACL the
29
+ -- dump describes rather than the image's defaults on top of it. Restored at
30
+ -- the end of this file.
31
+
32
+ DO $dp$ BEGIN
33
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;
34
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
35
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;
36
+ END $dp$;
37
+
38
+ DO $dp$ BEGIN
39
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;
40
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
41
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;
42
+ END $dp$;
43
+
44
+ DO $dp$ BEGIN
45
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;
46
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
47
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;
48
+ END $dp$;
49
+
50
+ DO $dp$ BEGIN
51
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;
52
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
53
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;
54
+ END $dp$;
55
+
56
+ DO $dp$ BEGIN
57
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;
58
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
59
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;
60
+ END $dp$;
61
+
62
+ DO $dp$ BEGIN
63
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;
64
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
65
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;
66
+ END $dp$;
67
+
68
+
69
+ -- \u2500\u2500 table \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
70
+
71
+ CREATE TABLE public.plg_reports_definitions (
72
+ id uuid DEFAULT gen_random_uuid() NOT NULL,
73
+ tenant_id uuid,
74
+ unit_id uuid,
75
+ key text NOT NULL,
76
+ version integer DEFAULT 1 NOT NULL,
77
+ title text NOT NULL,
78
+ description text,
79
+ module text NOT NULL,
80
+ source_view text NOT NULL,
81
+ params jsonb DEFAULT '[]'::jsonb NOT NULL,
82
+ columns jsonb DEFAULT '[]'::jsonb NOT NULL,
83
+ default_order_by text,
84
+ required_permission text NOT NULL,
85
+ is_active boolean DEFAULT true NOT NULL,
86
+ metadata jsonb DEFAULT '{}'::jsonb NOT NULL,
87
+ created_by uuid,
88
+ owner_id uuid,
89
+ created_at timestamp with time zone DEFAULT now() NOT NULL,
90
+ updated_at timestamp with time zone DEFAULT now() NOT NULL,
91
+ CONSTRAINT plg_reports_definitions_required_permission_check CHECK ((btrim(required_permission) <> ''::text)),
92
+ CONSTRAINT plg_reports_definitions_version_check CHECK ((version >= 1))
93
+ );
94
+
95
+ ALTER TABLE ONLY public.plg_reports_definitions FORCE ROW LEVEL SECURITY;
96
+
97
+ CREATE TABLE public.plg_reports_runs (
98
+ id uuid DEFAULT gen_random_uuid() NOT NULL,
99
+ tenant_id uuid NOT NULL,
100
+ unit_id uuid,
101
+ definition_key text NOT NULL,
102
+ definition_version integer DEFAULT 1 NOT NULL,
103
+ params jsonb DEFAULT '{}'::jsonb NOT NULL,
104
+ requested_by uuid,
105
+ row_count bigint,
106
+ duration_ms integer,
107
+ status text DEFAULT 'ok'::text NOT NULL,
108
+ error text,
109
+ format text,
110
+ owner_id uuid,
111
+ created_at timestamp with time zone DEFAULT now() NOT NULL,
112
+ updated_at timestamp with time zone DEFAULT now() NOT NULL,
113
+ CONSTRAINT plg_reports_runs_status_check CHECK ((status = ANY (ARRAY['ok'::text, 'refused'::text, 'error'::text])))
114
+ );
115
+
116
+ ALTER TABLE ONLY public.plg_reports_runs FORCE ROW LEVEL SECURITY;
117
+
118
+ CREATE TABLE public.plg_reports_schedules (
119
+ id uuid DEFAULT gen_random_uuid() NOT NULL,
120
+ tenant_id uuid NOT NULL,
121
+ unit_id uuid,
122
+ definition_key text NOT NULL,
123
+ cron text NOT NULL,
124
+ params jsonb DEFAULT '{}'::jsonb NOT NULL,
125
+ recipients jsonb DEFAULT '[]'::jsonb NOT NULL,
126
+ format text DEFAULT 'csv'::text NOT NULL,
127
+ template_key text DEFAULT 'report.delivered'::text NOT NULL,
128
+ is_active boolean DEFAULT true NOT NULL,
129
+ last_run_at timestamp with time zone,
130
+ next_run_at timestamp with time zone,
131
+ owner_id uuid,
132
+ created_at timestamp with time zone DEFAULT now() NOT NULL,
133
+ updated_at timestamp with time zone DEFAULT now() NOT NULL,
134
+ CONSTRAINT plg_reports_schedules_format_check CHECK ((format = ANY (ARRAY['csv'::text, 'xlsx'::text, 'pdf'::text])))
135
+ );
136
+
137
+ ALTER TABLE ONLY public.plg_reports_schedules FORCE ROW LEVEL SECURITY;
138
+
139
+
140
+ -- \u2500\u2500 function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
141
+
142
+ CREATE FUNCTION public.reports_definition(p_key text, p_tenant uuid DEFAULT NULL::uuid) RETURNS public.plg_reports_definitions
143
+ LANGUAGE sql STABLE SECURITY DEFINER
144
+ SET search_path TO ''
145
+ AS $$
146
+ SELECT d.* FROM public.plg_reports_definitions d
147
+ WHERE d.key = p_key AND d.is_active
148
+ AND (d.tenant_id = coalesce(p_tenant, app.current_tenant_id()) OR d.tenant_id IS NULL)
149
+ -- A tenant's own definition beats the platform one, and the newest version
150
+ -- beats an older one.
151
+ ORDER BY (d.tenant_id IS NOT NULL) DESC, d.version DESC
152
+ LIMIT 1
153
+ $$;
154
+
155
+ CREATE FUNCTION public.reports_deliver_due(p_limit integer DEFAULT 50) RETURNS jsonb
156
+ LANGUAGE plpgsql SECURITY DEFINER
157
+ SET search_path TO ''
158
+ AS $$
159
+ DECLARE
160
+ s public.plg_reports_schedules%ROWTYPE;
161
+ v_recipient jsonb;
162
+ v_res jsonb;
163
+ v_sent integer := 0;
164
+ v_runs integer := 0;
165
+ BEGIN
166
+ IF to_regprocedure('public.notifications_enqueue(uuid, text, uuid, jsonb, text, timestamptz, text, text, uuid, uuid)') IS NULL THEN
167
+ RETURN jsonb_build_object('skipped', 'plugin-notifications is not installed');
168
+ END IF;
169
+
170
+ FOR s IN
171
+ SELECT * FROM public.plg_reports_schedules
172
+ WHERE is_active AND (next_run_at IS NULL OR next_run_at <= now())
173
+ ORDER BY next_run_at NULLS FIRST
174
+ LIMIT greatest(p_limit, 0)
175
+ FOR UPDATE SKIP LOCKED
176
+ LOOP
177
+ v_runs := v_runs + 1;
178
+ FOR v_recipient IN SELECT * FROM jsonb_array_elements(coalesce(s.recipients, '[]'::jsonb)) LOOP
179
+ -- The campaign rule applies to a report too: this ENQUEUES, it does not
180
+ -- send. One engine, one queue, one place a delivery can be retried.
181
+ v_res := to_jsonb(public.notifications_enqueue(
182
+ p_tenant_id => s.tenant_id,
183
+ p_template_key => s.template_key,
184
+ p_person_id => nullif(v_recipient ->> 'person_id', '')::uuid,
185
+ p_payload => jsonb_build_object('report_key', s.definition_key, 'format', s.format),
186
+ p_dedupe_key => 'report:' || s.id::text || ':' || to_char(now(), 'YYYY-MM-DD') || ':' || coalesce(v_recipient ->> 'person_id', ''),
187
+ p_unit_id => s.unit_id));
188
+ v_sent := v_sent + 1;
189
+ END LOOP;
190
+
191
+ UPDATE public.plg_reports_schedules
192
+ SET last_run_at = now(),
193
+ next_run_at = now() + interval '1 day'
194
+ WHERE id = s.id;
195
+ END LOOP;
196
+
197
+ RETURN jsonb_build_object('schedules', v_runs, 'queued', v_sent);
198
+ END $$;
199
+
200
+ CREATE FUNCTION public.reports_ensure_delivery_template(p_tenant uuid, p_channel text DEFAULT 'email'::text) RETURNS uuid
201
+ LANGUAGE plpgsql SECURITY DEFINER
202
+ SET search_path TO ''
203
+ AS $$
204
+ DECLARE v_id uuid;
205
+ BEGIN
206
+ IF to_regclass('public.plg_notifications_templates') IS NULL THEN
207
+ RAISE EXCEPTION 'reports_ensure_delivery_template: plugin-notifications is not installed' USING ERRCODE = '0A000';
208
+ END IF;
209
+ INSERT INTO public.plg_notifications_templates (tenant_id, key, channel, locale, subject, body, is_default, purpose)
210
+ VALUES (p_tenant, 'report.delivered', p_channel, 'pt-BR',
211
+ 'Seu relat\xF3rio est\xE1 pronto',
212
+ 'O relat\xF3rio {{report_key}} foi gerado em {{format}} e est\xE1 dispon\xEDvel.', true, 'transactional')
213
+ ON CONFLICT (tenant_id, key, channel, locale) DO UPDATE SET is_active = true
214
+ RETURNING id INTO v_id;
215
+ RETURN v_id;
216
+ END $$;
217
+
218
+ CREATE FUNCTION public.reports_run(p_key text, p_params jsonb DEFAULT '{}'::jsonb, p_limit integer DEFAULT 1000, p_offset integer DEFAULT 0, p_format text DEFAULT NULL::text) RETURNS jsonb
219
+ LANGUAGE plpgsql SECURITY DEFINER
220
+ SET search_path TO ''
221
+ AS $$
222
+ DECLARE
223
+ v_tenant uuid := app.current_tenant_id();
224
+ v_def public.plg_reports_definitions%ROWTYPE;
225
+ v_sql text;
226
+ v_where text := '';
227
+ v_param jsonb;
228
+ v_name text;
229
+ v_value text;
230
+ v_rows jsonb;
231
+ v_count bigint := 0;
232
+ v_start timestamptz := clock_timestamp();
233
+ v_has_unit boolean;
234
+ BEGIN
235
+ IF v_tenant IS NULL THEN
236
+ RAISE EXCEPTION 'reports_run: no active tenant' USING ERRCODE = '42501';
237
+ END IF;
238
+
239
+ v_def := public.reports_definition(p_key, v_tenant);
240
+ IF v_def.id IS NULL THEN
241
+ RAISE EXCEPTION 'reports_run: no active report called %', p_key USING ERRCODE = 'P0002';
242
+ END IF;
243
+
244
+ -- \u2500\u2500 refuse BEFORE the query, not after the rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
245
+ --
246
+ -- The refusal is RETURNED rather than raised, and the reason is mechanical:
247
+ -- raising rolls the statement back, taking the run record with it, and a
248
+ -- refusal nobody can see afterwards is the thing this history exists for.
249
+ -- No rows are read either way \u2014 which is what "refuse before the query" means
250
+ -- \u2014 and a caller that ignores \`status\` still gets nothing.
251
+ IF NOT (select app.has_permission(v_def.required_permission, NULL)) THEN
252
+ INSERT INTO public.plg_reports_runs (tenant_id, definition_key, definition_version, params, requested_by, status, error)
253
+ VALUES (v_tenant, p_key, v_def.version, coalesce(p_params, '{}'::jsonb), auth.uid(), 'refused',
254
+ format('%s is required', v_def.required_permission));
255
+ RETURN jsonb_build_object(
256
+ 'key', p_key, 'version', v_def.version, 'title', v_def.title,
257
+ 'status', 'refused', 'required_permission', v_def.required_permission,
258
+ 'columns', '[]'::jsonb, 'rows', '[]'::jsonb, 'row_count', 0);
259
+ END IF;
260
+
261
+ -- \u2500\u2500 scope the engine injects, and no definition can switch off \u2500\u2500\u2500\u2500\u2500\u2500\u2500
262
+ v_where := format('%I = %L', 'tenant_id', v_tenant);
263
+
264
+ SELECT EXISTS (
265
+ SELECT 1 FROM information_schema.columns
266
+ WHERE table_schema = 'public' AND table_name = v_def.source_view AND column_name = 'unit_id')
267
+ INTO v_has_unit;
268
+ IF v_has_unit THEN
269
+ v_where := v_where || ' AND (unit_id IS NULL OR (select app.has_unit(unit_id)))';
270
+ END IF;
271
+
272
+ -- \u2500\u2500 the declared parameters, and only those \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
273
+ FOR v_param IN SELECT * FROM jsonb_array_elements(coalesce(v_def.params, '[]'::jsonb)) LOOP
274
+ v_name := v_param ->> 'name';
275
+ v_value := coalesce(p_params ->> v_name, v_param ->> 'default');
276
+ IF v_value IS NULL THEN
277
+ IF coalesce((v_param ->> 'required')::boolean, false) THEN
278
+ RAISE EXCEPTION 'reports_run: % requires the parameter %', p_key, v_name USING ERRCODE = '22023';
279
+ END IF;
280
+ CONTINUE;
281
+ END IF;
282
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns
283
+ WHERE table_schema = 'public' AND table_name = v_def.source_view
284
+ AND column_name = coalesce(v_param ->> 'column', v_name)) THEN
285
+ RAISE EXCEPTION 'reports_run: parameter % has no column on %', v_name, v_def.source_view USING ERRCODE = '42703';
286
+ END IF;
287
+ v_where := v_where || format(' AND %I %s %L',
288
+ coalesce(v_param ->> 'column', v_name),
289
+ CASE v_param ->> 'operator' WHEN '>=' THEN '>=' WHEN '<=' THEN '<=' WHEN '>' THEN '>' WHEN '<' THEN '<' WHEN 'ilike' THEN 'ILIKE' ELSE '=' END,
290
+ v_value);
291
+ END LOOP;
292
+
293
+ -- A parameter the caller sent that the definition never declared is refused
294
+ -- rather than ignored: silently dropping a filter shows the wrong number.
295
+ IF p_params IS NOT NULL AND jsonb_typeof(p_params) = 'object' THEN
296
+ FOR v_name IN SELECT key FROM jsonb_each_text(p_params) LOOP
297
+ IF NOT EXISTS (SELECT 1 FROM jsonb_array_elements(coalesce(v_def.params, '[]'::jsonb)) d WHERE d ->> 'name' = v_name) THEN
298
+ RAISE EXCEPTION 'reports_run: % does not declare a parameter called %', p_key, v_name USING ERRCODE = '22023';
299
+ END IF;
300
+ END LOOP;
301
+ END IF;
302
+
303
+ v_sql := format('SELECT coalesce(jsonb_agg(t), ''[]''::jsonb), count(*) FROM (SELECT * FROM public.%I WHERE %s %s LIMIT %s OFFSET %s) t',
304
+ v_def.source_view, v_where,
305
+ CASE WHEN v_def.default_order_by IS NULL THEN '' ELSE 'ORDER BY ' || quote_ident(v_def.default_order_by) END,
306
+ greatest(coalesce(p_limit, 1000), 0), greatest(coalesce(p_offset, 0), 0));
307
+
308
+ EXECUTE v_sql INTO v_rows, v_count;
309
+
310
+ INSERT INTO public.plg_reports_runs (tenant_id, definition_key, definition_version, params, requested_by,
311
+ row_count, duration_ms, status, format)
312
+ VALUES (v_tenant, p_key, v_def.version, coalesce(p_params, '{}'::jsonb), auth.uid(),
313
+ v_count, (extract(epoch FROM clock_timestamp() - v_start) * 1000)::integer, 'ok', p_format);
314
+
315
+ RETURN jsonb_build_object(
316
+ 'key', p_key, 'version', v_def.version, 'title', v_def.title,
317
+ 'status', 'ok', 'columns', v_def.columns, 'rows', v_rows, 'row_count', v_count);
318
+ END $$;
319
+
320
+ CREATE FUNCTION public.trg_plg_reports_definitions_source() RETURNS trigger
321
+ LANGUAGE plpgsql SECURITY DEFINER
322
+ SET search_path TO ''
323
+ AS $_$
324
+ DECLARE v_kind char;
325
+ BEGIN
326
+ IF NEW.source_view !~ '^[a-z_][a-z0-9_]*$' THEN
327
+ RAISE EXCEPTION 'plg_reports_definitions: source_view % is not a bare relation name in public', NEW.source_view
328
+ USING ERRCODE = '22023';
329
+ END IF;
330
+
331
+ SELECT c.relkind INTO v_kind
332
+ FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
333
+ WHERE n.nspname = 'public' AND c.relname = NEW.source_view;
334
+
335
+ IF v_kind IS NULL THEN
336
+ RAISE EXCEPTION 'plg_reports_definitions: public.% does not exist', NEW.source_view USING ERRCODE = '42P01';
337
+ END IF;
338
+
339
+ -- A view or a materialized view is a read model by construction. A TABLE is
340
+ -- only allowed when its name says so \u2014 that is the \`plg_<x>_rep_*\` convention
341
+ -- Phase 1 established, and it is checked rather than trusted.
342
+ IF v_kind NOT IN ('v', 'm') AND NEW.source_view !~ '^plg_[a-z0-9_]+_rep_' THEN
343
+ RAISE EXCEPTION 'plg_reports_definitions: public.% is a business table, not a read model', NEW.source_view
344
+ USING ERRCODE = '23514',
345
+ HINT = 'Point the definition at a view or at a plg_<plugin>_rep_* read model. Reporting off a business table is how a report starts holding a schema still.';
346
+ END IF;
347
+ RETURN NEW;
348
+ END $_$;
349
+
350
+
351
+ -- \u2500\u2500 constraint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
352
+
353
+ ALTER TABLE ONLY public.plg_reports_definitions
354
+ ADD CONSTRAINT plg_reports_definitions_pkey PRIMARY KEY (id);
355
+
356
+ ALTER TABLE ONLY public.plg_reports_definitions
357
+ ADD CONSTRAINT plg_reports_definitions_tenant_id_id_key UNIQUE (tenant_id, id);
358
+
359
+ ALTER TABLE ONLY public.plg_reports_runs
360
+ ADD CONSTRAINT plg_reports_runs_pkey PRIMARY KEY (id);
361
+
362
+ ALTER TABLE ONLY public.plg_reports_runs
363
+ ADD CONSTRAINT plg_reports_runs_tenant_id_id_key UNIQUE (tenant_id, id);
364
+
365
+ ALTER TABLE ONLY public.plg_reports_schedules
366
+ ADD CONSTRAINT plg_reports_schedules_pkey PRIMARY KEY (id);
367
+
368
+ ALTER TABLE ONLY public.plg_reports_schedules
369
+ ADD CONSTRAINT plg_reports_schedules_tenant_id_id_key UNIQUE (tenant_id, id);
370
+
371
+
372
+ -- \u2500\u2500 index \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
373
+
374
+ CREATE UNIQUE INDEX plg_reports_definitions_key_uq ON public.plg_reports_definitions USING btree (COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::uuid), key, version);
375
+
376
+ CREATE INDEX plg_reports_definitions_module_idx ON public.plg_reports_definitions USING btree (module) WHERE is_active;
377
+
378
+ CREATE INDEX plg_reports_definitions_tenant_owner_idx ON public.plg_reports_definitions USING btree (tenant_id, owner_id) WHERE (owner_id IS NOT NULL);
379
+
380
+ CREATE INDEX plg_reports_definitions_tenant_unit_idx ON public.plg_reports_definitions USING btree (tenant_id, unit_id);
381
+
382
+ CREATE INDEX plg_reports_runs_key_idx ON public.plg_reports_runs USING btree (tenant_id, definition_key, created_at DESC);
383
+
384
+ CREATE INDEX plg_reports_runs_tenant_owner_idx ON public.plg_reports_runs USING btree (tenant_id, owner_id) WHERE (owner_id IS NOT NULL);
385
+
386
+ CREATE INDEX plg_reports_runs_tenant_unit_idx ON public.plg_reports_runs USING btree (tenant_id, unit_id);
387
+
388
+ CREATE INDEX plg_reports_schedules_due_idx ON public.plg_reports_schedules USING btree (next_run_at) WHERE is_active;
389
+
390
+ CREATE INDEX plg_reports_schedules_tenant_owner_idx ON public.plg_reports_schedules USING btree (tenant_id, owner_id) WHERE (owner_id IS NOT NULL);
391
+
392
+ CREATE INDEX plg_reports_schedules_tenant_unit_idx ON public.plg_reports_schedules USING btree (tenant_id, unit_id);
393
+
394
+
395
+ -- \u2500\u2500 fk constraint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
396
+
397
+ ALTER TABLE ONLY public.plg_reports_definitions
398
+ ADD CONSTRAINT plg_reports_definitions_owner_fk FOREIGN KEY (tenant_id, owner_id) REFERENCES app.memberships(tenant_id, user_id) ON DELETE SET NULL;
399
+
400
+ ALTER TABLE ONLY public.plg_reports_definitions
401
+ ADD CONSTRAINT plg_reports_definitions_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;
402
+
403
+ ALTER TABLE ONLY public.plg_reports_definitions
404
+ ADD CONSTRAINT plg_reports_definitions_unit_fk FOREIGN KEY (tenant_id, unit_id) REFERENCES app.units(tenant_id, id) ON DELETE SET NULL;
405
+
406
+ ALTER TABLE ONLY public.plg_reports_runs
407
+ ADD CONSTRAINT plg_reports_runs_owner_fk FOREIGN KEY (tenant_id, owner_id) REFERENCES app.memberships(tenant_id, user_id) ON DELETE SET NULL;
408
+
409
+ ALTER TABLE ONLY public.plg_reports_runs
410
+ ADD CONSTRAINT plg_reports_runs_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;
411
+
412
+ ALTER TABLE ONLY public.plg_reports_runs
413
+ ADD CONSTRAINT plg_reports_runs_unit_fk FOREIGN KEY (tenant_id, unit_id) REFERENCES app.units(tenant_id, id) ON DELETE SET NULL;
414
+
415
+ ALTER TABLE ONLY public.plg_reports_schedules
416
+ ADD CONSTRAINT plg_reports_schedules_owner_fk FOREIGN KEY (tenant_id, owner_id) REFERENCES app.memberships(tenant_id, user_id) ON DELETE SET NULL;
417
+
418
+ ALTER TABLE ONLY public.plg_reports_schedules
419
+ ADD CONSTRAINT plg_reports_schedules_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;
420
+
421
+ ALTER TABLE ONLY public.plg_reports_schedules
422
+ ADD CONSTRAINT plg_reports_schedules_unit_fk FOREIGN KEY (tenant_id, unit_id) REFERENCES app.units(tenant_id, id) ON DELETE SET NULL;
423
+
424
+
425
+ -- \u2500\u2500 table data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
426
+
427
+ COPY public.plg_reports_definitions (id, tenant_id, unit_id, key, version, title, description, module, source_view, params, columns, default_order_by, required_permission, is_active, metadata, created_by, owner_id, created_at, updated_at) FROM stdin;
428
+ b9bdb96e-2219-4bea-a83d-be54e28c4bda \\N \\N orders.sales 1 Vendas Every sale, whichever door it came through \u2014 the query the order vocabulary made possible. orders v_sales [{"name": "channel", "column": "channel"}, {"name": "status", "column": "status"}, {"name": "from", "column": "created_at", "operator": ">="}, {"name": "to", "column": "created_at", "operator": "<="}] [] created_at orders.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
429
+ 706d0d78-193c-4abe-8828-5e00d719d155 \\N \\N orders.fulfillment 1 Entregas pendentes Ordered against delivered, per line. A positive pending is what a boolean could never describe. orders v_order_fulfillment [{"name": "state", "column": "state"}] [] \\N orders.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
430
+ 6e27d897-55d8-4f39-b963-b5cb3121069d \\N \\N financial.receivables 1 Contas a receber Instalments and what is still owed on them, straight from the allocations. financial v_financial_installment_balances [{"name": "status", "column": "status"}, {"name": "due_from", "column": "due_date", "operator": ">="}, {"name": "due_to", "column": "due_date", "operator": "<="}] [] due_date financial.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
431
+ 66d7f336-fff1-40ea-b754-6b158f1b0dea \\N \\N financial.uninvoiced 1 Vendas sem t\xEDtulo Sales that produced no invoice. Empty is the bar this phase closes on. financial v_orders_sem_invoice [{"name": "kind", "column": "kind"}] [] created_at financial.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
432
+ a5a3ffab-e6e1-46ad-942f-f36582b4e126 \\N \\N inventory.balances 1 Saldo de estoque The balance, from the one ledger that owns it. inventory v_inventory_balances [] [] \\N inventory.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
433
+ a2d3191e-5a8d-4e03-892f-0c9573675ea5 \\N \\N notifications.queue 1 Fila de notifica\xE7\xF5es What is waiting, what is overdue, what was abandoned and what was refused. notifications v_notification_queue [{"name": "status", "column": "status"}, {"name": "channel", "column": "channel"}] [] \\N notifications.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
434
+ c616ae83-b861-41fb-959a-bbb85431ed77 \\N \\N agenda.appointments 1 Agendamentos The calendar as a table, with the Venda and the payment state each booking carries. agenda v_appointments [{"name": "status", "column": "status"}, {"name": "from", "column": "starts_at", "operator": ">="}, {"name": "to", "column": "starts_at", "operator": "<="}] [] starts_at agenda.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
435
+ 5af4a8c8-903b-4ea6-8829-98877b017883 \\N \\N crm.funnel 1 Funil The funnel over plg_crm_deals, with won_at and lost_at \u2014 which is what makes a cycle length measurable. crm v_deals [{"name": "status", "column": "status"}, {"name": "stage", "column": "stage_name"}] [] created_at crm.pipeline.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
436
+ 9143ce67-ecaa-46a3-8028-7c354c34f301 \\N \\N marketing.campaigns 1 Campanhas Reach, delivery and conversion per campaign \u2014 the metric comes from here, never from a counter on the campaign. marketing v_marketing_campaign_metrics [{"name": "status", "column": "status"}] [] \\N marketing.read t {} \\N \\N 2026-09-04 15:03:27.267053+00 2026-09-04 15:03:27.267053+00
437
+ \\.
438
+
439
+
440
+ -- \u2500\u2500 trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
441
+
442
+ CREATE TRIGGER plg_reports_definitions_source BEFORE INSERT OR UPDATE OF source_view ON public.plg_reports_definitions FOR EACH ROW EXECUTE FUNCTION public.trg_plg_reports_definitions_source();
443
+
444
+ CREATE TRIGGER plg_reports_definitions_updated_at BEFORE UPDATE ON public.plg_reports_definitions FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
445
+
446
+ CREATE TRIGGER plg_reports_runs_updated_at BEFORE UPDATE ON public.plg_reports_runs FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
447
+
448
+ CREATE TRIGGER plg_reports_schedules_updated_at BEFORE UPDATE ON public.plg_reports_schedules FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
449
+
450
+
451
+ -- \u2500\u2500 row security \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
452
+
453
+ ALTER TABLE public.plg_reports_definitions ENABLE ROW LEVEL SECURITY;
454
+
455
+ ALTER TABLE public.plg_reports_runs ENABLE ROW LEVEL SECURITY;
456
+
457
+ ALTER TABLE public.plg_reports_schedules ENABLE ROW LEVEL SECURITY;
458
+
459
+
460
+ -- \u2500\u2500 policy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
461
+
462
+ CREATE POLICY plg_reports_definitions_authz_delete ON public.plg_reports_definitions FOR DELETE TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_definitions.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.definition'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.definition'::text, plg_reports_definitions.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_definitions.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.delete'::text, plg_reports_definitions.unit_id) AS has_permission)));
463
+
464
+ CREATE POLICY plg_reports_definitions_authz_insert ON public.plg_reports_definitions FOR INSERT TO authenticated WITH CHECK (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_definitions.unit_id) AS has_unit)) AND ( SELECT app.has_permission('reports.create'::text, plg_reports_definitions.unit_id) AS has_permission)));
465
+
466
+ CREATE POLICY plg_reports_definitions_authz_select ON public.plg_reports_definitions FOR SELECT TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_definitions.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.definition'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.definition'::text, plg_reports_definitions.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_definitions.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.read'::text, plg_reports_definitions.unit_id) AS has_permission)));
467
+
468
+ CREATE POLICY plg_reports_definitions_authz_update ON public.plg_reports_definitions FOR UPDATE TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_definitions.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.definition'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.definition'::text, plg_reports_definitions.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_definitions.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.edit'::text, plg_reports_definitions.unit_id) AS has_permission))) WITH CHECK (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_definitions.unit_id) AS has_unit)) AND ( SELECT app.has_permission('reports.edit'::text, plg_reports_definitions.unit_id) AS has_permission)));
469
+
470
+ CREATE POLICY plg_reports_definitions_platform_read ON public.plg_reports_definitions FOR SELECT TO authenticated USING (((tenant_id IS NULL) AND is_active));
471
+
472
+ CREATE POLICY plg_reports_runs_authz_delete ON public.plg_reports_runs FOR DELETE TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_runs.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.run'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.run'::text, plg_reports_runs.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_runs.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.delete'::text, plg_reports_runs.unit_id) AS has_permission)));
473
+
474
+ CREATE POLICY plg_reports_runs_authz_insert ON public.plg_reports_runs FOR INSERT TO authenticated WITH CHECK (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_runs.unit_id) AS has_unit)) AND ( SELECT app.has_permission('reports.create'::text, plg_reports_runs.unit_id) AS has_permission)));
475
+
476
+ CREATE POLICY plg_reports_runs_authz_select ON public.plg_reports_runs FOR SELECT TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_runs.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.run'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.run'::text, plg_reports_runs.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_runs.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.read'::text, plg_reports_runs.unit_id) AS has_permission)));
477
+
478
+ CREATE POLICY plg_reports_runs_authz_update ON public.plg_reports_runs FOR UPDATE TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_runs.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.run'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.run'::text, plg_reports_runs.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_runs.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.edit'::text, plg_reports_runs.unit_id) AS has_permission))) WITH CHECK (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_runs.unit_id) AS has_unit)) AND ( SELECT app.has_permission('reports.edit'::text, plg_reports_runs.unit_id) AS has_permission)));
479
+
480
+ CREATE POLICY plg_reports_schedules_authz_delete ON public.plg_reports_schedules FOR DELETE TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_schedules.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.schedule'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.schedule'::text, plg_reports_schedules.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_schedules.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.delete'::text, plg_reports_schedules.unit_id) AS has_permission)));
481
+
482
+ CREATE POLICY plg_reports_schedules_authz_insert ON public.plg_reports_schedules FOR INSERT TO authenticated WITH CHECK (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_schedules.unit_id) AS has_unit)) AND ( SELECT app.has_permission('reports.create'::text, plg_reports_schedules.unit_id) AS has_permission)));
483
+
484
+ CREATE POLICY plg_reports_schedules_authz_select ON public.plg_reports_schedules FOR SELECT TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_schedules.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.schedule'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.schedule'::text, plg_reports_schedules.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_schedules.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.read'::text, plg_reports_schedules.unit_id) AS has_permission)));
485
+
486
+ CREATE POLICY plg_reports_schedules_authz_update ON public.plg_reports_schedules FOR UPDATE TO authenticated USING (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_schedules.unit_id) AS has_unit)) AND ((NOT ( SELECT app.owner_scoped('reports.schedule'::text) AS owner_scoped)) OR (owner_id IS NULL) OR (owner_id = ( SELECT auth.uid() AS uid)) OR ( SELECT app.has_grant('reports.schedule'::text, plg_reports_schedules.id, ARRAY['shared_with'::text]) AS has_grant) OR ( SELECT app.has_permission('reports.read_all'::text, plg_reports_schedules.unit_id) AS has_permission)) AND ( SELECT app.has_permission('reports.edit'::text, plg_reports_schedules.unit_id) AS has_permission))) WITH CHECK (((tenant_id = ( SELECT app.current_tenant_id() AS current_tenant_id)) AND ((unit_id IS NULL) OR ( SELECT app.has_unit(plg_reports_schedules.unit_id) AS has_unit)) AND ( SELECT app.has_permission('reports.edit'::text, plg_reports_schedules.unit_id) AS has_permission)));
487
+
488
+
489
+ -- \u2500\u2500 acl \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
490
+
491
+ GRANT MAINTAIN ON TABLE public.plg_reports_definitions TO anon;
492
+ GRANT ALL ON TABLE public.plg_reports_definitions TO authenticated;
493
+ GRANT ALL ON TABLE public.plg_reports_definitions TO service_role;
494
+
495
+ REVOKE ALL ON FUNCTION public.reports_definition(p_key text, p_tenant uuid) FROM PUBLIC;
496
+ GRANT ALL ON FUNCTION public.reports_definition(p_key text, p_tenant uuid) TO authenticated;
497
+ GRANT ALL ON FUNCTION public.reports_definition(p_key text, p_tenant uuid) TO service_role;
498
+
499
+ REVOKE ALL ON FUNCTION public.reports_deliver_due(p_limit integer) FROM PUBLIC;
500
+ GRANT ALL ON FUNCTION public.reports_deliver_due(p_limit integer) TO service_role;
501
+
502
+ REVOKE ALL ON FUNCTION public.reports_ensure_delivery_template(p_tenant uuid, p_channel text) FROM PUBLIC;
503
+ GRANT ALL ON FUNCTION public.reports_ensure_delivery_template(p_tenant uuid, p_channel text) TO authenticated;
504
+ GRANT ALL ON FUNCTION public.reports_ensure_delivery_template(p_tenant uuid, p_channel text) TO service_role;
505
+
506
+ REVOKE ALL ON FUNCTION public.reports_run(p_key text, p_params jsonb, p_limit integer, p_offset integer, p_format text) FROM PUBLIC;
507
+ GRANT ALL ON FUNCTION public.reports_run(p_key text, p_params jsonb, p_limit integer, p_offset integer, p_format text) TO authenticated;
508
+ GRANT ALL ON FUNCTION public.reports_run(p_key text, p_params jsonb, p_limit integer, p_offset integer, p_format text) TO service_role;
509
+
510
+ REVOKE ALL ON FUNCTION public.trg_plg_reports_definitions_source() FROM PUBLIC;
511
+ GRANT ALL ON FUNCTION public.trg_plg_reports_definitions_source() TO service_role;
512
+
513
+ GRANT MAINTAIN ON TABLE public.plg_reports_runs TO anon;
514
+ GRANT ALL ON TABLE public.plg_reports_runs TO authenticated;
515
+ GRANT ALL ON TABLE public.plg_reports_runs TO service_role;
516
+
517
+ GRANT MAINTAIN ON TABLE public.plg_reports_schedules TO anon;
518
+ GRANT ALL ON TABLE public.plg_reports_schedules TO authenticated;
519
+ GRANT ALL ON TABLE public.plg_reports_schedules TO service_role;
520
+
521
+
522
+ -- \u2500\u2500 comment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
523
+
524
+ COMMENT ON TABLE public.plg_reports_definitions IS 'The report catalogue (PRD 08). Versioned, so changing a report does not rewrite the history of everyone who ran the previous one; and \`required_permission\` is declared HERE rather than on a screen, so the engine can refuse before it queries.';
525
+
526
+ COMMENT ON FUNCTION public.reports_deliver_due(p_limit integer) IS 'Queue the scheduled reports that are due (PRD 08). It enqueues through the notification engine rather than sending \u2014 the same mechanism as dunning and as a campaign, which is what made building that engine first worth doing.';
527
+
528
+ COMMENT ON FUNCTION public.reports_run(p_key text, p_params jsonb, p_limit integer, p_offset integer, p_format text) IS 'Run a report from the catalogue (PRD 08). The permission is checked BEFORE the query is built \u2014 a refusal reads no rows and comes back as status=refused, recorded in plg_reports_runs \u2014 the tenant and unit predicates are injected by the engine and cannot be switched off by a definition, and an undeclared parameter is refused rather than ignored.';
529
+
530
+ COMMENT ON TABLE public.plg_reports_runs IS 'Who ran what, with which parameters, against which VERSION of the definition (PRD 08). A refusal is recorded too \u2014 "the report is empty" and "you were not allowed to see it" are different answers and the history has to keep them apart.';
531
+
532
+ COMMENT ON TABLE public.plg_reports_schedules IS 'A report on a schedule (PRD 08). Delivery is the notification engine''s job (PRD 03) \u2014 the same queue, retry and opt-out that carry a receipt carry a report, which is the whole reason that engine was built first.';
533
+
534
+
535
+ -- \u2500\u2500 default privileges: restore \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
536
+ -- Put back, so the NEXT thing installed inherits what the chain had.
537
+
538
+ DO $dp$ BEGIN
539
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;
540
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
541
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;
542
+ END $dp$;
543
+
544
+ DO $dp$ BEGIN
545
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;
546
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
547
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;
548
+ END $dp$;
549
+
550
+ DO $dp$ BEGIN
551
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;
552
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
553
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;
554
+ END $dp$;
555
+
556
+ DO $dp$ BEGIN
557
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;
558
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
559
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;
560
+ END $dp$;
561
+
562
+ DO $dp$ BEGIN
563
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;
564
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
565
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;
566
+ END $dp$;
567
+
568
+ DO $dp$ BEGIN
569
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;
570
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
571
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;
572
+ END $dp$;
573
+
574
+ DO $dp$ BEGIN
575
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;
576
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
577
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;
578
+ END $dp$;
579
+
580
+ DO $dp$ BEGIN
581
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;
582
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
583
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;
584
+ END $dp$;
585
+
586
+ DO $dp$ BEGIN
587
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;
588
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
589
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;
590
+ END $dp$;
591
+
592
+ DO $dp$ BEGIN
593
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;
594
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
595
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;
596
+ END $dp$;
597
+
598
+ DO $dp$ BEGIN
599
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;
600
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
601
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;
602
+ END $dp$;
603
+
604
+ DO $dp$ BEGIN
605
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;
606
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
607
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;
608
+ END $dp$;
609
+
610
+ DO $dp$ BEGIN
611
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;
612
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
613
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;
614
+ END $dp$;
615
+
616
+ DO $dp$ BEGIN
617
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;
618
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
619
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;
620
+ END $dp$;
621
+
622
+ DO $dp$ BEGIN
623
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;
624
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
625
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;
626
+ END $dp$;
627
+
628
+ DO $dp$ BEGIN
629
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;
630
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
631
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;
632
+ END $dp$;
633
+
634
+ DO $dp$ BEGIN
635
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;
636
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
637
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;
638
+ END $dp$;
639
+
640
+ DO $dp$ BEGIN
641
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;
642
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
643
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;
644
+ END $dp$;
645
+
646
+ DO $dp$ BEGIN
647
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;
648
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
649
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;
650
+ END $dp$;
651
+
652
+ DO $dp$ BEGIN
653
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;
654
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
655
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;
656
+ END $dp$;
657
+
658
+ DO $dp$ BEGIN
659
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;
660
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
661
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;
662
+ END $dp$;
663
+
664
+ DO $dp$ BEGIN
665
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;
666
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
667
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;
668
+ END $dp$;
669
+
670
+ DO $dp$ BEGIN
671
+ EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;
672
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
673
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;
674
+ END $dp$;
675
+
676
+ DO $dp$ BEGIN
677
+ EXECUTE $stmt$--
678
+ -- PostgreSQL database dump complete
679
+ --$stmt$;
680
+ EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
681
+ RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$--
682
+ -- PostgreSQL database dump complete
683
+ --$stmt$;
684
+ END $dp$;
685
+ `;
686
+ var MIGRATIONS = [
687
+ { id: "000_baseline", sql: MIGRATION_000_BASELINE }
688
+ ];
689
+
690
+ export { MIGRATIONS, MIGRATION_000_BASELINE };
691
+ //# sourceMappingURL=main.js.map
692
+ //# sourceMappingURL=main.js.map