@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.
Files changed (95) hide show
  1. package/README.md +3 -3
  2. package/dist/components/PersonStatementWidget.d.ts +1 -1
  3. package/dist/components/PersonStatementWidget.d.ts.map +1 -1
  4. package/dist/{FinancialContext.d.ts → context.d.ts} +4 -2
  5. package/dist/context.d.ts.map +1 -0
  6. package/dist/data/registries.d.ts.map +1 -0
  7. package/dist/index.d.ts +13 -4
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1426 -1426
  10. package/dist/index.js.map +1 -1
  11. package/dist/integrations/stripe/connectorDef.d.ts +14 -0
  12. package/dist/integrations/stripe/connectorDef.d.ts.map +1 -0
  13. package/dist/integrations/stripe/data.d.ts +43 -0
  14. package/dist/integrations/stripe/data.d.ts.map +1 -0
  15. package/dist/lib/message-events.d.ts +12 -0
  16. package/dist/lib/message-events.d.ts.map +1 -0
  17. package/dist/{quick-add.d.ts → lib/quick-add.d.ts} +1 -1
  18. package/dist/lib/quick-add.d.ts.map +1 -0
  19. package/dist/locales/en.d.ts.map +1 -1
  20. package/dist/locales/pt-BR.d.ts.map +1 -1
  21. package/dist/{FinancialPage.d.ts → views/FinancialPage.d.ts} +3 -3
  22. package/dist/views/FinancialPage.d.ts.map +1 -0
  23. package/dist/views/InvoiceDetailView.d.ts.map +1 -1
  24. package/dist/views/InvoiceFormView.d.ts.map +1 -1
  25. package/dist/views/InvoiceListView.d.ts.map +1 -1
  26. package/dist/views/PayablesView.d.ts +1 -1
  27. package/dist/views/PayablesView.d.ts.map +1 -1
  28. package/dist/views/ReceivablesView.d.ts +1 -1
  29. package/dist/views/ReceivablesView.d.ts.map +1 -1
  30. package/dist/views/StatementsView.d.ts.map +1 -1
  31. package/dist/views/dashboardWidgets.d.ts +1 -1
  32. package/dist/views/dashboardWidgets.d.ts.map +1 -1
  33. package/package.json +12 -14
  34. package/dist/FinancialContext.d.ts.map +0 -1
  35. package/dist/FinancialPage.d.ts.map +0 -1
  36. package/dist/contract.test.d.ts +0 -2
  37. package/dist/contract.test.d.ts.map +0 -1
  38. package/dist/quick-add.d.ts.map +0 -1
  39. package/dist/registries.d.ts.map +0 -1
  40. package/src/FinancialContext.tsx +0 -59
  41. package/src/FinancialPage.tsx +0 -413
  42. package/src/README.md +0 -155
  43. package/src/components/FinancialGeneralSettings.tsx +0 -39
  44. package/src/components/FinancialOnboarding.tsx +0 -87
  45. package/src/components/FinancialSettings.tsx +0 -31
  46. package/src/components/PaymentModal.tsx +0 -369
  47. package/src/components/PersonStatementWidget.tsx +0 -133
  48. package/src/components/TransferModal.tsx +0 -150
  49. package/src/contract.test.ts +0 -58
  50. package/src/data/capability.test.ts +0 -135
  51. package/src/data/index.ts +0 -3
  52. package/src/data/mock.ts +0 -842
  53. package/src/data/supabase.ts +0 -968
  54. package/src/data/tables.ts +0 -13
  55. package/src/data/types.ts +0 -86
  56. package/src/index.ts +0 -487
  57. package/src/lib/tenant.ts +0 -3
  58. package/src/locales/en.ts +0 -493
  59. package/src/locales/index.ts +0 -7
  60. package/src/locales/pt-BR.ts +0 -493
  61. package/src/migrations/000_plg_rename.sql +0 -30
  62. package/src/migrations/001_financial_base.sql +0 -109
  63. package/src/migrations/002_chart_of_accounts.sql +0 -28
  64. package/src/migrations/003_card_brands.sql +0 -12
  65. package/src/migrations/004_order_to_cash.sql +0 -178
  66. package/src/migrations/005_seed_defaults.sql +0 -38
  67. package/src/migrations/006_rls_policies.sql +0 -37
  68. package/src/migrations/006b_extract_fee_amount.sql +0 -20
  69. package/src/migrations/007_reconciliation.sql +0 -32
  70. package/src/migrations/007b_movement_payment_method_type.sql +0 -6
  71. package/src/migrations/008_split_payment_movements.sql +0 -29
  72. package/src/migrations/009_agent_rpcs.sql +0 -165
  73. package/src/migrations/010_invoice_internal.sql +0 -133
  74. package/src/migrations/011_nfe.sql +0 -58
  75. package/src/migrations/index.ts +0 -923
  76. package/src/quick-add.ts +0 -58
  77. package/src/registries.ts +0 -308
  78. package/src/store.ts +0 -329
  79. package/src/types.ts +0 -592
  80. package/src/views/CardsView.tsx +0 -213
  81. package/src/views/CashRegistersView.tsx +0 -597
  82. package/src/views/CommissionsView.tsx +0 -18
  83. package/src/views/InvoiceDetailView.tsx +0 -616
  84. package/src/views/InvoiceFormView.tsx +0 -945
  85. package/src/views/InvoiceListView.tsx +0 -323
  86. package/src/views/NotasFiscaisView.tsx +0 -209
  87. package/src/views/PayablesView.tsx +0 -48
  88. package/src/views/QuickTransactionForm.tsx +0 -548
  89. package/src/views/ReceivablesView.tsx +0 -48
  90. package/src/views/ReconciliationView.tsx +0 -211
  91. package/src/views/StatementsView.tsx +0 -335
  92. package/src/views/SummaryView.tsx +0 -203
  93. package/src/views/dashboardWidgets.tsx +0 -437
  94. package/src/views/navReferrer.ts +0 -24
  95. /package/dist/{registries.d.ts → data/registries.d.ts} +0 -0
