@fayz-ai/plugin-financial 0.1.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/dist/FinancialContext.d.ts +44 -0
- package/dist/FinancialContext.d.ts.map +1 -0
- package/dist/FinancialPage.d.ts +18 -0
- package/dist/FinancialPage.d.ts.map +1 -0
- package/dist/components/FinancialGeneralSettings.d.ts +3 -0
- package/dist/components/FinancialGeneralSettings.d.ts.map +1 -0
- package/dist/components/FinancialOnboarding.d.ts +5 -0
- package/dist/components/FinancialOnboarding.d.ts.map +1 -0
- package/dist/components/FinancialSettings.d.ts +8 -0
- package/dist/components/FinancialSettings.d.ts.map +1 -0
- package/dist/components/PaymentModal.d.ts +8 -0
- package/dist/components/PaymentModal.d.ts.map +1 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/mock.d.ts +9 -0
- package/dist/data/mock.d.ts.map +1 -0
- package/dist/data/supabase.d.ts +3 -0
- package/dist/data/supabase.d.ts.map +1 -0
- package/dist/data/types.d.ts +27 -0
- package/dist/data/types.d.ts.map +1 -0
- package/dist/index.cjs +4937 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4930 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/tenant.d.ts +3 -0
- package/dist/lib/tenant.d.ts.map +1 -0
- package/dist/locales/en.d.ts +2 -0
- package/dist/locales/en.d.ts.map +1 -0
- package/dist/locales/index.d.ts +2 -0
- package/dist/locales/index.d.ts.map +1 -0
- package/dist/locales/pt-BR.d.ts +2 -0
- package/dist/locales/pt-BR.d.ts.map +1 -0
- package/dist/registries.d.ts +3 -0
- package/dist/registries.d.ts.map +1 -0
- package/dist/store.d.ts +38 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/types.d.ts +302 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/views/CardsView.d.ts +3 -0
- package/dist/views/CardsView.d.ts.map +1 -0
- package/dist/views/CashRegistersView.d.ts +3 -0
- package/dist/views/CashRegistersView.d.ts.map +1 -0
- package/dist/views/CommissionsView.d.ts +3 -0
- package/dist/views/CommissionsView.d.ts.map +1 -0
- package/dist/views/InvoiceDetailView.d.ts +9 -0
- package/dist/views/InvoiceDetailView.d.ts.map +1 -0
- package/dist/views/InvoiceFormView.d.ts +13 -0
- package/dist/views/InvoiceFormView.d.ts.map +1 -0
- package/dist/views/InvoiceListView.d.ts +8 -0
- package/dist/views/InvoiceListView.d.ts.map +1 -0
- package/dist/views/PayablesView.d.ts +7 -0
- package/dist/views/PayablesView.d.ts.map +1 -0
- package/dist/views/ReceivablesView.d.ts +7 -0
- package/dist/views/ReceivablesView.d.ts.map +1 -0
- package/dist/views/StatementsView.d.ts +3 -0
- package/dist/views/StatementsView.d.ts.map +1 -0
- package/dist/views/SummaryView.d.ts +9 -0
- package/dist/views/SummaryView.d.ts.map +1 -0
- package/dist/views/dashboardWidgets.d.ts +11 -0
- package/dist/views/dashboardWidgets.d.ts.map +1 -0
- package/package.json +55 -0
- package/src/FinancialContext.tsx +47 -0
- package/src/FinancialPage.tsx +261 -0
- package/src/README.md +155 -0
- package/src/components/FinancialGeneralSettings.tsx +26 -0
- package/src/components/FinancialOnboarding.tsx +87 -0
- package/src/components/FinancialSettings.tsx +31 -0
- package/src/components/PaymentModal.tsx +322 -0
- package/src/data/index.ts +2 -0
- package/src/data/mock.ts +520 -0
- package/src/data/supabase.ts +509 -0
- package/src/data/types.ts +53 -0
- package/src/index.ts +307 -0
- package/src/lib/tenant.ts +3 -0
- package/src/locales/en.ts +331 -0
- package/src/locales/index.ts +7 -0
- package/src/locales/pt-BR.ts +331 -0
- package/src/migrations/001_financial_base.sql +109 -0
- package/src/migrations/002_chart_of_accounts.sql +28 -0
- package/src/migrations/003_card_brands.sql +12 -0
- package/src/migrations/004_order_to_cash.sql +164 -0
- package/src/migrations/005_seed_defaults.sql +38 -0
- package/src/registries.ts +305 -0
- package/src/store.ts +221 -0
- package/src/types.ts +359 -0
- package/src/views/CardsView.tsx +74 -0
- package/src/views/CashRegistersView.tsx +597 -0
- package/src/views/CommissionsView.tsx +18 -0
- package/src/views/InvoiceDetailView.tsx +595 -0
- package/src/views/InvoiceFormView.tsx +900 -0
- package/src/views/InvoiceListView.tsx +177 -0
- package/src/views/PayablesView.tsx +37 -0
- package/src/views/ReceivablesView.tsx +37 -0
- package/src/views/StatementsView.tsx +123 -0
- package/src/views/SummaryView.tsx +12 -0
- package/src/views/dashboardWidgets.tsx +191 -0
|
@@ -0,0 +1,38 @@
|
|
|
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 saas_core.tenants
|
|
12
|
+
WHERE id NOT IN (SELECT DISTINCT tenant_id FROM public.payment_method_types)
|
|
13
|
+
)
|
|
14
|
+
LOOP
|
|
15
|
+
INSERT INTO public.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 saas_core.tenants
|
|
30
|
+
WHERE id NOT IN (SELECT DISTINCT tenant_id FROM public.card_brands)
|
|
31
|
+
)
|
|
32
|
+
LOOP
|
|
33
|
+
INSERT INTO public.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 $$;
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import type { PluginRegistryDef } from '@fayz-ai/core'
|
|
2
|
+
import type { EntityDef } from '@fayz-ai/core'
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Payment Method Types (seeded — Cash, PIX, Credit Card, etc.)
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
const paymentMethodTypeEntity: EntityDef = {
|
|
9
|
+
name: 'Payment Type',
|
|
10
|
+
namePlural: 'Payment Types',
|
|
11
|
+
icon: 'CreditCard',
|
|
12
|
+
displayField: 'name',
|
|
13
|
+
defaultSort: 'name',
|
|
14
|
+
fields: [
|
|
15
|
+
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
16
|
+
{ key: 'transactionType', label: 'Transaction Type', type: 'select', options: ['cash', 'credit_card', 'debit_card', 'pix', 'bank_transfer', 'check', 'voucher'], showInTable: true, required: true },
|
|
17
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
18
|
+
],
|
|
19
|
+
data: { table: 'payment_method_types', tenantScoped: true },
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Payment Methods (user-created — e.g. "Visa 2x", "PIX Store")
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
const paymentMethodEntity: EntityDef = {
|
|
27
|
+
name: 'Payment Method',
|
|
28
|
+
namePlural: 'Payment Methods',
|
|
29
|
+
icon: 'CreditCard',
|
|
30
|
+
displayField: 'name',
|
|
31
|
+
defaultSort: 'name',
|
|
32
|
+
fields: [
|
|
33
|
+
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
34
|
+
{ key: 'paymentMethodTypeId', label: 'Type', type: 'select', options: ['cash', 'credit_card', 'debit_card', 'pix', 'bank_transfer', 'check'], showInTable: true, required: true },
|
|
35
|
+
{ key: 'discountValue', label: 'Discount %', type: 'number', showInTable: true },
|
|
36
|
+
{ key: 'interestValue', label: 'Interest %', type: 'number', showInTable: true },
|
|
37
|
+
{ key: 'minInstallments', label: 'Min Installments', type: 'number', defaultValue: 1 },
|
|
38
|
+
{ key: 'maxInstallments', label: 'Max Installments', type: 'number', defaultValue: 1, showInTable: true },
|
|
39
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
40
|
+
],
|
|
41
|
+
data: { table: 'payment_methods', tenantScoped: true },
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Chart of Accounts (tree structure)
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
const chartOfAccountsEntity: EntityDef = {
|
|
49
|
+
name: 'Ledger Account',
|
|
50
|
+
namePlural: 'Chart of Accounts',
|
|
51
|
+
icon: 'BookOpen',
|
|
52
|
+
displayField: 'name',
|
|
53
|
+
defaultSort: 'code',
|
|
54
|
+
fields: [
|
|
55
|
+
{ key: 'code', label: 'Code', type: 'text', required: true, showInTable: true },
|
|
56
|
+
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
57
|
+
{ key: 'nodeType', label: 'Type', type: 'select', options: ['group', 'leaf'], showInTable: true, required: true, defaultValue: 'leaf' },
|
|
58
|
+
{ key: 'parentId', label: 'Parent', type: 'text', showInTable: false },
|
|
59
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
60
|
+
],
|
|
61
|
+
data: { table: 'chart_of_accounts', tenantScoped: true },
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Cost Centers
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
const costCenterEntity: EntityDef = {
|
|
69
|
+
name: 'Cost Center',
|
|
70
|
+
namePlural: 'Cost Centers',
|
|
71
|
+
icon: 'Target',
|
|
72
|
+
displayField: 'name',
|
|
73
|
+
defaultSort: 'code',
|
|
74
|
+
fields: [
|
|
75
|
+
{ key: 'code', label: 'Code', type: 'text', required: true, showInTable: true },
|
|
76
|
+
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
77
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
78
|
+
],
|
|
79
|
+
data: { table: 'cost_centers', tenantScoped: true },
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Bank Accounts (physical cash, bank, credit card, digital wallet)
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
const bankAccountEntity: EntityDef = {
|
|
87
|
+
name: 'Bank Account',
|
|
88
|
+
namePlural: 'Bank Accounts',
|
|
89
|
+
icon: 'Landmark',
|
|
90
|
+
displayField: 'name',
|
|
91
|
+
defaultSort: 'name',
|
|
92
|
+
fields: [
|
|
93
|
+
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
94
|
+
{ key: 'accountType', label: 'Type', type: 'select', options: [
|
|
95
|
+
{ label: 'Bank Account', value: 'bank_account' },
|
|
96
|
+
{ label: 'Cash Register', value: 'cash_register' },
|
|
97
|
+
{ label: 'Credit Card', value: 'credit_card' },
|
|
98
|
+
{ label: 'Digital Wallet', value: 'digital_wallet' },
|
|
99
|
+
], showInTable: true, required: true },
|
|
100
|
+
{ key: 'bankName', label: 'Bank', type: 'text', showInTable: true },
|
|
101
|
+
{ key: 'accountNumber', label: 'Account Number', type: 'text' },
|
|
102
|
+
{ key: 'agencyNumber', label: 'Agency', type: 'text' },
|
|
103
|
+
{ key: 'initialBalance', label: 'Initial Balance', type: 'currency', defaultValue: 0 },
|
|
104
|
+
{ key: 'creditLimit', label: 'Credit Limit', type: 'currency' },
|
|
105
|
+
{ key: 'closingDay', label: 'Closing Day', type: 'number' },
|
|
106
|
+
{ key: 'dueDay', label: 'Due Day', type: 'number' },
|
|
107
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
108
|
+
],
|
|
109
|
+
data: { table: 'bank_accounts', tenantScoped: true },
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Card Brands (seeded)
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
const cardBrandEntity: EntityDef = {
|
|
117
|
+
name: 'Card Brand',
|
|
118
|
+
namePlural: 'Card Brands',
|
|
119
|
+
icon: 'CreditCard',
|
|
120
|
+
displayField: 'name',
|
|
121
|
+
defaultSort: 'name',
|
|
122
|
+
fields: [
|
|
123
|
+
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
124
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
125
|
+
],
|
|
126
|
+
data: { table: 'card_brands', tenantScoped: true },
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Commission Rules
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
const commissionRuleEntity: EntityDef = {
|
|
134
|
+
name: 'Commission Rule',
|
|
135
|
+
namePlural: 'Commission Rules',
|
|
136
|
+
icon: 'TrendingUp',
|
|
137
|
+
displayField: 'name',
|
|
138
|
+
defaultSort: 'priority',
|
|
139
|
+
defaultSortDir: 'desc',
|
|
140
|
+
fields: [
|
|
141
|
+
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
142
|
+
{ key: 'priority', label: 'Priority', type: 'number', required: true, showInTable: true, defaultValue: 1 },
|
|
143
|
+
{ key: 'valueType', label: 'Type', type: 'select', options: ['percentage', 'fixed'], required: true, showInTable: true, defaultValue: 'percentage' },
|
|
144
|
+
{ key: 'value', label: 'Value', type: 'number', required: true, showInTable: true },
|
|
145
|
+
{ key: 'description', label: 'Description', type: 'textarea' },
|
|
146
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
147
|
+
],
|
|
148
|
+
data: { table: 'commission_rules', tenantScoped: true },
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Price Tables
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
const priceTableEntity: EntityDef = {
|
|
156
|
+
name: 'Price Table',
|
|
157
|
+
namePlural: 'Price Tables',
|
|
158
|
+
icon: 'Tag',
|
|
159
|
+
displayField: 'description',
|
|
160
|
+
defaultSort: 'createdAt',
|
|
161
|
+
defaultSortDir: 'desc',
|
|
162
|
+
fields: [
|
|
163
|
+
{ key: 'description', label: 'Description', type: 'text', required: true, showInTable: true },
|
|
164
|
+
{ key: 'type', label: 'Type', type: 'select', options: ['sale', 'cost'], required: true, showInTable: true, defaultValue: 'sale' },
|
|
165
|
+
{ key: 'validFrom', label: 'Valid From', type: 'date', showInTable: true },
|
|
166
|
+
{ key: 'validUntil', label: 'Valid Until', type: 'date', showInTable: true },
|
|
167
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
168
|
+
],
|
|
169
|
+
data: { table: 'price_tables', tenantScoped: true },
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Price Variations
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
const priceVariationEntity: EntityDef = {
|
|
177
|
+
name: 'Price Variation',
|
|
178
|
+
namePlural: 'Price Variations',
|
|
179
|
+
icon: 'Percent',
|
|
180
|
+
displayField: 'name',
|
|
181
|
+
defaultSort: 'createdAt',
|
|
182
|
+
defaultSortDir: 'desc',
|
|
183
|
+
fields: [
|
|
184
|
+
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
185
|
+
{ key: 'variationType', label: 'Type', type: 'select', options: ['discount', 'increase'], required: true, showInTable: true },
|
|
186
|
+
{ key: 'valueType', label: 'Value Type', type: 'select', options: ['percentage', 'fixed'], required: true, showInTable: true },
|
|
187
|
+
{ key: 'value', label: 'Value', type: 'number', required: true, showInTable: true },
|
|
188
|
+
{ key: 'firstAppointmentOnly', label: 'First Appointment Only', type: 'boolean', defaultValue: false },
|
|
189
|
+
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
190
|
+
],
|
|
191
|
+
data: { table: 'price_variations', tenantScoped: true },
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Export all registries
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
export const financialRegistries: PluginRegistryDef[] = [
|
|
199
|
+
// --- Editable registries (user manages) ---
|
|
200
|
+
{
|
|
201
|
+
id: 'payment-methods',
|
|
202
|
+
entity: paymentMethodEntity,
|
|
203
|
+
icon: 'Wallet',
|
|
204
|
+
description: 'Configured payment methods with fees and installment rules',
|
|
205
|
+
display: 'table',
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: 'chart-of-accounts',
|
|
209
|
+
entity: chartOfAccountsEntity,
|
|
210
|
+
icon: 'BookOpen',
|
|
211
|
+
description: 'Hierarchical chart of accounts for categorizing transactions',
|
|
212
|
+
display: 'tree',
|
|
213
|
+
seedData: [
|
|
214
|
+
{ id: 'coa-1', code: '1', name: 'Revenue', nodeType: 'group', isActive: true },
|
|
215
|
+
{ id: 'coa-1.1', code: '1.1', name: 'Sales Revenue', nodeType: 'group', parentId: 'coa-1', isActive: true },
|
|
216
|
+
{ id: 'coa-1.1.01', code: '1.1.01', name: 'Services', nodeType: 'leaf', parentId: 'coa-1.1', isActive: true },
|
|
217
|
+
{ id: 'coa-1.1.02', code: '1.1.02', name: 'Products', nodeType: 'leaf', parentId: 'coa-1.1', isActive: true },
|
|
218
|
+
{ id: 'coa-2', code: '2', name: 'Other Revenue', nodeType: 'group', isActive: true },
|
|
219
|
+
{ id: 'coa-2.1', code: '2.1', name: 'Financial Income', nodeType: 'leaf', parentId: 'coa-2', isActive: true },
|
|
220
|
+
{ id: 'coa-3', code: '3', name: 'Operational Costs', nodeType: 'group', isActive: true },
|
|
221
|
+
{ id: 'coa-3.1', code: '3.1', name: 'Materials & Supplies', nodeType: 'group', parentId: 'coa-3', isActive: true },
|
|
222
|
+
{ id: 'coa-3.1.01', code: '3.1.01', name: 'Raw Materials', nodeType: 'leaf', parentId: 'coa-3.1', isActive: true },
|
|
223
|
+
{ id: 'coa-3.1.02', code: '3.1.02', name: 'Consumables', nodeType: 'leaf', parentId: 'coa-3.1', isActive: true },
|
|
224
|
+
{ id: 'coa-4', code: '4', name: 'Expenses', nodeType: 'group', isActive: true },
|
|
225
|
+
{ id: 'coa-4.1', code: '4.1', name: 'Rent', nodeType: 'leaf', parentId: 'coa-4', isActive: true },
|
|
226
|
+
{ id: 'coa-4.2', code: '4.2', name: 'Utilities', nodeType: 'leaf', parentId: 'coa-4', isActive: true },
|
|
227
|
+
{ id: 'coa-4.3', code: '4.3', name: 'Payroll', nodeType: 'leaf', parentId: 'coa-4', isActive: true },
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: 'cost-centers',
|
|
232
|
+
entity: costCenterEntity,
|
|
233
|
+
icon: 'Target',
|
|
234
|
+
description: 'Cost centers for expense tracking and departmental budgets',
|
|
235
|
+
display: 'table',
|
|
236
|
+
seedData: [
|
|
237
|
+
{ id: 'cc-001', code: '001', name: 'General', isActive: true },
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: 'bank-accounts',
|
|
242
|
+
entity: bankAccountEntity,
|
|
243
|
+
icon: 'Landmark',
|
|
244
|
+
description: 'Bank accounts, cash registers, credit cards, and digital wallets',
|
|
245
|
+
display: 'table',
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: 'commission-rules',
|
|
249
|
+
entity: commissionRuleEntity,
|
|
250
|
+
icon: 'TrendingUp',
|
|
251
|
+
description: 'Commission rules for service and product revenue sharing',
|
|
252
|
+
display: 'table',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
id: 'price-tables',
|
|
256
|
+
entity: priceTableEntity,
|
|
257
|
+
icon: 'Tag',
|
|
258
|
+
description: 'Price tables for sale and cost pricing',
|
|
259
|
+
display: 'table',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: 'price-variations',
|
|
263
|
+
entity: priceVariationEntity,
|
|
264
|
+
icon: 'Percent',
|
|
265
|
+
description: 'Discount and surcharge rules for pricing',
|
|
266
|
+
display: 'table',
|
|
267
|
+
},
|
|
268
|
+
// --- Read-only registries (system / seeded data) ---
|
|
269
|
+
{
|
|
270
|
+
id: 'payment-method-types',
|
|
271
|
+
entity: paymentMethodTypeEntity,
|
|
272
|
+
icon: 'CreditCard',
|
|
273
|
+
description: 'System payment types (cash, card, PIX, etc.)',
|
|
274
|
+
display: 'table',
|
|
275
|
+
readOnly: true,
|
|
276
|
+
seedData: [
|
|
277
|
+
{ id: 'pmt-cash', name: 'Cash', transactionType: 'cash', isActive: true },
|
|
278
|
+
{ id: 'pmt-pix', name: 'PIX', transactionType: 'pix', isActive: true },
|
|
279
|
+
{ id: 'pmt-credit', name: 'Credit Card', transactionType: 'credit_card', isActive: true },
|
|
280
|
+
{ id: 'pmt-debit', name: 'Debit Card', transactionType: 'debit_card', isActive: true },
|
|
281
|
+
{ id: 'pmt-transfer', name: 'Bank Transfer', transactionType: 'bank_transfer', isActive: true },
|
|
282
|
+
{ id: 'pmt-check', name: 'Check', transactionType: 'check', isActive: true },
|
|
283
|
+
],
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: 'card-brands',
|
|
287
|
+
entity: cardBrandEntity,
|
|
288
|
+
icon: 'CreditCard',
|
|
289
|
+
description: 'Credit and debit card brands',
|
|
290
|
+
display: 'table',
|
|
291
|
+
readOnly: true,
|
|
292
|
+
seedData: [
|
|
293
|
+
{ id: 'cb-visa', name: 'Visa', isActive: true },
|
|
294
|
+
{ id: 'cb-mastercard', name: 'Mastercard', isActive: true },
|
|
295
|
+
{ id: 'cb-amex', name: 'American Express', isActive: true },
|
|
296
|
+
{ id: 'cb-elo', name: 'Elo', isActive: true },
|
|
297
|
+
{ id: 'cb-hipercard', name: 'Hipercard', isActive: true },
|
|
298
|
+
{ id: 'cb-diners', name: 'Diners Club', isActive: true },
|
|
299
|
+
{ id: 'cb-discover', name: 'Discover', isActive: true },
|
|
300
|
+
{ id: 'cb-jcb', name: 'JCB', isActive: true },
|
|
301
|
+
{ id: 'cb-aura', name: 'Aura', isActive: true },
|
|
302
|
+
{ id: 'cb-hiper', name: 'Hiper', isActive: true },
|
|
303
|
+
],
|
|
304
|
+
},
|
|
305
|
+
]
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { createStore, type StoreApi } from 'zustand/vanilla'
|
|
2
|
+
import { dedup } from '@fayz-ai/saas'
|
|
3
|
+
import { toast } from '@fayz-ai/ui'
|
|
4
|
+
import type { FinancialDataProvider } from './data/types'
|
|
5
|
+
import type {
|
|
6
|
+
Invoice, FinancialMovement, BankAccount, CashSession,
|
|
7
|
+
PaymentMethod, PaymentMethodType, CardTransaction,
|
|
8
|
+
FinancialSummary, StatementEntry,
|
|
9
|
+
InvoiceQuery, MovementQuery, StatementQuery,
|
|
10
|
+
CreateInvoiceInput, PayMovementInput,
|
|
11
|
+
OpenCashSessionInput, CloseCashSessionInput,
|
|
12
|
+
DateRange,
|
|
13
|
+
} from './types'
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Store state
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
export interface FinancialUIState {
|
|
20
|
+
// Data cache
|
|
21
|
+
invoices: Invoice[]
|
|
22
|
+
invoicesTotal: number
|
|
23
|
+
invoicesLoading: boolean
|
|
24
|
+
invoiceQuery: InvoiceQuery
|
|
25
|
+
|
|
26
|
+
movements: FinancialMovement[]
|
|
27
|
+
movementsTotal: number
|
|
28
|
+
movementsLoading: boolean
|
|
29
|
+
|
|
30
|
+
bankAccounts: BankAccount[]
|
|
31
|
+
bankAccountsLoading: boolean
|
|
32
|
+
|
|
33
|
+
cashSessions: CashSession[]
|
|
34
|
+
cashSessionsLoading: boolean
|
|
35
|
+
|
|
36
|
+
paymentMethods: PaymentMethod[]
|
|
37
|
+
paymentMethodTypes: PaymentMethodType[]
|
|
38
|
+
|
|
39
|
+
summary: FinancialSummary | null
|
|
40
|
+
summaryLoading: boolean
|
|
41
|
+
|
|
42
|
+
statementEntries: StatementEntry[]
|
|
43
|
+
statementLoading: boolean
|
|
44
|
+
|
|
45
|
+
cardTransactions: CardTransaction[]
|
|
46
|
+
cardsLoading: boolean
|
|
47
|
+
|
|
48
|
+
// Actions
|
|
49
|
+
fetchSummary(dateRange?: DateRange): Promise<void>
|
|
50
|
+
fetchInvoices(query: InvoiceQuery): Promise<void>
|
|
51
|
+
fetchBankAccounts(): Promise<void>
|
|
52
|
+
fetchCashSessions(bankAccountId?: string): Promise<void>
|
|
53
|
+
fetchPaymentMethods(): Promise<void>
|
|
54
|
+
fetchStatement(query: StatementQuery): Promise<void>
|
|
55
|
+
fetchCardTransactions(dateRange?: DateRange): Promise<void>
|
|
56
|
+
createInvoice(input: CreateInvoiceInput): Promise<Invoice>
|
|
57
|
+
payMovement(input: PayMovementInput): Promise<void>
|
|
58
|
+
cancelInvoice(invoiceId: string): Promise<void>
|
|
59
|
+
openCashSession(input: OpenCashSessionInput): Promise<CashSession>
|
|
60
|
+
closeCashSession(input: CloseCashSessionInput): Promise<CashSession>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Store factory
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
export function createFinancialStore(provider: FinancialDataProvider): StoreApi<FinancialUIState> {
|
|
68
|
+
return createStore<FinancialUIState>((set, get) => ({
|
|
69
|
+
invoices: [],
|
|
70
|
+
invoicesTotal: 0,
|
|
71
|
+
invoicesLoading: false,
|
|
72
|
+
invoiceQuery: {},
|
|
73
|
+
|
|
74
|
+
movements: [],
|
|
75
|
+
movementsTotal: 0,
|
|
76
|
+
movementsLoading: false,
|
|
77
|
+
|
|
78
|
+
bankAccounts: [],
|
|
79
|
+
bankAccountsLoading: false,
|
|
80
|
+
|
|
81
|
+
cashSessions: [],
|
|
82
|
+
cashSessionsLoading: false,
|
|
83
|
+
|
|
84
|
+
paymentMethods: [],
|
|
85
|
+
paymentMethodTypes: [],
|
|
86
|
+
|
|
87
|
+
summary: null,
|
|
88
|
+
summaryLoading: false,
|
|
89
|
+
|
|
90
|
+
statementEntries: [],
|
|
91
|
+
statementLoading: false,
|
|
92
|
+
|
|
93
|
+
cardTransactions: [],
|
|
94
|
+
cardsLoading: false,
|
|
95
|
+
|
|
96
|
+
async fetchSummary(dateRange) {
|
|
97
|
+
return dedup('fin:summary', async () => {
|
|
98
|
+
set({ summaryLoading: true })
|
|
99
|
+
const summary = await provider.getSummary(dateRange)
|
|
100
|
+
set({ summary, summaryLoading: false })
|
|
101
|
+
})
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
async fetchInvoices(query) {
|
|
105
|
+
return dedup('fin:invoices:' + JSON.stringify(query), async () => {
|
|
106
|
+
set({ invoicesLoading: true, invoiceQuery: query })
|
|
107
|
+
const result = await provider.getInvoices(query)
|
|
108
|
+
set({ invoices: result.data, invoicesTotal: result.total, invoicesLoading: false })
|
|
109
|
+
})
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
async fetchBankAccounts() {
|
|
113
|
+
return dedup('fin:bankAccounts', async () => {
|
|
114
|
+
set({ bankAccountsLoading: true })
|
|
115
|
+
const bankAccounts = await provider.getBankAccounts()
|
|
116
|
+
set({ bankAccounts, bankAccountsLoading: false })
|
|
117
|
+
})
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
async fetchCashSessions(bankAccountId) {
|
|
121
|
+
return dedup('fin:cashSessions:' + (bankAccountId ?? ''), async () => {
|
|
122
|
+
set({ cashSessionsLoading: true })
|
|
123
|
+
const cashSessions = await provider.getCashSessions(bankAccountId)
|
|
124
|
+
set({ cashSessions, cashSessionsLoading: false })
|
|
125
|
+
})
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
async fetchPaymentMethods() {
|
|
129
|
+
return dedup('fin:paymentMethods', async () => {
|
|
130
|
+
const [paymentMethods, paymentMethodTypes] = await Promise.all([
|
|
131
|
+
provider.getPaymentMethods(),
|
|
132
|
+
provider.getPaymentMethodTypes(),
|
|
133
|
+
])
|
|
134
|
+
set({ paymentMethods, paymentMethodTypes })
|
|
135
|
+
})
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
async fetchStatement(query) {
|
|
139
|
+
return dedup('fin:statement:' + JSON.stringify(query), async () => {
|
|
140
|
+
set({ statementLoading: true })
|
|
141
|
+
const statementEntries = await provider.getStatement(query)
|
|
142
|
+
set({ statementEntries, statementLoading: false })
|
|
143
|
+
})
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
async fetchCardTransactions(dateRange) {
|
|
147
|
+
return dedup('fin:cards', async () => {
|
|
148
|
+
set({ cardsLoading: true })
|
|
149
|
+
const cardTransactions = await provider.getCardTransactions(dateRange)
|
|
150
|
+
set({ cardTransactions, cardsLoading: false })
|
|
151
|
+
})
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
async createInvoice(input) {
|
|
155
|
+
try {
|
|
156
|
+
const invoice = await provider.createInvoice(input)
|
|
157
|
+
const query = get().invoiceQuery
|
|
158
|
+
const result = await provider.getInvoices(query)
|
|
159
|
+
const summary = await provider.getSummary()
|
|
160
|
+
set({ invoices: result.data, invoicesTotal: result.total, summary })
|
|
161
|
+
toast.success('Invoice created')
|
|
162
|
+
return invoice
|
|
163
|
+
} catch (err: any) {
|
|
164
|
+
toast.error('Failed to create invoice', { description: err?.message })
|
|
165
|
+
throw err
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
async payMovement(input) {
|
|
170
|
+
try {
|
|
171
|
+
await provider.payMovement(input)
|
|
172
|
+
const query = get().invoiceQuery
|
|
173
|
+
const [result, summary] = await Promise.all([provider.getInvoices(query), provider.getSummary()])
|
|
174
|
+
set({ invoices: result.data, invoicesTotal: result.total, summary })
|
|
175
|
+
toast.success('Payment recorded')
|
|
176
|
+
} catch (err: any) {
|
|
177
|
+
toast.error('Failed to record payment', { description: err?.message })
|
|
178
|
+
throw err
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
async cancelInvoice(invoiceId) {
|
|
183
|
+
try {
|
|
184
|
+
await provider.cancelInvoice(invoiceId)
|
|
185
|
+
const query = get().invoiceQuery
|
|
186
|
+
const [result, summary] = await Promise.all([provider.getInvoices(query), provider.getSummary()])
|
|
187
|
+
set({ invoices: result.data, invoicesTotal: result.total, summary })
|
|
188
|
+
toast.success('Invoice cancelled')
|
|
189
|
+
} catch (err: any) {
|
|
190
|
+
toast.error('Failed to cancel invoice', { description: err?.message })
|
|
191
|
+
throw err
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
async openCashSession(input) {
|
|
196
|
+
try {
|
|
197
|
+
const session = await provider.openCashSession(input)
|
|
198
|
+
const cashSessions = await provider.getCashSessions()
|
|
199
|
+
set({ cashSessions })
|
|
200
|
+
toast.success('Cash session opened')
|
|
201
|
+
return session
|
|
202
|
+
} catch (err: any) {
|
|
203
|
+
toast.error('Failed to open session', { description: err?.message })
|
|
204
|
+
throw err
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
async closeCashSession(input) {
|
|
209
|
+
try {
|
|
210
|
+
const session = await provider.closeCashSession(input)
|
|
211
|
+
const cashSessions = await provider.getCashSessions()
|
|
212
|
+
set({ cashSessions })
|
|
213
|
+
toast.success('Cash session closed')
|
|
214
|
+
return session
|
|
215
|
+
} catch (err: any) {
|
|
216
|
+
toast.error('Failed to close session', { description: err?.message })
|
|
217
|
+
throw err
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
}))
|
|
221
|
+
}
|