@fayz-ai/plugin-financial 0.2.0 → 0.8.1
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 +2 -0
- package/dist/FinancialContext.d.ts +6 -0
- package/dist/FinancialContext.d.ts.map +1 -1
- package/dist/FinancialPage.d.ts.map +1 -1
- package/dist/components/FinancialGeneralSettings.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/tables.d.ts +11 -0
- package/dist/data/tables.d.ts.map +1 -0
- package/dist/index.cjs +345 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +347 -84
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +18 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/registries.d.ts.map +1 -1
- package/dist/views/CardsView.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/QuickTransactionForm.d.ts.map +1 -1
- package/package.json +10 -4
- package/src/FinancialContext.tsx +6 -0
- package/src/FinancialPage.tsx +56 -12
- package/src/README.md +5 -5
- package/src/components/FinancialGeneralSettings.tsx +21 -8
- package/src/contract.test.ts +1 -1
- package/src/data/supabase.ts +56 -48
- package/src/data/tables.ts +12 -0
- package/src/index.ts +128 -0
- package/src/locales/en.ts +10 -0
- package/src/locales/pt-BR.ts +10 -0
- package/src/migrations/000_plg_rename.sql +30 -0
- package/src/migrations/001_financial_base.sql +30 -30
- package/src/migrations/002_chart_of_accounts.sql +10 -10
- package/src/migrations/003_card_brands.sql +4 -4
- package/src/migrations/004_order_to_cash.sql +43 -29
- package/src/migrations/005_seed_defaults.sql +6 -6
- package/src/migrations/006_rls_policies.sql +5 -5
- package/src/migrations/{006_extract_fee_amount.sql → 006b_extract_fee_amount.sql} +4 -4
- package/src/migrations/007_reconciliation.sql +9 -9
- package/src/migrations/007b_movement_payment_method_type.sql +6 -0
- package/src/migrations/008_split_payment_movements.sql +5 -5
- package/src/migrations/009_agent_rpcs.sql +165 -0
- package/src/migrations/010_invoice_internal.sql +133 -0
- package/src/migrations/index.ts +862 -0
- package/src/registries.ts +8 -7
- package/src/views/CardsView.tsx +12 -9
- package/src/views/InvoiceFormView.tsx +10 -0
- package/src/views/InvoiceListView.tsx +98 -9
- package/src/views/QuickTransactionForm.tsx +11 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
-- Financial plugin — separate WHO MAY invoice from HOW an invoice is built.
|
|
2
|
+
--
|
|
3
|
+
-- fn_invoice_from_order (004) mixes the two: it checks that the caller is a
|
|
4
|
+
-- member of the order's tenant, then does the work. That check is right for a
|
|
5
|
+
-- human clicking "Faturar" in the admin, and impossible for anything else — a
|
|
6
|
+
-- storefront checkout runs as the SHOPPER, whose user_tenant_ids() is empty by
|
|
7
|
+
-- definition, so an order that should raise a receivable the moment it is placed
|
|
8
|
+
-- cannot raise one at all. SECURITY DEFINER does not help: auth.uid() still
|
|
9
|
+
-- belongs to the caller inside the definer context.
|
|
10
|
+
--
|
|
11
|
+
-- So the body moves to an internal function with no authorization of its own,
|
|
12
|
+
-- and fn_invoice_from_order keeps its signature, keeps its check, and delegates.
|
|
13
|
+
-- Callers that exist today (CRM approveQuote, the agenda bridge) are untouched.
|
|
14
|
+
-- The internal one is granted to NOBODY: it is reachable only from other
|
|
15
|
+
-- SECURITY DEFINER functions and triggers that have already decided the caller
|
|
16
|
+
-- is entitled — the shop's order-to-cash bridge being the first.
|
|
17
|
+
--
|
|
18
|
+
-- Idempotent.
|
|
19
|
+
|
|
20
|
+
CREATE OR REPLACE FUNCTION public.fn_invoice_from_order_internal(
|
|
21
|
+
p_order_id uuid,
|
|
22
|
+
p_due_date date DEFAULT NULL,
|
|
23
|
+
p_status text DEFAULT NULL,
|
|
24
|
+
p_installments int DEFAULT 1,
|
|
25
|
+
p_direction text DEFAULT 'credit'
|
|
26
|
+
) RETURNS uuid
|
|
27
|
+
LANGUAGE plpgsql
|
|
28
|
+
SECURITY DEFINER
|
|
29
|
+
SET search_path = public
|
|
30
|
+
AS $$
|
|
31
|
+
DECLARE
|
|
32
|
+
v_order public.orders%ROWTYPE;
|
|
33
|
+
v_ref text;
|
|
34
|
+
v_seq bigint;
|
|
35
|
+
v_summary text;
|
|
36
|
+
v_contact text;
|
|
37
|
+
v_existing uuid;
|
|
38
|
+
v_kind text;
|
|
39
|
+
v_prefix text;
|
|
40
|
+
v_n int := GREATEST(COALESCE(p_installments, 1), 1);
|
|
41
|
+
v_total numeric;
|
|
42
|
+
v_base numeric;
|
|
43
|
+
v_amt numeric;
|
|
44
|
+
v_alloc numeric := 0;
|
|
45
|
+
v_due date;
|
|
46
|
+
i int;
|
|
47
|
+
BEGIN
|
|
48
|
+
SELECT * INTO v_order FROM public.orders WHERE id = p_order_id;
|
|
49
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'fn_invoice_from_order: order % not found', p_order_id; END IF;
|
|
50
|
+
|
|
51
|
+
-- Idempotent: an order already carrying a bill is already invoiced.
|
|
52
|
+
SELECT id INTO v_existing FROM public.plg_financial_movements
|
|
53
|
+
WHERE invoice_id = p_order_id AND movement_kind = 'bill' LIMIT 1;
|
|
54
|
+
IF v_existing IS NOT NULL THEN RETURN p_order_id; END IF;
|
|
55
|
+
|
|
56
|
+
IF p_direction = 'debit' THEN v_kind := 'invoice_payable'; v_prefix := 'PAG';
|
|
57
|
+
ELSE v_kind := 'invoice_receivable'; v_prefix := 'REC'; END IF;
|
|
58
|
+
|
|
59
|
+
SELECT public.next_sequence(v_order.tenant_id, v_kind) INTO v_seq;
|
|
60
|
+
v_ref := v_prefix || '-' || lpad(v_seq::text, 5, '0');
|
|
61
|
+
|
|
62
|
+
SELECT string_agg(name, ', ' ORDER BY sort_order) INTO v_summary
|
|
63
|
+
FROM public.order_items WHERE order_id = p_order_id;
|
|
64
|
+
SELECT name INTO v_contact FROM public.people WHERE id = v_order.party_id;
|
|
65
|
+
|
|
66
|
+
UPDATE public.orders SET
|
|
67
|
+
status = COALESCE(p_status, status),
|
|
68
|
+
stage = 'invoiced',
|
|
69
|
+
direction = p_direction,
|
|
70
|
+
reference_number = v_ref,
|
|
71
|
+
metadata = COALESCE(metadata, '{}'::jsonb) || jsonb_build_object(
|
|
72
|
+
'itemsSummary', COALESCE(v_summary, ''),
|
|
73
|
+
'contactName', v_contact,
|
|
74
|
+
'installmentCount', v_n,
|
|
75
|
+
'quoteNumber', v_order.reference_number,
|
|
76
|
+
'direction', p_direction
|
|
77
|
+
),
|
|
78
|
+
updated_at = now()
|
|
79
|
+
WHERE id = p_order_id;
|
|
80
|
+
|
|
81
|
+
v_total := COALESCE(v_order.total, 0);
|
|
82
|
+
v_base := round(v_total / v_n, 2);
|
|
83
|
+
v_due := COALESCE(p_due_date, current_date);
|
|
84
|
+
FOR i IN 1..v_n LOOP
|
|
85
|
+
IF i = v_n THEN v_amt := round(v_total - v_alloc, 2);
|
|
86
|
+
ELSE v_amt := v_base; v_alloc := v_alloc + v_base; END IF;
|
|
87
|
+
INSERT INTO public.plg_financial_movements
|
|
88
|
+
(tenant_id, invoice_id, direction, movement_kind, amount, paid_amount, status, due_date, installment_number)
|
|
89
|
+
VALUES
|
|
90
|
+
(v_order.tenant_id, p_order_id, p_direction, 'bill', v_amt, 0, 'pending',
|
|
91
|
+
(v_due + ((i - 1) || ' months')::interval)::date, i);
|
|
92
|
+
END LOOP;
|
|
93
|
+
|
|
94
|
+
RETURN p_order_id;
|
|
95
|
+
END;
|
|
96
|
+
$$;
|
|
97
|
+
|
|
98
|
+
-- Deliberately NOT granted to authenticated/anon: entry is through
|
|
99
|
+
-- fn_invoice_from_order, or from another definer function that has already
|
|
100
|
+
-- established the caller's right to create this receivable.
|
|
101
|
+
REVOKE ALL ON FUNCTION public.fn_invoice_from_order_internal(uuid, date, text, int, text) FROM PUBLIC;
|
|
102
|
+
|
|
103
|
+
-- The public entry point: unchanged signature and behaviour, now a guard around
|
|
104
|
+
-- the shared body instead of a second copy of it.
|
|
105
|
+
CREATE OR REPLACE FUNCTION public.fn_invoice_from_order(
|
|
106
|
+
p_order_id uuid,
|
|
107
|
+
p_due_date date DEFAULT NULL,
|
|
108
|
+
p_status text DEFAULT NULL,
|
|
109
|
+
p_installments int DEFAULT 1,
|
|
110
|
+
p_direction text DEFAULT 'credit'
|
|
111
|
+
) RETURNS uuid
|
|
112
|
+
LANGUAGE plpgsql
|
|
113
|
+
SECURITY DEFINER
|
|
114
|
+
SET search_path = public
|
|
115
|
+
AS $$
|
|
116
|
+
DECLARE v_tenant uuid;
|
|
117
|
+
BEGIN
|
|
118
|
+
SELECT tenant_id INTO v_tenant FROM public.orders WHERE id = p_order_id;
|
|
119
|
+
IF v_tenant IS NULL THEN
|
|
120
|
+
RAISE EXCEPTION 'fn_invoice_from_order: order % not found', p_order_id;
|
|
121
|
+
END IF;
|
|
122
|
+
IF v_tenant NOT IN (SELECT public.user_tenant_ids()) THEN
|
|
123
|
+
RAISE EXCEPTION 'fn_invoice_from_order: forbidden';
|
|
124
|
+
END IF;
|
|
125
|
+
|
|
126
|
+
RETURN public.fn_invoice_from_order_internal(
|
|
127
|
+
p_order_id, p_due_date, p_status, p_installments, p_direction);
|
|
128
|
+
END;
|
|
129
|
+
$$;
|
|
130
|
+
|
|
131
|
+
GRANT EXECUTE ON FUNCTION public.fn_invoice_from_order(uuid, date, text, int, text) TO authenticated;
|
|
132
|
+
|
|
133
|
+
NOTIFY pgrst, 'reload schema';
|