@@ -1,150 +0,0 @@
1
- import React, { useState, useEffect } from 'react'
2
- import { createPortal } from 'react-dom'
3
- import { X, ArrowRightLeft } from 'lucide-react'
4
- import { useFinancialConfig, useFinancialStore } from '../FinancialContext'
5
- import { useTranslation } from '@fayz-ai/core'
6
- import { CurrencyInput, DatePicker, Button } from '@fayz-ai/ui'
7
-
8
- export function TransferModal({ onClose, onDone, defaultFromId }: {
9
- onClose: () => void
10
- onDone: () => void
11
- defaultFromId?: string
12
- }) {
13
- const t = useTranslation()
14
- const { currency } = useFinancialConfig()
15
- const bankAccounts = useFinancialStore((s) => s.bankAccounts)
16
- const createTransfer = useFinancialStore((s) => s.createTransfer)
17
- const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts)
18
-
19
- const [fromAccountId, setFromAccountId] = useState(defaultFromId ?? '')
20
- const [toAccountId, setToAccountId] = useState('')
21
- const [amount, setAmount] = useState(0)
22
- const [date, setDate] = useState(new Date().toISOString().slice(0, 10))
23
- const [notes, setNotes] = useState('')
24
- const [saving, setSaving] = useState(false)
25
- const [visible, setVisible] = useState(false)
26
-
27
- useEffect(() => {
28
- fetchBankAccounts()
29
- requestAnimationFrame(() => setVisible(true))
30
- }, [])
31
-
32
- function handleClose() {
33
- setVisible(false)
34
- setTimeout(onClose, 200)
35
- }
36
-
37
- const sameAccount = !!fromAccountId && fromAccountId === toAccountId
38
- const canSubmit = !!fromAccountId && !!toAccountId && !sameAccount && amount > 0 && !saving
39
-
40
- // Enter anywhere in the modal submits (unless typing in a textarea / Shift+Enter).
41
- function handleKeyDown(e: React.KeyboardEvent) {
42
- if (e.key !== 'Enter' || e.shiftKey) return
43
- if ((e.target as HTMLElement).tagName === 'TEXTAREA') return
44
- e.preventDefault()
45
- if (canSubmit) void handleSubmit()
46
- }
47
-
48
- async function handleSubmit() {
49
- if (!canSubmit) return
50
- setSaving(true)
51
- try {
52
- await createTransfer({ fromAccountId, toAccountId, amount, date, notes: notes || undefined })
53
- setVisible(false)
54
- setTimeout(onDone, 200)
55
- } catch {
56
- // toast handled by store
57
- } finally {
58
- setSaving(false)
59
- }
60
- }
61
-
62
- const selectClass = 'w-full mt-1 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-3 py-2 text-sm'
63
-
64
- return createPortal(
65
- <div
66
- className="fixed inset-0 z-50 flex items-center justify-center transition-colors duration-200"
67
- style={{ backgroundColor: visible ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)' }}
68
- onClick={handleClose}
69
- >
70
- <div
71
- className="w-full max-w-md rounded-modal border bg-card shadow-lg mx-4 transition-all duration-200 max-h-[90vh] overflow-y-auto"
72
- style={{ opacity: visible ? 1 : 0, transform: visible ? 'scale(1) translateY(0)' : 'scale(0.95) translateY(8px)' }}
73
- onClick={(e) => e.stopPropagation()}
74
- onKeyDown={handleKeyDown}
75
- >
76
- {/* Header */}
77
- <div className="flex items-center justify-between px-5 py-4 border-b">
78
- <div className="flex items-center gap-2">
79
- <ArrowRightLeft className="h-4 w-4 text-warning" />
80
- <h3 className="text-base font-semibold">{t('financial.transfer.title')}</h3>
81
- </div>
82
- <button onClick={handleClose} className="p-1 text-muted-foreground hover:text-foreground transition-colors">
83
- <X className="h-4 w-4" />
84
- </button>
85
- </div>
86
-
87
- <div className="px-5 py-4 space-y-4">
88
- <div className="grid grid-cols-2 gap-3">
89
- <div>
90
- <label className="text-xs font-medium text-muted-foreground">{t('financial.transfer.from')}</label>
91
- <select value={fromAccountId} onChange={(e) => setFromAccountId(e.target.value)} className={selectClass}>
92
- <option value="">{t('financial.transfer.selectAccount')}</option>
93
- {bankAccounts.map((a) => <option key={a.id} value={a.id}>{a.name}</option>)}
94
- </select>
95
- </div>
96
- <div>
97
- <label className="text-xs font-medium text-muted-foreground">{t('financial.transfer.to')}</label>
98
- <select value={toAccountId} onChange={(e) => setToAccountId(e.target.value)} className={selectClass}>
99
- <option value="">{t('financial.transfer.selectAccount')}</option>
100
- {bankAccounts.map((a) => <option key={a.id} value={a.id}>{a.name}</option>)}
101
- </select>
102
- </div>
103
- </div>
104
-
105
- {sameAccount && (
106
- <p className="text-xs text-destructive">{t('financial.transfer.sameAccount')}</p>
107
- )}
108
-
109
- <div className="grid grid-cols-2 gap-3">
110
- <CurrencyInput
111
- label={t('financial.transfer.amount')}
112
- value={amount}
113
- onChange={setAmount}
114
- symbol={currency.symbol}
115
- locale={currency.locale}
116
- currencyCode={currency.code}
117
- />
118
- <div>
119
- <label className="text-xs font-medium text-muted-foreground">{t('financial.transfer.date')}</label>
120
- <DatePicker value={date} onChange={setDate} className="mt-1" />
121
- </div>
122
- </div>
123
-
124
- <div>
125
- <label className="text-xs font-medium text-muted-foreground">{t('financial.transfer.notes')}</label>
126
- <input
127
- type="text"
128
- value={notes}
129
- onChange={(e) => setNotes(e.target.value)}
130
- placeholder={t('financial.transfer.optionalNotes')}
131
- className="w-full mt-1 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
132
- />
133
- </div>
134
- </div>
135
-
136
- {/* Actions */}
137
- <div className="flex justify-end gap-2 px-5 py-4 border-t">
138
- <Button variant="outline" size="sm" onClick={handleClose}>
139
- {t('financial.transfer.cancel')}
140
- </Button>
141
- <Button variant="default" size="sm" onClick={handleSubmit} disabled={!canSubmit}>
142
- <ArrowRightLeft className="h-3.5 w-3.5" />
143
- {saving ? t('financial.transfer.processing') : t('financial.transfer.submit')}
144
- </Button>
145
- </div>
146
- </div>
147
- </div>,
148
- document.body,
149
- )
150
- }
@@ -1,58 +0,0 @@
1
- // Integrity test for the shared plugin-contract assertions (@fayz-ai/core/testing).
2
- // Exercises the gate's rules with inline manifests (no UI import) so the tool
3
- // itself is proven: a conformant manifest passes; each malformed shape throws.
4
- // Real plugins call assertPluginManifestContract(theirManifest) in an env where
5
- // their UI bundle loads (app-level / jsdom). See docs/PLUGIN-PATTERNS.md → integrity.
6
- import { describe, it, expect } from 'vitest'
7
- import { assertPluginManifestContract, PluginContractError } from '@fayz-ai/core/testing'
8
- import type { PluginManifest } from '@fayz-ai/core'
9
-
10
- const Dummy = () => null
11
-
12
- function manifest(overrides: Partial<PluginManifest> = {}): PluginManifest {
13
- return {
14
- id: 'demo', name: 'Demo', icon: 'Box', version: '1.0.0',
15
- navigation: [{ section: 'main', label: 'Demo', route: '/demo', icon: 'Box' } as any],
16
- routes: [{ path: '/demo', component: Dummy } as any],
17
- ...overrides,
18
- }
19
- }
20
-
21
- describe('assertPluginManifestContract', () => {
22
- it('passes a conformant page plugin', () => {
23
- expect(() => assertPluginManifestContract(manifest())).not.toThrow()
24
- })
25
-
26
- it('passes a settings-only plugin (nav/routes empty, one settings tab)', () => {
27
- expect(() => assertPluginManifestContract(manifest({
28
- navigation: [], routes: [],
29
- settings: [{ id: 'demo', label: 'Demo', icon: 'Box', component: Dummy } as any],
30
- }))).not.toThrow()
31
- })
32
-
33
- it('rejects a missing id', () => {
34
- expect(() => assertPluginManifestContract(manifest({ id: '' }))).toThrow(PluginContractError)
35
- })
36
-
37
- it('rejects a nav entry pointing at an undeclared route', () => {
38
- expect(() => assertPluginManifestContract(manifest({
39
- navigation: [{ section: 'main', label: 'X', route: '/nowhere', icon: 'Box' } as any],
40
- }))).toThrow(/no matching route/)
41
- })
42
-
43
- it('rejects duplicate route paths', () => {
44
- expect(() => assertPluginManifestContract(manifest({
45
- routes: [{ path: '/demo', component: Dummy } as any, { path: '/demo', component: Dummy } as any],
46
- }))).toThrow(/duplicate route/)
47
- })
48
-
49
- it('rejects a settings tab without a component', () => {
50
- expect(() => assertPluginManifestContract(manifest({
51
- settings: [{ id: 'demo', label: 'Demo' } as any],
52
- }))).toThrow(/component/)
53
- })
54
-
55
- it('rejects a plugin that contributes nothing mountable', () => {
56
- expect(() => assertPluginManifestContract(manifest({ navigation: [], routes: [], settings: [] }))).toThrow(/nothing/)
57
- })
58
- })
@@ -1,135 +0,0 @@
1
- // Capability + contract test for plugin-financial (Plugin Capability Contract).
2
- // Proves the data slice end-to-end against the mock provider — including the
3
- // ⚖️ reconciliation (conciliação) feature: import bank lines → list unreconciled
4
- // → suggest a match → reconcile → assert it leaves the pending set. Rows are
5
- // created manually (mock provider), which is exactly how a capability is proven
6
- // before/without a live backend. Mirrors plugins/plugin-tasks capability.test.ts.
7
- import { describe, it, expect } from 'vitest'
8
- import { createMockFinancialProvider } from './mock'
9
-
10
- describe('plugin-financial · invoice/movement slice (mock provider)', () => {
11
- it('persists an invoice and its installment movements', async () => {
12
- const provider = createMockFinancialProvider()
13
- const invoice = await provider.createInvoice({
14
- direction: 'credit',
15
- invoiceDate: '2026-06-01',
16
- contactName: 'Acme',
17
- items: [{ itemKind: 'other', description: 'Consulting', quantity: 1, unitPrice: 300 }],
18
- installments: [
19
- { direction: 'credit', movementKind: 'bill', amount: 300, dueDate: '2026-06-10', installmentNumber: 1 },
20
- ],
21
- })
22
- expect(invoice.id).toBeTruthy()
23
- expect(invoice.totalAmount).toBe(300)
24
-
25
- const movs = await provider.getMovements({ direction: 'credit' })
26
- expect(movs.data.some((m) => m.invoiceId === invoice.id && m.amount === 300)).toBe(true)
27
- })
28
-
29
- it('filters invoices by item account and cost center assignments', async () => {
30
- const provider = createMockFinancialProvider()
31
- const target = await provider.createInvoice({
32
- direction: 'debit',
33
- invoiceDate: '2026-06-01',
34
- contactName: 'Supplier A',
35
- items: [{
36
- itemKind: 'other',
37
- description: 'Supplies',
38
- quantity: 1,
39
- unitPrice: 120,
40
- accountId: 'coa-expenses',
41
- costCenterId: 'cc-salon',
42
- }],
43
- installments: [
44
- { direction: 'debit', movementKind: 'bill', amount: 120, dueDate: '2026-06-10', installmentNumber: 1 },
45
- ],
46
- })
47
- await provider.createInvoice({
48
- direction: 'debit',
49
- invoiceDate: '2026-06-01',
50
- contactName: 'Supplier B',
51
- items: [{
52
- itemKind: 'other',
53
- description: 'Marketing',
54
- quantity: 1,
55
- unitPrice: 80,
56
- accountId: 'coa-marketing',
57
- costCenterId: 'cc-marketing',
58
- }],
59
- installments: [
60
- { direction: 'debit', movementKind: 'bill', amount: 80, dueDate: '2026-06-10', installmentNumber: 1 },
61
- ],
62
- })
63
-
64
- const byAccount = await provider.getInvoices({ direction: 'debit', accountId: 'coa-expenses' })
65
- expect(byAccount.data.map((invoice) => invoice.id)).toEqual([target.id])
66
-
67
- const byCostCenter = await provider.getInvoices({ direction: 'debit', costCenterId: 'cc-salon' })
68
- expect(byCostCenter.data.map((invoice) => invoice.id)).toEqual([target.id])
69
-
70
- const byBoth = await provider.getInvoices({ direction: 'debit', accountId: 'coa-expenses', costCenterId: 'cc-marketing' })
71
- expect(byBoth.data).toHaveLength(0)
72
- })
73
- })
74
-
75
- describe('plugin-financial · ⚖️ reconciliation capability (mock provider)', () => {
76
- it('imports bank lines idempotently', async () => {
77
- const provider = createMockFinancialProvider()
78
- const lines = [
79
- { externalId: 'tx-1', externalSource: 'plugbank', direction: 'credit' as const, amount: 150, date: '2026-06-10', description: 'PIX recebido' },
80
- { externalId: 'tx-2', externalSource: 'plugbank', direction: 'debit' as const, amount: 80, date: '2026-06-11', description: 'Tarifa' },
81
- ]
82
- const first = await provider.importBankTransactions!({ lines })
83
- expect(first.imported).toBe(2)
84
- expect(first.duplicates).toBe(0)
85
-
86
- // Re-importing the same lines must not duplicate (idempotency by external id).
87
- const second = await provider.importBankTransactions!({ lines })
88
- expect(second.imported).toBe(0)
89
- expect(second.duplicates).toBe(2)
90
-
91
- const pending = await provider.getUnreconciled!()
92
- expect(pending).toHaveLength(2)
93
- expect(pending.every((m) => m.externalSource === 'plugbank' && !m.reconciledAt)).toBe(true)
94
- })
95
-
96
- it('suggests and matches an internal movement, leaving the pending set', async () => {
97
- const provider = createMockFinancialProvider()
98
- // An internal receivable the bank line should reconcile against.
99
- await provider.createInvoice({
100
- direction: 'credit',
101
- invoiceDate: '2026-06-08',
102
- contactName: 'Acme',
103
- items: [{ itemKind: 'other', description: 'Service', quantity: 1, unitPrice: 150 }],
104
- installments: [{ direction: 'credit', movementKind: 'bill', amount: 150, dueDate: '2026-06-10', installmentNumber: 1 }],
105
- })
106
- // The imported bank line (same amount, same direction, near date).
107
- await provider.importBankTransactions!({
108
- lines: [{ externalId: 'tx-1', externalSource: 'plugbank', direction: 'credit', amount: 150, date: '2026-06-10', description: 'PIX Acme' }],
109
- })
110
-
111
- const [bankLine] = await provider.getUnreconciled!()
112
- expect(bankLine).toBeTruthy()
113
-
114
- const candidates = await provider.suggestReconciliation!(bankLine.id)
115
- expect(candidates.length).toBeGreaterThan(0)
116
- expect(candidates[0].movement.amount).toBe(150)
117
- expect(candidates[0].score).toBeGreaterThan(0.5)
118
-
119
- await provider.reconcileMovement!({ bankMovementId: bankLine.id, matchedMovementId: candidates[0].movement.id })
120
-
121
- // The line is gone from the pending set and both sides are marked reconciled.
122
- const stillPending = await provider.getUnreconciled!()
123
- expect(stillPending.some((m) => m.id === bankLine.id)).toBe(false)
124
- })
125
-
126
- it('accepts a bank line standalone when there is no internal counterpart', async () => {
127
- const provider = createMockFinancialProvider()
128
- await provider.importBankTransactions!({
129
- lines: [{ externalId: 'tx-9', externalSource: 'plugbank', direction: 'debit', amount: 42, date: '2026-06-12', description: 'Taxa avulsa' }],
130
- })
131
- const [line] = await provider.getUnreconciled!()
132
- await provider.reconcileMovement!({ bankMovementId: line.id }) // no match → accept standalone
133
- expect(await provider.getUnreconciled!()).toHaveLength(0)
134
- })
135
- })
package/src/data/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export type { FinancialDataProvider } from './types'
2
- export { createMockFinancialProvider } from './mock'
3
- export type { MockFinancialSeed, MockFinancialProviderOptions } from './mock'