@fayz-ai/plugin-financial 0.9.0 → 0.9.2
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.
- package/README.md +3 -3
- package/dist/components/PersonStatementWidget.d.ts +1 -1
- package/dist/components/PersonStatementWidget.d.ts.map +1 -1
- package/dist/{FinancialContext.d.ts → context.d.ts} +4 -2
- package/dist/context.d.ts.map +1 -0
- package/dist/data/registries.d.ts.map +1 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1426 -1426
- package/dist/index.js.map +1 -1
- package/dist/integrations/stripe/connectorDef.d.ts +14 -0
- package/dist/integrations/stripe/connectorDef.d.ts.map +1 -0
- package/dist/integrations/stripe/data.d.ts +43 -0
- package/dist/integrations/stripe/data.d.ts.map +1 -0
- package/dist/lib/message-events.d.ts +12 -0
- package/dist/lib/message-events.d.ts.map +1 -0
- package/dist/{quick-add.d.ts → lib/quick-add.d.ts} +1 -1
- package/dist/lib/quick-add.d.ts.map +1 -0
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/{FinancialPage.d.ts → views/FinancialPage.d.ts} +3 -3
- package/dist/views/FinancialPage.d.ts.map +1 -0
- package/dist/views/InvoiceDetailView.d.ts.map +1 -1
- package/dist/views/InvoiceFormView.d.ts.map +1 -1
- package/dist/views/InvoiceListView.d.ts.map +1 -1
- package/dist/views/PayablesView.d.ts +1 -1
- package/dist/views/PayablesView.d.ts.map +1 -1
- package/dist/views/ReceivablesView.d.ts +1 -1
- package/dist/views/ReceivablesView.d.ts.map +1 -1
- package/dist/views/StatementsView.d.ts.map +1 -1
- package/dist/views/dashboardWidgets.d.ts +1 -1
- package/dist/views/dashboardWidgets.d.ts.map +1 -1
- package/package.json +12 -14
- package/dist/FinancialContext.d.ts.map +0 -1
- package/dist/FinancialPage.d.ts.map +0 -1
- package/dist/contract.test.d.ts +0 -2
- package/dist/contract.test.d.ts.map +0 -1
- package/dist/quick-add.d.ts.map +0 -1
- package/dist/registries.d.ts.map +0 -1
- package/src/FinancialContext.tsx +0 -59
- package/src/FinancialPage.tsx +0 -413
- package/src/README.md +0 -155
- package/src/components/FinancialGeneralSettings.tsx +0 -39
- package/src/components/FinancialOnboarding.tsx +0 -87
- package/src/components/FinancialSettings.tsx +0 -31
- package/src/components/PaymentModal.tsx +0 -369
- package/src/components/PersonStatementWidget.tsx +0 -133
- package/src/components/TransferModal.tsx +0 -150
- package/src/contract.test.ts +0 -58
- package/src/data/capability.test.ts +0 -135
- package/src/data/index.ts +0 -3
- package/src/data/mock.ts +0 -842
- package/src/data/supabase.ts +0 -968
- package/src/data/tables.ts +0 -13
- package/src/data/types.ts +0 -86
- package/src/index.ts +0 -487
- package/src/lib/tenant.ts +0 -3
- package/src/locales/en.ts +0 -493
- package/src/locales/index.ts +0 -7
- package/src/locales/pt-BR.ts +0 -493
- package/src/migrations/000_plg_rename.sql +0 -30
- package/src/migrations/001_financial_base.sql +0 -109
- package/src/migrations/002_chart_of_accounts.sql +0 -28
- package/src/migrations/003_card_brands.sql +0 -12
- package/src/migrations/004_order_to_cash.sql +0 -178
- package/src/migrations/005_seed_defaults.sql +0 -38
- package/src/migrations/006_rls_policies.sql +0 -37
- package/src/migrations/006b_extract_fee_amount.sql +0 -20
- package/src/migrations/007_reconciliation.sql +0 -32
- package/src/migrations/007b_movement_payment_method_type.sql +0 -6
- package/src/migrations/008_split_payment_movements.sql +0 -29
- package/src/migrations/009_agent_rpcs.sql +0 -165
- package/src/migrations/010_invoice_internal.sql +0 -133
- package/src/migrations/011_nfe.sql +0 -58
- package/src/migrations/index.ts +0 -923
- package/src/quick-add.ts +0 -58
- package/src/registries.ts +0 -308
- package/src/store.ts +0 -329
- package/src/types.ts +0 -592
- package/src/views/CardsView.tsx +0 -213
- package/src/views/CashRegistersView.tsx +0 -597
- package/src/views/CommissionsView.tsx +0 -18
- package/src/views/InvoiceDetailView.tsx +0 -616
- package/src/views/InvoiceFormView.tsx +0 -945
- package/src/views/InvoiceListView.tsx +0 -323
- package/src/views/NotasFiscaisView.tsx +0 -209
- package/src/views/PayablesView.tsx +0 -48
- package/src/views/QuickTransactionForm.tsx +0 -548
- package/src/views/ReceivablesView.tsx +0 -48
- package/src/views/ReconciliationView.tsx +0 -211
- package/src/views/StatementsView.tsx +0 -335
- package/src/views/SummaryView.tsx +0 -203
- package/src/views/dashboardWidgets.tsx +0 -437
- package/src/views/navReferrer.ts +0 -24
- /package/dist/{registries.d.ts → data/registries.d.ts} +0 -0
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
-- Financial Plugin: Chart of Accounts + Cost Centers
|
|
2
|
-
|
|
3
|
-
CREATE TABLE IF NOT EXISTS public.plg_financial_chart_of_accounts (
|
|
4
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
5
|
-
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
6
|
-
code text NOT NULL,
|
|
7
|
-
name text NOT NULL,
|
|
8
|
-
node_type text NOT NULL DEFAULT 'leaf',
|
|
9
|
-
parent_id uuid REFERENCES public.plg_financial_chart_of_accounts(id),
|
|
10
|
-
is_active boolean NOT NULL DEFAULT true,
|
|
11
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
12
|
-
updated_at timestamptz NOT NULL DEFAULT now()
|
|
13
|
-
);
|
|
14
|
-
ALTER TABLE public.plg_financial_chart_of_accounts ENABLE ROW LEVEL SECURITY;
|
|
15
|
-
CREATE INDEX IF NOT EXISTS idx_plg_financial_chart_of_accounts_tenant ON public.plg_financial_chart_of_accounts(tenant_id);
|
|
16
|
-
CREATE INDEX IF NOT EXISTS idx_plg_financial_chart_of_accounts_parent ON public.plg_financial_chart_of_accounts(parent_id);
|
|
17
|
-
|
|
18
|
-
CREATE TABLE IF NOT EXISTS public.plg_financial_cost_centers (
|
|
19
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
20
|
-
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
21
|
-
code text NOT NULL,
|
|
22
|
-
name text NOT NULL,
|
|
23
|
-
is_active boolean NOT NULL DEFAULT true,
|
|
24
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
25
|
-
updated_at timestamptz NOT NULL DEFAULT now()
|
|
26
|
-
);
|
|
27
|
-
ALTER TABLE public.plg_financial_cost_centers ENABLE ROW LEVEL SECURITY;
|
|
28
|
-
CREATE INDEX IF NOT EXISTS idx_plg_financial_cost_centers_tenant ON public.plg_financial_cost_centers(tenant_id);
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
-- Financial Plugin: Card Brands
|
|
2
|
-
|
|
3
|
-
CREATE TABLE IF NOT EXISTS public.plg_financial_card_brands (
|
|
4
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
5
|
-
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
6
|
-
name text NOT NULL,
|
|
7
|
-
is_active boolean NOT NULL DEFAULT true,
|
|
8
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
9
|
-
updated_at timestamptz NOT NULL DEFAULT now()
|
|
10
|
-
);
|
|
11
|
-
ALTER TABLE public.plg_financial_card_brands ENABLE ROW LEVEL SECURITY;
|
|
12
|
-
CREATE INDEX IF NOT EXISTS idx_plg_financial_card_brands_tenant ON public.plg_financial_card_brands(tenant_id);
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
-- Financial plugin — order-to-cash companion SQL.
|
|
2
|
-
-- The atomic "promote an order into an invoice + installment ledger" function
|
|
3
|
-
-- that CRM (approveQuote) and the agenda financial bridge both RPC. It belongs
|
|
4
|
-
-- to the financial plugin (it writes plg_financial_movements + reads orders), and it
|
|
5
|
-
-- ships WITH the plugin so enabling Payments provisions it. Idempotent.
|
|
6
|
-
-- Depends on: public.orders, public.plg_financial_movements (001_financial_base).
|
|
7
|
-
|
|
8
|
-
-- 1. Sequence generator (REC-00001 / PAG-00001 numbering)
|
|
9
|
-
CREATE TABLE IF NOT EXISTS public.sequences (
|
|
10
|
-
tenant_id uuid NOT NULL REFERENCES public.tenants(id),
|
|
11
|
-
kind text NOT NULL,
|
|
12
|
-
current_value bigint NOT NULL DEFAULT 0,
|
|
13
|
-
PRIMARY KEY (tenant_id, kind)
|
|
14
|
-
);
|
|
15
|
-
-- Baseline: RLS ON, deny-all (no policies), like the salon pool. The counter is
|
|
16
|
-
-- an internal, non-API table written ONLY through next_sequence() below, which is
|
|
17
|
-
-- SECURITY DEFINER and so bypasses RLS as the table owner. Legacy pools that
|
|
18
|
-
-- shipped this table without RLS are also remediated by packages/db 013_sequences_rls.sql.
|
|
19
|
-
ALTER TABLE public.sequences ENABLE ROW LEVEL SECURITY;
|
|
20
|
-
|
|
21
|
-
-- SECURITY DEFINER: next_sequence is called directly via PostgREST RPC by
|
|
22
|
-
-- authenticated users (src/data/supabase.ts → createInvoice), so with deny-all
|
|
23
|
-
-- RLS on public.sequences it MUST run as the table owner to write the counter.
|
|
24
|
-
-- SET search_path pins schema resolution for the definer context.
|
|
25
|
-
CREATE OR REPLACE FUNCTION public.next_sequence(p_tenant_id uuid, p_kind text)
|
|
26
|
-
RETURNS bigint
|
|
27
|
-
LANGUAGE plpgsql
|
|
28
|
-
SECURITY DEFINER
|
|
29
|
-
SET search_path = public
|
|
30
|
-
AS $$
|
|
31
|
-
DECLARE v_next bigint;
|
|
32
|
-
BEGIN
|
|
33
|
-
INSERT INTO public.sequences (tenant_id, kind, current_value)
|
|
34
|
-
VALUES (p_tenant_id, p_kind, 1)
|
|
35
|
-
ON CONFLICT (tenant_id, kind)
|
|
36
|
-
DO UPDATE SET current_value = public.sequences.current_value + 1
|
|
37
|
-
RETURNING current_value INTO v_next;
|
|
38
|
-
RETURN v_next;
|
|
39
|
-
END;
|
|
40
|
-
$$;
|
|
41
|
-
|
|
42
|
-
-- 2. Order-to-cash columns on the spine orders (plugin extends public.orders)
|
|
43
|
-
ALTER TABLE public.orders
|
|
44
|
-
ADD COLUMN IF NOT EXISTS stage text,
|
|
45
|
-
ADD COLUMN IF NOT EXISTS direction text;
|
|
46
|
-
|
|
47
|
-
-- 3. RLS + grants for plg_financial_movements (table from 001_financial_base.sql)
|
|
48
|
-
ALTER TABLE public.plg_financial_movements ENABLE ROW LEVEL SECURITY;
|
|
49
|
-
GRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_financial_movements TO authenticated;
|
|
50
|
-
DO $$
|
|
51
|
-
BEGIN
|
|
52
|
-
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='plg_financial_movements' AND policyname='plg_financial_movements_select') THEN
|
|
53
|
-
CREATE POLICY plg_financial_movements_select ON public.plg_financial_movements FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
54
|
-
CREATE POLICY plg_financial_movements_insert ON public.plg_financial_movements FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
55
|
-
CREATE POLICY plg_financial_movements_update ON public.plg_financial_movements FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
56
|
-
CREATE POLICY plg_financial_movements_delete ON public.plg_financial_movements FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
57
|
-
END IF;
|
|
58
|
-
END $$;
|
|
59
|
-
|
|
60
|
-
-- 4. The function (v2 — backward compatible with the 3-arg CRM call via defaults)
|
|
61
|
-
DROP FUNCTION IF EXISTS public.fn_invoice_from_order(uuid, date, text);
|
|
62
|
-
CREATE OR REPLACE FUNCTION public.fn_invoice_from_order(
|
|
63
|
-
p_order_id uuid,
|
|
64
|
-
p_due_date date DEFAULT NULL,
|
|
65
|
-
p_status text DEFAULT NULL,
|
|
66
|
-
p_installments int DEFAULT 1,
|
|
67
|
-
p_direction text DEFAULT 'credit'
|
|
68
|
-
) RETURNS uuid
|
|
69
|
-
LANGUAGE plpgsql
|
|
70
|
-
SECURITY DEFINER
|
|
71
|
-
SET search_path = public
|
|
72
|
-
AS $$
|
|
73
|
-
DECLARE
|
|
74
|
-
v_order public.orders%ROWTYPE;
|
|
75
|
-
v_ref text;
|
|
76
|
-
v_seq bigint;
|
|
77
|
-
v_summary text;
|
|
78
|
-
v_contact text;
|
|
79
|
-
v_existing uuid;
|
|
80
|
-
v_kind text;
|
|
81
|
-
v_prefix text;
|
|
82
|
-
v_n int := GREATEST(COALESCE(p_installments, 1), 1);
|
|
83
|
-
v_total numeric;
|
|
84
|
-
v_base numeric;
|
|
85
|
-
v_amt numeric;
|
|
86
|
-
v_alloc numeric := 0;
|
|
87
|
-
v_due date;
|
|
88
|
-
i int;
|
|
89
|
-
BEGIN
|
|
90
|
-
SELECT * INTO v_order FROM public.orders WHERE id = p_order_id;
|
|
91
|
-
IF NOT FOUND THEN RAISE EXCEPTION 'fn_invoice_from_order: order % not found', p_order_id; END IF;
|
|
92
|
-
IF v_order.tenant_id NOT IN (SELECT public.user_tenant_ids()) THEN
|
|
93
|
-
RAISE EXCEPTION 'fn_invoice_from_order: forbidden';
|
|
94
|
-
END IF;
|
|
95
|
-
|
|
96
|
-
SELECT id INTO v_existing FROM public.plg_financial_movements
|
|
97
|
-
WHERE invoice_id = p_order_id AND movement_kind = 'bill' LIMIT 1;
|
|
98
|
-
IF v_existing IS NOT NULL THEN RETURN p_order_id; END IF;
|
|
99
|
-
|
|
100
|
-
IF p_direction = 'debit' THEN v_kind := 'invoice_payable'; v_prefix := 'PAG';
|
|
101
|
-
ELSE v_kind := 'invoice_receivable'; v_prefix := 'REC'; END IF;
|
|
102
|
-
|
|
103
|
-
SELECT public.next_sequence(v_order.tenant_id, v_kind) INTO v_seq;
|
|
104
|
-
v_ref := v_prefix || '-' || lpad(v_seq::text, 5, '0');
|
|
105
|
-
|
|
106
|
-
SELECT string_agg(name, ', ' ORDER BY sort_order) INTO v_summary
|
|
107
|
-
FROM public.order_items WHERE order_id = p_order_id;
|
|
108
|
-
SELECT name INTO v_contact FROM public.people WHERE id = v_order.party_id;
|
|
109
|
-
|
|
110
|
-
UPDATE public.orders SET
|
|
111
|
-
status = COALESCE(p_status, status),
|
|
112
|
-
stage = 'invoiced',
|
|
113
|
-
direction = p_direction,
|
|
114
|
-
reference_number = v_ref,
|
|
115
|
-
metadata = COALESCE(metadata, '{}'::jsonb) || jsonb_build_object(
|
|
116
|
-
'itemsSummary', COALESCE(v_summary, ''),
|
|
117
|
-
'contactName', v_contact,
|
|
118
|
-
'installmentCount', v_n,
|
|
119
|
-
'quoteNumber', v_order.reference_number,
|
|
120
|
-
'direction', p_direction
|
|
121
|
-
),
|
|
122
|
-
updated_at = now()
|
|
123
|
-
WHERE id = p_order_id;
|
|
124
|
-
|
|
125
|
-
v_total := COALESCE(v_order.total, 0);
|
|
126
|
-
v_base := round(v_total / v_n, 2);
|
|
127
|
-
v_due := COALESCE(p_due_date, current_date);
|
|
128
|
-
FOR i IN 1..v_n LOOP
|
|
129
|
-
IF i = v_n THEN v_amt := round(v_total - v_alloc, 2);
|
|
130
|
-
ELSE v_amt := v_base; v_alloc := v_alloc + v_base; END IF;
|
|
131
|
-
INSERT INTO public.plg_financial_movements
|
|
132
|
-
(tenant_id, invoice_id, direction, movement_kind, amount, paid_amount, status, due_date, installment_number)
|
|
133
|
-
VALUES
|
|
134
|
-
(v_order.tenant_id, p_order_id, p_direction, 'bill', v_amt, 0, 'pending',
|
|
135
|
-
(v_due + ((i - 1) || ' months')::interval)::date, i);
|
|
136
|
-
END LOOP;
|
|
137
|
-
|
|
138
|
-
RETURN p_order_id;
|
|
139
|
-
END;
|
|
140
|
-
$$;
|
|
141
|
-
GRANT EXECUTE ON FUNCTION public.fn_invoice_from_order(uuid, date, text, int, text) TO authenticated;
|
|
142
|
-
|
|
143
|
-
-- 5. Derived balances view (Payments page + agenda payment badge read this)
|
|
144
|
-
CREATE OR REPLACE VIEW public.v_invoice_balances AS
|
|
145
|
-
WITH bill AS (
|
|
146
|
-
SELECT
|
|
147
|
-
invoice_id,
|
|
148
|
-
SUM(amount) FILTER (WHERE status <> 'cancelled') AS billed,
|
|
149
|
-
SUM(paid_amount) FILTER (WHERE status <> 'cancelled') AS paid,
|
|
150
|
-
bool_or(status NOT IN ('paid','cancelled') AND due_date < current_date) AS has_overdue
|
|
151
|
-
FROM public.plg_financial_movements
|
|
152
|
-
WHERE movement_kind = 'bill'
|
|
153
|
-
GROUP BY invoice_id
|
|
154
|
-
)
|
|
155
|
-
SELECT
|
|
156
|
-
o.id AS invoice_id, o.tenant_id, o.kind, o.reference_number, o.party_id, o.direction,
|
|
157
|
-
CASE WHEN o.direction = 'debit' OR o.kind = 'invoice_payable' THEN 'payable' ELSE 'receivable' END AS flow,
|
|
158
|
-
COALESCE(b.billed, o.total, 0) AS amount,
|
|
159
|
-
COALESCE(b.paid, 0) AS paid,
|
|
160
|
-
CASE WHEN o.status = 'cancelled' OR o.stage = 'cancelled' THEN 0
|
|
161
|
-
ELSE COALESCE(b.billed, o.total, 0) - COALESCE(b.paid, 0) END AS balance,
|
|
162
|
-
CASE
|
|
163
|
-
WHEN o.status = 'cancelled' OR o.stage = 'cancelled' THEN 'cancelled'
|
|
164
|
-
WHEN COALESCE(b.billed, o.total, 0) = 0 THEN 'open'
|
|
165
|
-
WHEN COALESCE(b.paid, 0) >= COALESCE(b.billed, o.total, 0) THEN 'paid'
|
|
166
|
-
WHEN COALESCE(b.paid, 0) > 0 THEN 'partial'
|
|
167
|
-
WHEN COALESCE(b.has_overdue, false) THEN 'overdue'
|
|
168
|
-
ELSE 'open'
|
|
169
|
-
END AS status,
|
|
170
|
-
o.created_at, o.updated_at
|
|
171
|
-
FROM public.orders o
|
|
172
|
-
LEFT JOIN bill b ON b.invoice_id = o.id
|
|
173
|
-
WHERE o.kind IN ('invoice_receivable','invoice_payable') OR b.invoice_id IS NOT NULL;
|
|
174
|
-
ALTER VIEW public.v_invoice_balances SET (security_invoker = true);
|
|
175
|
-
GRANT SELECT ON public.v_invoice_balances TO authenticated;
|
|
176
|
-
|
|
177
|
-
-- Refresh PostgREST's schema cache so the new RPC/views are visible immediately.
|
|
178
|
-
NOTIFY pgrst, 'reload schema';
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
-- Financial plugin — seed: payment method types + card brands.
|
|
2
|
-
-- These are the read-only "system" registries the plugin's UI assumes exist
|
|
3
|
-
-- (PaymentModal, card selection). They live as registry seedData in TS but were
|
|
4
|
-
-- never written to the DB, so a fresh tenant had none. This backfills them for
|
|
5
|
-
-- every tenant that has none. Idempotent. Plugin-owned so every app gets them.
|
|
6
|
-
|
|
7
|
-
DO $$
|
|
8
|
-
DECLARE t_id uuid;
|
|
9
|
-
BEGIN
|
|
10
|
-
FOR t_id IN (
|
|
11
|
-
SELECT id FROM public.tenants
|
|
12
|
-
WHERE id NOT IN (SELECT DISTINCT tenant_id FROM public.plg_financial_payment_method_types)
|
|
13
|
-
)
|
|
14
|
-
LOOP
|
|
15
|
-
INSERT INTO public.plg_financial_payment_method_types (tenant_id, name, transaction_type) VALUES
|
|
16
|
-
(t_id, 'Cash', 'cash'),
|
|
17
|
-
(t_id, 'PIX', 'pix'),
|
|
18
|
-
(t_id, 'Credit Card', 'credit_card'),
|
|
19
|
-
(t_id, 'Debit Card', 'debit_card'),
|
|
20
|
-
(t_id, 'Bank Transfer', 'bank_transfer'),
|
|
21
|
-
(t_id, 'Check', 'check');
|
|
22
|
-
END LOOP;
|
|
23
|
-
END $$;
|
|
24
|
-
|
|
25
|
-
DO $$
|
|
26
|
-
DECLARE t_id uuid;
|
|
27
|
-
BEGIN
|
|
28
|
-
FOR t_id IN (
|
|
29
|
-
SELECT id FROM public.tenants
|
|
30
|
-
WHERE id NOT IN (SELECT DISTINCT tenant_id FROM public.plg_financial_card_brands)
|
|
31
|
-
)
|
|
32
|
-
LOOP
|
|
33
|
-
INSERT INTO public.plg_financial_card_brands (tenant_id, name) VALUES
|
|
34
|
-
(t_id, 'Visa'), (t_id, 'Mastercard'), (t_id, 'American Express'),
|
|
35
|
-
(t_id, 'Elo'), (t_id, 'Hipercard'), (t_id, 'Diners Club'),
|
|
36
|
-
(t_id, 'Discover'), (t_id, 'JCB'), (t_id, 'Aura'), (t_id, 'Hiper');
|
|
37
|
-
END LOOP;
|
|
38
|
-
END $$;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
-- Financial Plugin: RLS policies + grants for ALL base tables.
|
|
2
|
-
--
|
|
3
|
-
-- The base migrations (001/002/003) ENABLE row-level security but relied on an
|
|
4
|
-
-- external `project_rls.sql` auto-detection step to create the actual policies.
|
|
5
|
-
-- That step does not run in the Drizzle/companion pipeline, so every table
|
|
6
|
-
-- except plg_financial_movements was RLS-enabled with NO policy → all inserts denied
|
|
7
|
-
-- ("new row violates row-level security policy"). This creates the canonical
|
|
8
|
-
-- per-tenant policies idempotently. Re-running is a no-op (IF NOT EXISTS guards;
|
|
9
|
-
-- plg_financial_movements policies from 004 are skipped).
|
|
10
|
-
--
|
|
11
|
-
-- Canonical RLS form: tenant_id IN (SELECT public.user_tenant_ids()).
|
|
12
|
-
|
|
13
|
-
DO $$
|
|
14
|
-
DECLARE t text;
|
|
15
|
-
BEGIN
|
|
16
|
-
FOR t IN SELECT unnest(ARRAY[
|
|
17
|
-
'plg_financial_payment_method_types','plg_financial_payment_methods','plg_financial_bank_accounts',
|
|
18
|
-
'plg_financial_cash_register_sessions','plg_financial_movements',
|
|
19
|
-
'plg_financial_chart_of_accounts','plg_financial_cost_centers','plg_financial_card_brands'
|
|
20
|
-
])
|
|
21
|
-
LOOP
|
|
22
|
-
EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY', t);
|
|
23
|
-
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON public.%I TO authenticated', t);
|
|
24
|
-
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename=t AND policyname=t||'_select') THEN
|
|
25
|
-
EXECUTE format('CREATE POLICY %I ON public.%I FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t||'_select', t);
|
|
26
|
-
END IF;
|
|
27
|
-
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename=t AND policyname=t||'_insert') THEN
|
|
28
|
-
EXECUTE format('CREATE POLICY %I ON public.%I FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()))', t||'_insert', t);
|
|
29
|
-
END IF;
|
|
30
|
-
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename=t AND policyname=t||'_update') THEN
|
|
31
|
-
EXECUTE format('CREATE POLICY %I ON public.%I FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t||'_update', t);
|
|
32
|
-
END IF;
|
|
33
|
-
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename=t AND policyname=t||'_delete') THEN
|
|
34
|
-
EXECUTE format('CREATE POLICY %I ON public.%I FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t||'_delete', t);
|
|
35
|
-
END IF;
|
|
36
|
-
END LOOP;
|
|
37
|
-
END $$;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
-- ---------------------------------------------------------------------------
|
|
2
|
-
-- Financial extract (extrato): net card settlement + transfers
|
|
3
|
-
-- ---------------------------------------------------------------------------
|
|
4
|
-
-- Adds the processing/MDR fee column used by the bank/cash account statement so
|
|
5
|
-
-- the running balance reflects the NET cash that actually lands in the account.
|
|
6
|
-
-- gross = paid_amount (settles the receivable/payable)
|
|
7
|
-
-- net cash = paid_amount - fee_amount (credits only; v1 has no fee on debits)
|
|
8
|
-
ALTER TABLE public.plg_financial_movements
|
|
9
|
-
ADD COLUMN IF NOT EXISTS fee_amount numeric(14,2) NOT NULL DEFAULT 0;
|
|
10
|
-
|
|
11
|
-
COMMENT ON COLUMN public.plg_financial_movements.fee_amount IS
|
|
12
|
-
'Processing/MDR fee deducted at settlement. Net cash impact = paid_amount - fee_amount (credit). v1: same-day net, no D+N settlement-date modeling.';
|
|
13
|
-
|
|
14
|
-
CREATE INDEX IF NOT EXISTS idx_plg_financial_movements_statement
|
|
15
|
-
ON public.plg_financial_movements (bank_account_id, payment_date)
|
|
16
|
-
WHERE status IN ('paid', 'partial');
|
|
17
|
-
|
|
18
|
-
-- Transfers between accounts = a debit row (source) + credit row (destination),
|
|
19
|
-
-- both status='paid', movement_kind='transfer', correlated via metadata jsonb:
|
|
20
|
-
-- metadata->>'transferId' | 'transferRole' ('out'|'in') | 'counterAccountId'
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
-- ---------------------------------------------------------------------------
|
|
2
|
-
-- Reconciliation (conciliação): bank-statement lines ↔ internal movements
|
|
3
|
-
-- ---------------------------------------------------------------------------
|
|
4
|
-
-- An integration (e.g. open banking) imports bank-statement lines as
|
|
5
|
-
-- plg_financial_movements tagged with (external_source, external_id). The matching
|
|
6
|
-
-- UI then links each imported line to the internal movement it settles, or
|
|
7
|
-
-- accepts it standalone. These columns make a movement carry its bank-line
|
|
8
|
-
-- identity and its reconciliation state.
|
|
9
|
-
-- external_source / external_id : the bank line's provider + unique id
|
|
10
|
-
-- reconciled_at : when the line was matched/accepted (NULL = pending)
|
|
11
|
-
-- matched_movement_id : the internal movement this line reconciles (NULL = standalone)
|
|
12
|
-
ALTER TABLE public.plg_financial_movements
|
|
13
|
-
ADD COLUMN IF NOT EXISTS external_id text,
|
|
14
|
-
ADD COLUMN IF NOT EXISTS external_source text,
|
|
15
|
-
ADD COLUMN IF NOT EXISTS reconciled_at timestamptz,
|
|
16
|
-
ADD COLUMN IF NOT EXISTS matched_movement_id uuid
|
|
17
|
-
REFERENCES public.plg_financial_movements(id) ON DELETE SET NULL;
|
|
18
|
-
|
|
19
|
-
COMMENT ON COLUMN public.plg_financial_movements.external_source IS
|
|
20
|
-
'Provider of an imported bank-statement line (e.g. plugbank, inter). NULL for app-native movements.';
|
|
21
|
-
COMMENT ON COLUMN public.plg_financial_movements.external_id IS
|
|
22
|
-
'Provider-unique id of the imported bank line. Forms the import idempotency key with external_source.';
|
|
23
|
-
|
|
24
|
-
-- Idempotency: a given bank line imports at most once per tenant.
|
|
25
|
-
CREATE UNIQUE INDEX IF NOT EXISTS uq_plg_financial_movements_external
|
|
26
|
-
ON public.plg_financial_movements (tenant_id, external_source, external_id)
|
|
27
|
-
WHERE external_id IS NOT NULL AND external_source IS NOT NULL;
|
|
28
|
-
|
|
29
|
-
-- Fast lookup of imported-but-unreconciled lines for the matching screen.
|
|
30
|
-
CREATE INDEX IF NOT EXISTS idx_plg_financial_movements_unreconciled
|
|
31
|
-
ON public.plg_financial_movements (tenant_id, payment_date)
|
|
32
|
-
WHERE external_source IS NOT NULL AND reconciled_at IS NULL;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
-- payment_method_type_id existed on pre-pool installs (added out-of-band on
|
|
2
|
-
-- the salon/beauty DB) but was never captured in the file series; 008 requires
|
|
3
|
-
-- it on plg_financial_movements. No-op where the column already exists.
|
|
4
|
-
|
|
5
|
-
ALTER TABLE public.plg_financial_movements
|
|
6
|
-
ADD COLUMN IF NOT EXISTS payment_method_type_id uuid REFERENCES public.plg_financial_payment_method_types(id);
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
-- Split bill (obligation) vs payment (cash event).
|
|
2
|
-
-- Each payment is now its own movement_kind='payment' row (one per cash event), so
|
|
3
|
-
-- two payments on one installment show as two extract rows instead of one summed row.
|
|
4
|
-
-- Bills stay pure obligations; v_invoice_balances already filters movement_kind='bill',
|
|
5
|
-
-- so the new payment rows don't affect invoice balances. Backfill is idempotent.
|
|
6
|
-
-- Requires fee_amount (006b_extract_fee_amount.sql).
|
|
7
|
-
|
|
8
|
-
INSERT INTO public.plg_financial_movements
|
|
9
|
-
(tenant_id, invoice_id, direction, movement_kind, amount, paid_amount, fee_amount, status,
|
|
10
|
-
due_date, payment_date, installment_number, payment_method_id, payment_method_type_id,
|
|
11
|
-
bank_account_id, cash_session_id, card_brand, card_installments, notes, metadata)
|
|
12
|
-
SELECT
|
|
13
|
-
b.tenant_id, b.invoice_id, b.direction, 'payment', b.paid_amount, b.paid_amount,
|
|
14
|
-
COALESCE(b.fee_amount, 0), 'paid',
|
|
15
|
-
b.due_date, COALESCE(b.payment_date, b.updated_at::date, CURRENT_DATE), b.installment_number,
|
|
16
|
-
b.payment_method_id, b.payment_method_type_id, b.bank_account_id, b.cash_session_id,
|
|
17
|
-
b.card_brand, b.card_installments, b.notes,
|
|
18
|
-
jsonb_build_object('backfilledFromBill', b.id)
|
|
19
|
-
FROM public.plg_financial_movements b
|
|
20
|
-
WHERE b.movement_kind = 'bill' AND b.paid_amount > 0
|
|
21
|
-
AND NOT EXISTS (
|
|
22
|
-
SELECT 1 FROM public.plg_financial_movements p
|
|
23
|
-
WHERE p.movement_kind = 'payment' AND p.metadata->>'backfilledFromBill' = b.id::text
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
UPDATE public.plg_financial_movements
|
|
27
|
-
SET payment_date = NULL, bank_account_id = NULL, payment_method_id = NULL,
|
|
28
|
-
payment_method_type_id = NULL, cash_session_id = NULL, card_brand = NULL, card_installments = NULL
|
|
29
|
-
WHERE movement_kind = 'bill';
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
-- ============================================================================
|
|
2
|
-
-- plugin-financial 009: server-plane agent write RPC.
|
|
3
|
-
--
|
|
4
|
-
-- public.agent_financial_mark_payment_received — "marca o atendimento como
|
|
5
|
-
-- pago em cartão 2x". Resolves the order (directly or via a booking id),
|
|
6
|
-
-- guard-checks the actor (agent_guard, spine 015), promotes the order into an
|
|
7
|
-
-- invoice + installment ledger through the EXISTING invariant function
|
|
8
|
-
-- fn_invoice_from_order (004) — never re-implementing its numbering/allocation
|
|
9
|
-
-- logic — then settles the installments following 008's split model (bill rows
|
|
10
|
-
-- flip to paid; each cash event is its own movement_kind='payment' row).
|
|
11
|
-
--
|
|
12
|
-
-- fn_invoice_from_order authorizes via user_tenant_ids() (auth.uid()). This
|
|
13
|
-
-- RPC runs under the broker's service key, so it impersonates the ALREADY
|
|
14
|
-
-- GUARD-VERIFIED actor for the duration of the transaction by setting
|
|
15
|
-
-- request.jwt.claims locally — the invariant function keeps working unchanged
|
|
16
|
-
-- and the audit trail carries the real acting user.
|
|
17
|
-
--
|
|
18
|
-
-- Contract: (p_tenant_id, p_actor_user_id, p_payload) → jsonb
|
|
19
|
-
-- payload {order_id? uuid, booking_id? uuid, method text
|
|
20
|
-
-- ('cash'|'pix'|'credit_card'|'debit_card'|'transfer'),
|
|
21
|
-
-- installments? int (credit_card only, 1–12), amount? numeric}
|
|
22
|
-
-- ============================================================================
|
|
23
|
-
|
|
24
|
-
CREATE OR REPLACE FUNCTION public.agent_financial_mark_payment_received(
|
|
25
|
-
p_tenant_id uuid,
|
|
26
|
-
p_actor_user_id uuid,
|
|
27
|
-
p_payload jsonb
|
|
28
|
-
) RETURNS jsonb
|
|
29
|
-
LANGUAGE plpgsql SECURITY DEFINER
|
|
30
|
-
SET search_path = public
|
|
31
|
-
AS $$
|
|
32
|
-
DECLARE
|
|
33
|
-
v_denial jsonb;
|
|
34
|
-
v_order_id uuid;
|
|
35
|
-
v_booking_id uuid;
|
|
36
|
-
v_method text;
|
|
37
|
-
v_installments int;
|
|
38
|
-
v_amount numeric;
|
|
39
|
-
v_order record;
|
|
40
|
-
v_bill record;
|
|
41
|
-
v_paid_count int := 0;
|
|
42
|
-
v_total_paid numeric := 0;
|
|
43
|
-
BEGIN
|
|
44
|
-
-- ── payload ──────────────────────────────────────────────────────────────
|
|
45
|
-
BEGIN
|
|
46
|
-
v_order_id := (p_payload->>'order_id')::uuid;
|
|
47
|
-
v_booking_id := (p_payload->>'booking_id')::uuid;
|
|
48
|
-
v_amount := (p_payload->>'amount')::numeric;
|
|
49
|
-
v_installments := COALESCE((p_payload->>'installments')::int, 1);
|
|
50
|
-
EXCEPTION WHEN OTHERS THEN
|
|
51
|
-
RETURN jsonb_build_object('ok', false, 'error', 'invalid payload: ' || SQLERRM);
|
|
52
|
-
END;
|
|
53
|
-
v_method := lower(COALESCE(p_payload->>'method', 'cash'));
|
|
54
|
-
IF v_method NOT IN ('cash', 'pix', 'credit_card', 'debit_card', 'transfer') THEN
|
|
55
|
-
RETURN jsonb_build_object('ok', false, 'error', 'unknown payment method');
|
|
56
|
-
END IF;
|
|
57
|
-
IF v_installments NOT BETWEEN 1 AND 12
|
|
58
|
-
OR (v_installments > 1 AND v_method <> 'credit_card') THEN
|
|
59
|
-
RETURN jsonb_build_object('ok', false, 'error', 'installments require credit_card (1-12)');
|
|
60
|
-
END IF;
|
|
61
|
-
|
|
62
|
-
-- booking → order
|
|
63
|
-
IF v_order_id IS NULL AND v_booking_id IS NOT NULL THEN
|
|
64
|
-
SELECT b.order_id INTO v_order_id
|
|
65
|
-
FROM appointments b
|
|
66
|
-
WHERE b.id = v_booking_id AND b.tenant_id = p_tenant_id;
|
|
67
|
-
END IF;
|
|
68
|
-
IF v_order_id IS NULL THEN
|
|
69
|
-
RETURN jsonb_build_object('ok', false, 'error', 'order_id or booking_id required (booking must have an order)');
|
|
70
|
-
END IF;
|
|
71
|
-
|
|
72
|
-
SELECT o.id, o.status, o.total INTO v_order
|
|
73
|
-
FROM orders o
|
|
74
|
-
WHERE o.id = v_order_id AND o.tenant_id = p_tenant_id;
|
|
75
|
-
IF NOT FOUND THEN
|
|
76
|
-
RETURN jsonb_build_object('ok', false, 'error', 'unknown order for this tenant');
|
|
77
|
-
END IF;
|
|
78
|
-
IF v_order.status = 'cancelled' THEN
|
|
79
|
-
RETURN jsonb_build_object('ok', false, 'error', 'cannot receive payment on a cancelled order');
|
|
80
|
-
END IF;
|
|
81
|
-
|
|
82
|
-
-- ── authorization ────────────────────────────────────────────────────────
|
|
83
|
-
v_denial := agent_guard(p_tenant_id, p_actor_user_id, 'financial.receivables', 'update');
|
|
84
|
-
IF v_denial IS NOT NULL THEN
|
|
85
|
-
RETURN jsonb_build_object('ok', false, 'denial', v_denial);
|
|
86
|
-
END IF;
|
|
87
|
-
|
|
88
|
-
-- already fully paid?
|
|
89
|
-
IF EXISTS (
|
|
90
|
-
SELECT 1 FROM plg_financial_movements m
|
|
91
|
-
WHERE m.invoice_id = v_order_id AND m.movement_kind = 'bill'
|
|
92
|
-
) AND NOT EXISTS (
|
|
93
|
-
SELECT 1 FROM plg_financial_movements m
|
|
94
|
-
WHERE m.invoice_id = v_order_id AND m.movement_kind = 'bill' AND m.status <> 'paid'
|
|
95
|
-
) THEN
|
|
96
|
-
RETURN jsonb_build_object('ok', false, 'error', 'order is already fully paid');
|
|
97
|
-
END IF;
|
|
98
|
-
|
|
99
|
-
-- ── invoice via the existing invariant function (impersonate the verified
|
|
100
|
-
-- actor so fn_invoice_from_order's user_tenant_ids() check passes) ─────
|
|
101
|
-
PERFORM set_config('request.jwt.claims',
|
|
102
|
-
jsonb_build_object('sub', p_actor_user_id, 'role', 'authenticated')::text,
|
|
103
|
-
true);
|
|
104
|
-
PERFORM fn_invoice_from_order(v_order_id, current_date, NULL, v_installments, 'credit');
|
|
105
|
-
|
|
106
|
-
-- ── settle: flip pending bill installments to paid + emit payment rows ───
|
|
107
|
-
FOR v_bill IN
|
|
108
|
-
SELECT m.* FROM plg_financial_movements m
|
|
109
|
-
WHERE m.invoice_id = v_order_id
|
|
110
|
-
AND m.tenant_id = p_tenant_id
|
|
111
|
-
AND m.movement_kind = 'bill'
|
|
112
|
-
AND m.status <> 'paid'
|
|
113
|
-
ORDER BY m.installment_number NULLS FIRST
|
|
114
|
-
LOOP
|
|
115
|
-
UPDATE plg_financial_movements SET
|
|
116
|
-
status = 'paid',
|
|
117
|
-
paid_amount = v_bill.amount,
|
|
118
|
-
payment_date = current_date,
|
|
119
|
-
card_installments = CASE WHEN v_method = 'credit_card' THEN v_installments ELSE NULL END,
|
|
120
|
-
metadata = COALESCE(metadata, '{}'::jsonb)
|
|
121
|
-
|| jsonb_build_object('paidVia', 'agent', 'method', v_method),
|
|
122
|
-
updated_at = now()
|
|
123
|
-
WHERE id = v_bill.id;
|
|
124
|
-
|
|
125
|
-
INSERT INTO plg_financial_movements
|
|
126
|
-
(tenant_id, invoice_id, direction, movement_kind, amount, paid_amount, status,
|
|
127
|
-
due_date, payment_date, installment_number, card_installments, metadata)
|
|
128
|
-
VALUES
|
|
129
|
-
(p_tenant_id, v_order_id, v_bill.direction, 'payment', v_bill.amount, v_bill.amount,
|
|
130
|
-
'paid', v_bill.due_date, current_date, v_bill.installment_number,
|
|
131
|
-
CASE WHEN v_method = 'credit_card' THEN v_installments ELSE NULL END,
|
|
132
|
-
jsonb_build_object('source', 'agent', 'method', v_method));
|
|
133
|
-
|
|
134
|
-
v_paid_count := v_paid_count + 1;
|
|
135
|
-
v_total_paid := v_total_paid + v_bill.amount;
|
|
136
|
-
END LOOP;
|
|
137
|
-
|
|
138
|
-
IF v_paid_count = 0 THEN
|
|
139
|
-
RETURN jsonb_build_object('ok', false, 'error', 'no open installments found to settle');
|
|
140
|
-
END IF;
|
|
141
|
-
|
|
142
|
-
INSERT INTO audit_logs (tenant_id, user_id, action, entity_type, entity_id, metadata)
|
|
143
|
-
VALUES (p_tenant_id, p_actor_user_id, 'agent.markPaymentReceived', 'order', v_order_id::text,
|
|
144
|
-
jsonb_build_object('payload', p_payload, 'installmentsSettled', v_paid_count,
|
|
145
|
-
'totalPaid', v_total_paid));
|
|
146
|
-
|
|
147
|
-
RETURN jsonb_build_object(
|
|
148
|
-
'ok', true,
|
|
149
|
-
'id', v_order_id,
|
|
150
|
-
'record', jsonb_build_object(
|
|
151
|
-
'ref', jsonb_build_object('id', v_order_id, 'resource', 'orders',
|
|
152
|
-
'archetype', 'order:service_order'),
|
|
153
|
-
'method', v_method,
|
|
154
|
-
'installments', v_installments,
|
|
155
|
-
'installments_settled', v_paid_count,
|
|
156
|
-
'total_paid', v_total_paid
|
|
157
|
-
)
|
|
158
|
-
);
|
|
159
|
-
END;
|
|
160
|
-
$$;
|
|
161
|
-
|
|
162
|
-
REVOKE ALL ON FUNCTION public.agent_financial_mark_payment_received(uuid, uuid, jsonb) FROM public;
|
|
163
|
-
REVOKE EXECUTE ON FUNCTION public.agent_financial_mark_payment_received(uuid, uuid, jsonb) FROM anon;
|
|
164
|
-
GRANT EXECUTE ON FUNCTION public.agent_financial_mark_payment_received(uuid, uuid, jsonb)
|
|
165
|
-
TO authenticated, service_role;
|