@fayz-ai/plugin-financial 0.9.2 → 0.10.0

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 (47) hide show
  1. package/dist/components/PaymentModal.d.ts +3 -1
  2. package/dist/components/PaymentModal.d.ts.map +1 -1
  3. package/dist/components/ReceiptField.d.ts +17 -0
  4. package/dist/components/ReceiptField.d.ts.map +1 -0
  5. package/dist/context.d.ts +0 -2
  6. package/dist/context.d.ts.map +1 -1
  7. package/dist/data/mock.d.ts.map +1 -1
  8. package/dist/data/supabase.d.ts.map +1 -1
  9. package/dist/index.d.ts +0 -8
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +740 -762
  12. package/dist/index.js.map +1 -1
  13. package/dist/integrations/acquirers/shared.d.ts +15 -0
  14. package/dist/integrations/acquirers/shared.d.ts.map +1 -0
  15. package/dist/integrations/cielo/connectorDef.d.ts +6 -0
  16. package/dist/integrations/cielo/connectorDef.d.ts.map +1 -0
  17. package/dist/integrations/mercadopago/connectorDef.d.ts +5 -0
  18. package/dist/integrations/mercadopago/connectorDef.d.ts.map +1 -0
  19. package/dist/integrations/pagbank/connectorDef.d.ts +5 -0
  20. package/dist/integrations/pagbank/connectorDef.d.ts.map +1 -0
  21. package/dist/integrations/stone/connectorDef.d.ts +6 -0
  22. package/dist/integrations/stone/connectorDef.d.ts.map +1 -0
  23. package/dist/locales/en.d.ts.map +1 -1
  24. package/dist/locales/pt-BR.d.ts.map +1 -1
  25. package/dist/store.d.ts +1 -2
  26. package/dist/store.d.ts.map +1 -1
  27. package/dist/types.d.ts +6 -31
  28. package/dist/types.d.ts.map +1 -1
  29. package/dist/views/CardsView.d.ts +3 -1
  30. package/dist/views/CardsView.d.ts.map +1 -1
  31. package/dist/views/FinancialPage.d.ts +2 -0
  32. package/dist/views/FinancialPage.d.ts.map +1 -1
  33. package/dist/views/InvoiceDetailView.d.ts.map +1 -1
  34. package/dist/views/InvoiceFormView.d.ts +3 -1
  35. package/dist/views/InvoiceFormView.d.ts.map +1 -1
  36. package/dist/views/InvoiceListView.d.ts +3 -1
  37. package/dist/views/InvoiceListView.d.ts.map +1 -1
  38. package/dist/views/PayablesView.d.ts.map +1 -1
  39. package/dist/views/ReceivablesView.d.ts.map +1 -1
  40. package/dist/views/StatementsView.d.ts.map +1 -1
  41. package/package.json +7 -7
  42. package/dist/data/capability.test.d.ts +0 -2
  43. package/dist/data/capability.test.d.ts.map +0 -1
  44. package/dist/lib/quick-add.d.ts +0 -13
  45. package/dist/lib/quick-add.d.ts.map +0 -1
  46. package/dist/views/QuickTransactionForm.d.ts +0 -16
  47. package/dist/views/QuickTransactionForm.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
- import * as React18 from 'react';
2
- import React18__default, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
3
- import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, createSafeDataProvider, registerTranslations, registerMessageDefaults, useTranslation, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
4
- import { toast, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, DataTable, PageTransition, ModulePage, Button, Modal, ModalContent, DatePicker, DashboardCanvas, SubpageHeader, Skeleton, Card, FilterChip, Input, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, CurrencyInput, useSaveBar, SearchSelect, ListView, KpiCard, ChartWidget, CardHeader, CardTitle, CardContent, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
5
- import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, PermissionGate, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, useLimitGuard, invalidateLimit, PersonLink, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, OrderDetailSkeleton, OrderDetailBlock, OrderDetailView } from '@fayz-ai/admin';
6
- import { Wallet, Plus, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, X, CalendarDays, Paperclip, Camera, Tag, Repeat, ChevronDown, FileText, FileCheck2, ExternalLink, Send, Ban, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, Clock, ArrowLeftRight, CheckCheck, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, CircleAlert, CircleEllipsis, Pencil, LayoutList, Layers, Building2, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
1
+ import React17, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
2
+ import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, createSafeDataProvider, registerTranslations, registerMessageDefaults, useTranslation, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
3
+ import { toast, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, DataTable, PageTransition, ModulePage, Button, DashboardCanvas, SubpageHeader, Skeleton, PageHeaderActions, Card, FilterChip, DatePicker, Input, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, CurrencyInput, useSaveBar, SearchSelect, ListView, KpiCard, ChartWidget, CardHeader, CardTitle, CardContent, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
4
+ import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, PermissionGate, PersonLink, useLimitGuard, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, OrderDetailSkeleton, OrderDetailBlock, OrderDetailView, QuickActionsButton, invalidateLimit } from '@fayz-ai/admin';
5
+ import { Wallet, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, Plus, FileText, FileCheck2, ExternalLink, Send, Ban, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, ChevronDown, Clock, Paperclip, CheckCheck, X, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, CircleAlert, CircleEllipsis, Pencil, CalendarDays, LayoutList, Layers, Camera, Building2, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
7
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
7
  import { createPortal } from 'react-dom';
9
8
  import { createStore as createStore$1 } from 'zustand/vanilla';
@@ -160,489 +159,6 @@ function MobileSummary() {
160
159
  ] }, date)) })
161
160
  ] });
162
161
  }
163
- function typeTheme(type) {
164
- switch (type) {
165
- case "income":
166
- return { header: "bg-success text-success-foreground", ring: "ring-success", icon: ArrowDownCircle };
167
- case "transfer":
168
- return { header: "bg-muted text-foreground", ring: "ring-ring", icon: ArrowLeftRight };
169
- case "expense":
170
- default:
171
- return { header: "bg-destructive text-destructive-foreground", ring: "ring-destructive", icon: ArrowUpCircle };
172
- }
173
- }
174
- function ChipSelect({ icon: Icon, label, value, options, onChange, emptyLabel }) {
175
- const [open, setOpen] = useState(false);
176
- const ref = useRef(null);
177
- useEffect(() => {
178
- if (!open) return;
179
- function onDoc(e) {
180
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
181
- }
182
- document.addEventListener("mousedown", onDoc);
183
- return () => document.removeEventListener("mousedown", onDoc);
184
- }, [open]);
185
- const selected = options.find((o) => o.id === value);
186
- return /* @__PURE__ */ jsxs("div", { ref, className: "relative", children: [
187
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: label }),
188
- /* @__PURE__ */ jsxs(
189
- "button",
190
- {
191
- type: "button",
192
- onClick: () => setOpen((v) => !v),
193
- className: "mt-1 flex w-full items-center gap-2 rounded-input border border-input bg-card px-3 py-2.5 text-left text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] transition-colors hover:bg-muted/30",
194
- children: [
195
- /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
196
- /* @__PURE__ */ jsx("span", { className: `flex-1 truncate ${selected ? "" : "text-muted-foreground"}`, children: selected?.name ?? emptyLabel }),
197
- /* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })
198
- ]
199
- }
200
- ),
201
- open && /* @__PURE__ */ jsx("div", { className: "fayz-glass-surface absolute bottom-full left-0 right-0 z-50 mb-1 max-h-56 overflow-auto rounded-lg border bg-popover py-1 shadow-lg", children: options.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: emptyLabel }) : /* @__PURE__ */ jsxs(Fragment, { children: [
202
- value != null && /* @__PURE__ */ jsxs(
203
- "button",
204
- {
205
- onClick: () => {
206
- onChange(void 0);
207
- setOpen(false);
208
- },
209
- className: "flex w-full items-center gap-2 px-3 py-2.5 text-left text-xs text-muted-foreground hover:bg-muted/50",
210
- children: [
211
- /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" }),
212
- " ",
213
- emptyLabel
214
- ]
215
- }
216
- ),
217
- options.map((o) => /* @__PURE__ */ jsxs(
218
- "button",
219
- {
220
- onClick: () => {
221
- onChange(o.id);
222
- setOpen(false);
223
- },
224
- className: `flex w-full items-center gap-2 px-3 py-2.5 text-left text-sm hover:bg-muted/50 ${o.id === value ? "bg-muted/30" : ""}`,
225
- children: [
226
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.name }),
227
- o.hint && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: o.hint }),
228
- o.id === value && /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-primary" })
229
- ]
230
- },
231
- o.id
232
- ))
233
- ] }) })
234
- ] });
235
- }
236
- function TogglePill({ icon: Icon, label, checked, onChange, accent }) {
237
- return /* @__PURE__ */ jsxs(
238
- "button",
239
- {
240
- type: "button",
241
- onClick: () => onChange(!checked),
242
- className: `flex items-center gap-2 rounded-full border px-3 py-2 text-xs font-medium transition-colors ${checked ? `border-transparent ${accent ?? "bg-primary text-primary-foreground"}` : "border-input bg-card text-muted-foreground hover:bg-muted/40"}`,
243
- children: [
244
- /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
245
- label
246
- ]
247
- }
248
- );
249
- }
250
- function QuickTransactionForm({ open, onOpenChange, defaultType = "expense", defaultAccountId, focusReceipt = false }) {
251
- const t2 = useTranslation();
252
- const { currency } = useFinancialConfig();
253
- const provider = useFinancialProvider();
254
- const createQuickTransaction = useFinancialStore((s) => s.createQuickTransaction);
255
- const guardMovements = useLimitGuard("movements_month");
256
- const can = usePermissionOptional();
257
- const [type, setType] = useState(defaultType);
258
- const [amount, setAmount] = useState(0);
259
- const [amountStr, setAmountStr] = useState("");
260
- const [paid, setPaid] = useState(true);
261
- const [date, setDate] = useState(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
262
- const [description, setDescription] = useState("");
263
- const [categoryId, setCategoryId] = useState();
264
- const [accountId, setAccountId] = useState();
265
- const [toAccountId, setToAccountId] = useState();
266
- const [recurring, setRecurring] = useState(false);
267
- const [saving, setSaving] = useState(false);
268
- const [receiptUrl, setReceiptUrl] = useState();
269
- const fileInputRef = useRef(null);
270
- const [accounts, setAccounts] = useState([]);
271
- const [categories, setCategories] = useState([]);
272
- useEffect(() => {
273
- if (!open) return;
274
- setType(defaultType);
275
- setAmount(0);
276
- setAmountStr("");
277
- setPaid(true);
278
- setDate((/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
279
- setDescription("");
280
- setCategoryId(void 0);
281
- setAccountId(defaultAccountId);
282
- setToAccountId(void 0);
283
- setRecurring(false);
284
- setReceiptUrl(void 0);
285
- }, [open, defaultType, defaultAccountId]);
286
- useEffect(() => {
287
- if (open && focusReceipt) {
288
- const id = window.setTimeout(() => fileInputRef.current?.click(), 250);
289
- return () => window.clearTimeout(id);
290
- }
291
- }, [open, focusReceipt]);
292
- function onPickReceipt(e) {
293
- const file = e.target.files?.[0];
294
- e.target.value = "";
295
- if (!file) return;
296
- if (!file.type.startsWith("image/")) {
297
- toast.error(t2("financial.quickTx.receiptInvalid"));
298
- return;
299
- }
300
- const reader = new FileReader();
301
- reader.onload = () => {
302
- setReceiptUrl(typeof reader.result === "string" ? reader.result : void 0);
303
- };
304
- reader.onerror = () => toast.error(t2("financial.quickTx.receiptInvalid"));
305
- reader.readAsDataURL(file);
306
- }
307
- useEffect(() => {
308
- if (!open) return;
309
- let cancelled = false;
310
- void (async () => {
311
- const [accs, coa] = await Promise.all([
312
- provider.getBankAccounts(),
313
- provider.getChartOfAccounts().catch(() => [])
314
- ]);
315
- if (cancelled) return;
316
- setAccounts(accs);
317
- setCategories(coa);
318
- setAccountId((prev) => prev ?? accs[0]?.id);
319
- })();
320
- return () => {
321
- cancelled = true;
322
- };
323
- }, [open, provider]);
324
- const theme = typeTheme(type);
325
- const HeaderIcon = theme.icon;
326
- const accountOptions = useMemo(
327
- () => accounts.map((a) => ({ id: a.id, name: a.name, hint: a.accountType === "credit_card" ? t2("financial.quickTx.card") : void 0 })),
328
- [accounts, t2]
329
- );
330
- const categoryOptions = useMemo(
331
- () => categories.filter((c) => c.isActive).map((c) => ({ id: c.id, name: c.name, hint: c.code })),
332
- [categories]
333
- );
334
- function onAmountChange(raw) {
335
- const cleaned = raw.replace(/[^\d.,]/g, "").replace(",", ".");
336
- setAmountStr(raw.replace(/[^\d.,]/g, ""));
337
- const n = Number.parseFloat(cleaned);
338
- setAmount(Number.isFinite(n) ? n : 0);
339
- }
340
- const canSave = amount > 0 && !saving && (type === "transfer" ? !!accountId && !!toAccountId && accountId !== toAccountId : true);
341
- async function handleSave() {
342
- if (!can("financial", "create")) return;
343
- if (!canSave) {
344
- if (type === "transfer" && accountId && accountId === toAccountId) {
345
- toast.error(t2("financial.transfer.sameAccount"));
346
- }
347
- return;
348
- }
349
- if (await guardMovements(type === "transfer" ? 2 : 1) === "blocked") return;
350
- setSaving(true);
351
- try {
352
- await createQuickTransaction({
353
- type,
354
- amount,
355
- date,
356
- paid: type === "transfer" ? true : paid,
357
- description: description || void 0,
358
- categoryId: type === "transfer" ? void 0 : categoryId,
359
- bankAccountId: accountId,
360
- toAccountId: type === "transfer" ? toAccountId : void 0,
361
- recurring: type === "transfer" ? false : recurring,
362
- receiptUrl: type === "transfer" ? void 0 : receiptUrl
363
- });
364
- invalidateLimit("movements_month");
365
- onOpenChange(false);
366
- } catch {
367
- } finally {
368
- setSaving(false);
369
- }
370
- }
371
- const TYPES = [
372
- { id: "expense", label: t2("financial.quickTx.expense") },
373
- { id: "income", label: t2("financial.quickTx.income") },
374
- { id: "transfer", label: t2("financial.quickTx.transfer") }
375
- ];
376
- return /* @__PURE__ */ jsx(Modal, { open, onOpenChange, children: /* @__PURE__ */ jsxs(ModalContent, { size: "md", noPadding: true, hideClose: true, className: "overflow-hidden", children: [
377
- /* @__PURE__ */ jsxs("div", { className: `${theme.header} shrink-0 px-5 pb-5 pt-5`, children: [
378
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
379
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
380
- /* @__PURE__ */ jsx(HeaderIcon, { className: "h-5 w-5" }),
381
- /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: t2("financial.quickTx.title") })
382
- ] }),
383
- /* @__PURE__ */ jsx(
384
- "button",
385
- {
386
- onClick: () => onOpenChange(false),
387
- className: "flex h-7 w-7 items-center justify-center rounded-full opacity-80 transition-colors hover:bg-black/10 hover:opacity-100",
388
- "aria-label": t2("financial.quickTx.close"),
389
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
390
- }
391
- )
392
- ] }),
393
- /* @__PURE__ */ jsx("div", { className: "mt-4 flex gap-1 rounded-full bg-black/15 p-1", children: TYPES.map((ty) => /* @__PURE__ */ jsx(
394
- "button",
395
- {
396
- onClick: () => setType(ty.id),
397
- className: `flex-1 rounded-full px-2 py-1.5 text-xs font-medium transition-colors ${type === ty.id ? "bg-white/90 text-foreground shadow-sm" : "opacity-90 hover:bg-white/10"}`,
398
- children: ty.label
399
- },
400
- ty.id
401
- )) }),
402
- /* @__PURE__ */ jsxs("div", { className: "mt-5 flex items-baseline justify-center gap-2", children: [
403
- /* @__PURE__ */ jsx("span", { className: "text-xl font-medium opacity-90", children: currency.symbol }),
404
- /* @__PURE__ */ jsx(
405
- "input",
406
- {
407
- type: "text",
408
- inputMode: "decimal",
409
- autoFocus: true,
410
- value: amountStr,
411
- onChange: (e) => onAmountChange(e.target.value),
412
- onKeyDown: (e) => {
413
- if (e.key === "Enter") void handleSave();
414
- },
415
- placeholder: "0,00",
416
- className: "w-full max-w-[220px] bg-transparent text-center text-4xl font-bold tracking-tight outline-none placeholder:opacity-40"
417
- }
418
- )
419
- ] })
420
- ] }),
421
- /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto px-5 py-5", children: [
422
- type !== "transfer" && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-input border border-input bg-card px-3 py-2.5 shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)]", children: [
423
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: type === "income" ? t2("financial.quickTx.received") : t2("financial.quickTx.paid") }),
424
- /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
425
- /* @__PURE__ */ jsx(
426
- "button",
427
- {
428
- onClick: () => setPaid(true),
429
- className: `rounded-full px-3 py-1 text-xs font-medium transition-colors ${paid ? "bg-success text-success-foreground" : "bg-muted text-muted-foreground"}`,
430
- children: t2("financial.quickTx.yes")
431
- }
432
- ),
433
- /* @__PURE__ */ jsx(
434
- "button",
435
- {
436
- onClick: () => setPaid(false),
437
- className: `rounded-full px-3 py-1 text-xs font-medium transition-colors ${!paid ? "bg-warning text-warning-foreground" : "bg-muted text-muted-foreground"}`,
438
- children: t2("financial.quickTx.no")
439
- }
440
- )
441
- ] })
442
- ] }),
443
- /* @__PURE__ */ jsxs("div", { children: [
444
- /* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [
445
- /* @__PURE__ */ jsx(CalendarDays, { className: "h-3.5 w-3.5" }),
446
- " ",
447
- t2("financial.invoiceForm.date")
448
- ] }),
449
- /* @__PURE__ */ jsx(DatePicker, { value: date, onChange: setDate })
450
- ] }),
451
- /* @__PURE__ */ jsxs("div", { children: [
452
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: t2("financial.invoiceForm.description") }),
453
- /* @__PURE__ */ jsx(
454
- "input",
455
- {
456
- type: "text",
457
- value: description,
458
- onChange: (e) => setDescription(e.target.value),
459
- onKeyDown: (e) => {
460
- if (e.key === "Enter") void handleSave();
461
- },
462
- placeholder: t2("financial.quickTx.descriptionPlaceholder"),
463
- className: "mt-1 w-full rounded-input border border-input bg-card px-3 py-2.5 text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] outline-none focus:ring-2 focus:ring-ring"
464
- }
465
- )
466
- ] }),
467
- type !== "transfer" && /* @__PURE__ */ jsxs("div", { children: [
468
- /* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [
469
- /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }),
470
- " ",
471
- t2("financial.quickTx.receipt")
472
- ] }),
473
- /* @__PURE__ */ jsx(
474
- "input",
475
- {
476
- ref: fileInputRef,
477
- type: "file",
478
- accept: "image/*",
479
- capture: "environment",
480
- className: "hidden",
481
- onChange: onPickReceipt
482
- }
483
- ),
484
- receiptUrl ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-input border border-input bg-card p-2 shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)]", children: [
485
- /* @__PURE__ */ jsx(
486
- "img",
487
- {
488
- src: receiptUrl,
489
- alt: t2("financial.quickTx.receipt"),
490
- className: "h-16 w-16 shrink-0 rounded-md border object-cover"
491
- }
492
- ),
493
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
494
- /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: t2("financial.quickTx.receiptAttached") }),
495
- /* @__PURE__ */ jsx(
496
- "button",
497
- {
498
- type: "button",
499
- onClick: () => fileInputRef.current?.click(),
500
- className: "text-xs text-primary hover:underline",
501
- children: t2("financial.quickTx.receiptReplace")
502
- }
503
- )
504
- ] }),
505
- /* @__PURE__ */ jsx(
506
- "button",
507
- {
508
- type: "button",
509
- onClick: () => setReceiptUrl(void 0),
510
- "aria-label": t2("financial.quickTx.receiptRemove"),
511
- className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted",
512
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
513
- }
514
- )
515
- ] }) : /* @__PURE__ */ jsxs(
516
- "button",
517
- {
518
- type: "button",
519
- onClick: () => fileInputRef.current?.click(),
520
- className: `flex w-full items-center justify-center gap-2 rounded-input border border-dashed px-3 py-3 text-sm font-medium transition-colors hover:bg-muted/30 ${focusReceipt ? "border-primary text-primary ring-1 ring-primary/30" : "border-input text-muted-foreground"}`,
521
- children: [
522
- /* @__PURE__ */ jsx(Camera, { className: "h-4 w-4" }),
523
- " ",
524
- t2("financial.quickTx.receiptCapture")
525
- ]
526
- }
527
- )
528
- ] }),
529
- type === "transfer" ? /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3", children: [
530
- /* @__PURE__ */ jsx(
531
- ChipSelect,
532
- {
533
- icon: Wallet,
534
- label: t2("financial.quickTx.fromAccount"),
535
- value: accountId,
536
- options: accountOptions,
537
- onChange: setAccountId,
538
- emptyLabel: t2("financial.quickTx.selectAccount")
539
- }
540
- ),
541
- /* @__PURE__ */ jsx(
542
- ChipSelect,
543
- {
544
- icon: Wallet,
545
- label: t2("financial.quickTx.toAccount"),
546
- value: toAccountId,
547
- options: accountOptions.filter((o) => o.id !== accountId),
548
- onChange: setToAccountId,
549
- emptyLabel: t2("financial.quickTx.selectAccount")
550
- }
551
- )
552
- ] }) : /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3", children: [
553
- /* @__PURE__ */ jsx(
554
- ChipSelect,
555
- {
556
- icon: Tag,
557
- label: t2("financial.quickTx.category"),
558
- value: categoryId,
559
- options: categoryOptions,
560
- onChange: setCategoryId,
561
- emptyLabel: t2("financial.quickTx.selectCategory")
562
- }
563
- ),
564
- /* @__PURE__ */ jsx(
565
- ChipSelect,
566
- {
567
- icon: Wallet,
568
- label: t2("financial.quickTx.account"),
569
- value: accountId,
570
- options: accountOptions,
571
- onChange: setAccountId,
572
- emptyLabel: t2("financial.quickTx.selectAccount")
573
- }
574
- )
575
- ] }),
576
- type !== "transfer" && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
577
- /* @__PURE__ */ jsx(
578
- TogglePill,
579
- {
580
- icon: Repeat,
581
- label: t2("financial.quickTx.recurring"),
582
- checked: recurring,
583
- onChange: setRecurring
584
- }
585
- ),
586
- /* @__PURE__ */ jsxs(
587
- "button",
588
- {
589
- type: "button",
590
- onClick: () => fileInputRef.current?.click(),
591
- title: t2("financial.quickTx.attachment"),
592
- className: `flex items-center gap-2 rounded-full border px-3 py-2 text-xs font-medium transition-colors ${receiptUrl ? "border-transparent bg-primary text-primary-foreground" : "border-input bg-card text-muted-foreground hover:bg-muted/40"}`,
593
- children: [
594
- /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }),
595
- " ",
596
- t2("financial.quickTx.attachment"),
597
- receiptUrl && /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" })
598
- ]
599
- }
600
- )
601
- ] })
602
- ] }),
603
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-3 border-t px-5 py-3", children: [
604
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: amount > 0 ? formatCurrency(amount, currency) : t2("financial.quickTx.enterAmount") }),
605
- /* @__PURE__ */ jsxs(
606
- Button,
607
- {
608
- className: "ml-auto",
609
- onClick: () => void handleSave(),
610
- disabled: !canSave,
611
- children: [
612
- /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }),
613
- saving ? t2("financial.quickTx.saving") : t2("financial.quickTx.save")
614
- ]
615
- }
616
- )
617
- ] })
618
- ] }) });
619
- }
620
- var pending = null;
621
- var listeners = /* @__PURE__ */ new Set();
622
- function emit() {
623
- for (const l of listeners) l();
624
- }
625
- function openQuickAdd(type = "expense", receipt = false) {
626
- pending = { type, receipt };
627
- emit();
628
- }
629
- function consumeQuickAdd() {
630
- const req = pending;
631
- pending = null;
632
- return req;
633
- }
634
- function subscribe(cb) {
635
- listeners.add(cb);
636
- return () => {
637
- listeners.delete(cb);
638
- };
639
- }
640
- function getSnapshot() {
641
- return pending;
642
- }
643
- function usePendingQuickAdd() {
644
- return React18.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
645
- }
646
162
  var STATUS_OPTIONS = [
647
163
  { value: "open", labelKey: "financial.invoice.statusOpen", color: "bg-info-soft text-info-soft-foreground", icon: CircleDashed },
648
164
  { value: "partial", labelKey: "financial.invoice.statusPartial", color: "bg-warning-soft text-warning-soft-foreground", icon: CircleEllipsis },
@@ -728,10 +244,12 @@ function useInvoiceColumns(currency) {
728
244
  }
729
245
  ], [currency, t2]);
730
246
  }
731
- function InvoiceListView({ direction, onNew, onEdit }) {
247
+ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
732
248
  const t2 = useTranslation();
733
249
  const can = usePermissionOptional();
734
- const onNewGated = can("financial", "create") ? onNew : void 0;
250
+ const canCreate = can("financial", "create");
251
+ const onNewGated = canCreate ? onNew : void 0;
252
+ const onNewReceiptGated = canCreate ? onNewReceipt : void 0;
735
253
  const { currency } = useFinancialConfig();
736
254
  const provider = useFinancialProvider();
737
255
  const invoices = useFinancialStore((s) => s.invoices);
@@ -886,6 +404,29 @@ function InvoiceListView({ direction, onNew, onEdit }) {
886
404
  }
887
405
  )
888
406
  ] });
407
+ const newActions = onNewGated ? /* @__PURE__ */ jsx(
408
+ QuickActionsButton,
409
+ {
410
+ variant: "default",
411
+ label: t2("financial.invoice.new"),
412
+ actions: [
413
+ {
414
+ id: "manual",
415
+ label: t2("financial.invoice.newManual"),
416
+ description: t2("financial.invoice.newManualHint"),
417
+ icon: "FileText",
418
+ action: onNewGated
419
+ },
420
+ ...onNewReceiptGated ? [{
421
+ id: "receipt",
422
+ label: t2("financial.quickTx.sendReceipt"),
423
+ description: t2("financial.quickTx.receiptCapture"),
424
+ icon: "Camera",
425
+ action: onNewReceiptGated
426
+ }] : []
427
+ ]
428
+ }
429
+ ) : void 0;
889
430
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
890
431
  /* @__PURE__ */ jsx(
891
432
  ListView,
@@ -904,8 +445,7 @@ function InvoiceListView({ direction, onNew, onEdit }) {
904
445
  filters: listFilters,
905
446
  onExport: filtered.length > 0 ? exportRows : void 0,
906
447
  exportLabel: t2("crud.export.action"),
907
- newLabel: t2("financial.invoice.new"),
908
- onNew: onNewGated,
448
+ headerActions: newActions,
909
449
  onRowClick: (row) => onEdit?.(row.id),
910
450
  emptyIcon: FileText,
911
451
  emptyMessage: t2("financial.invoice.noInvoices"),
@@ -931,6 +471,126 @@ function InvoiceListView({ direction, onNew, onEdit }) {
931
471
  ] })
932
472
  ] });
933
473
  }
474
+ function useReceiptPicker(onPick) {
475
+ const t2 = useTranslation();
476
+ const ref = useRef(null);
477
+ const input = /* @__PURE__ */ jsx(
478
+ "input",
479
+ {
480
+ ref,
481
+ type: "file",
482
+ accept: "image/*",
483
+ capture: "environment",
484
+ className: "hidden",
485
+ onChange: (e) => {
486
+ const file = e.target.files?.[0];
487
+ e.target.value = "";
488
+ if (!file) return;
489
+ if (!file.type.startsWith("image/")) {
490
+ toast.error(t2("financial.quickTx.receiptInvalid"));
491
+ return;
492
+ }
493
+ const reader = new FileReader();
494
+ reader.onload = () => {
495
+ if (typeof reader.result === "string") onPick(reader.result);
496
+ };
497
+ reader.onerror = () => toast.error(t2("financial.quickTx.receiptInvalid"));
498
+ reader.readAsDataURL(file);
499
+ }
500
+ }
501
+ );
502
+ return { open: () => ref.current?.click(), input };
503
+ }
504
+ function ReceiptField({
505
+ value,
506
+ onChange,
507
+ autoOpen = false
508
+ }) {
509
+ const t2 = useTranslation();
510
+ const fileInputRef = useRef(null);
511
+ useEffect(() => {
512
+ if (!autoOpen) return;
513
+ const id = window.setTimeout(() => fileInputRef.current?.click(), 250);
514
+ return () => window.clearTimeout(id);
515
+ }, [autoOpen]);
516
+ function onPick(e) {
517
+ const file = e.target.files?.[0];
518
+ e.target.value = "";
519
+ if (!file) return;
520
+ if (!file.type.startsWith("image/")) {
521
+ toast.error(t2("financial.quickTx.receiptInvalid"));
522
+ return;
523
+ }
524
+ const reader = new FileReader();
525
+ reader.onload = () => {
526
+ onChange(typeof reader.result === "string" ? reader.result : void 0);
527
+ };
528
+ reader.onerror = () => toast.error(t2("financial.quickTx.receiptInvalid"));
529
+ reader.readAsDataURL(file);
530
+ }
531
+ return /* @__PURE__ */ jsxs("div", { children: [
532
+ /* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [
533
+ /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }),
534
+ " ",
535
+ t2("financial.quickTx.receipt")
536
+ ] }),
537
+ /* @__PURE__ */ jsx(
538
+ "input",
539
+ {
540
+ ref: fileInputRef,
541
+ type: "file",
542
+ accept: "image/*",
543
+ capture: "environment",
544
+ className: "hidden",
545
+ onChange: onPick
546
+ }
547
+ ),
548
+ value ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-input border border-input bg-card p-2 shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)]", children: [
549
+ /* @__PURE__ */ jsx(
550
+ "img",
551
+ {
552
+ src: value,
553
+ alt: t2("financial.quickTx.receipt"),
554
+ className: "h-16 w-16 shrink-0 rounded-md border object-cover"
555
+ }
556
+ ),
557
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
558
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: t2("financial.quickTx.receiptAttached") }),
559
+ /* @__PURE__ */ jsx(
560
+ "button",
561
+ {
562
+ type: "button",
563
+ onClick: () => fileInputRef.current?.click(),
564
+ className: "text-xs text-primary hover:underline",
565
+ children: t2("financial.quickTx.receiptReplace")
566
+ }
567
+ )
568
+ ] }),
569
+ /* @__PURE__ */ jsx(
570
+ "button",
571
+ {
572
+ type: "button",
573
+ onClick: () => onChange(void 0),
574
+ "aria-label": t2("financial.quickTx.receiptRemove"),
575
+ className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted",
576
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
577
+ }
578
+ )
579
+ ] }) : /* @__PURE__ */ jsxs(
580
+ "button",
581
+ {
582
+ type: "button",
583
+ onClick: () => fileInputRef.current?.click(),
584
+ className: `flex w-full items-center justify-center gap-2 rounded-input border border-dashed px-3 py-3 text-sm font-medium transition-colors hover:bg-muted/30 ${autoOpen ? "border-primary text-primary ring-1 ring-primary/30" : "border-input text-muted-foreground"}`,
585
+ children: [
586
+ /* @__PURE__ */ jsx(Camera, { className: "h-4 w-4" }),
587
+ " ",
588
+ t2("financial.quickTx.receiptCapture")
589
+ ]
590
+ }
591
+ )
592
+ ] });
593
+ }
934
594
  var ACCOUNT_TYPES_FOR_TX = {
935
595
  cash: ["cash_register"],
936
596
  credit_card: ["credit_card", "bank_account"],
@@ -964,7 +624,7 @@ var ICON_COLORS = {
964
624
  bank_transfer: "text-warning",
965
625
  check: "text-muted-foreground"
966
626
  };
967
- function PaymentModal({ movement, onClose, onPaid }) {
627
+ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
968
628
  const t2 = useTranslation();
969
629
  const { currency } = useFinancialConfig();
970
630
  const payMovement = useFinancialStore((s) => s.payMovement);
@@ -1035,6 +695,8 @@ function PaymentModal({ movement, onClose, onPaid }) {
1035
695
  setSaving(true);
1036
696
  try {
1037
697
  await payMovement({
698
+ // O comprovante viaja com o pagamento — é dele que ele é prova.
699
+ metadata: receiptUrl ? { receiptUrl } : void 0,
1038
700
  movementId: movement.id,
1039
701
  amount,
1040
702
  paymentDate,
@@ -1311,18 +973,28 @@ var formId = 1;
1311
973
  function fid() {
1312
974
  return `f${formId++}`;
1313
975
  }
1314
- function InvoiceFormView({ direction, editId, onSaved }) {
976
+ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1315
977
  const t2 = useTranslation();
1316
978
  const config = useFinancialConfig();
1317
979
  const { currency, itemTypes, locations } = config;
1318
980
  const provider = useFinancialProvider();
1319
981
  const hasItemLookups = itemTypes.some((tp) => config.entityLookups[tp.value]);
1320
982
  const defaultItemKind = itemTypes.find((tp) => tp.value === "other")?.value ?? itemTypes.find((tp) => !config.entityLookups[tp.value])?.value ?? itemTypes[0]?.value ?? "other";
983
+ const bankAccounts = useFinancialStore((s) => s.bankAccounts);
984
+ const paymentMethodTypes = useFinancialStore((s) => s.paymentMethodTypes);
985
+ const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
986
+ const fetchPaymentMethods = useFinancialStore((s) => s.fetchPaymentMethods);
987
+ const payMovement = useFinancialStore((s) => s.payMovement);
1321
988
  const createInvoice = useFinancialStore((s) => s.createInvoice);
1322
989
  const guardMovements = useLimitGuard("movements_month");
1323
990
  const can = usePermissionOptional();
1324
991
  const hasLocations = locations.length > 0;
1325
992
  const isEdit = !!editId;
993
+ useEffect(() => {
994
+ if (isEdit) return;
995
+ void fetchBankAccounts();
996
+ void fetchPaymentMethods();
997
+ }, [isEdit]);
1326
998
  const [loaded, setLoaded] = useState(!editId);
1327
999
  const [loading, setLoading] = useState(!!editId);
1328
1000
  const [contactName, setContactName] = useState("");
@@ -1443,6 +1115,10 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1443
1115
  function removeItem(id) {
1444
1116
  setItems(items.filter((it) => it._id !== id));
1445
1117
  }
1118
+ useEffect(() => {
1119
+ if (isEdit || totalAmount <= 0 || installments.length > 0) return;
1120
+ generateInstallments(1);
1121
+ }, [isEdit, totalAmount, installments.length]);
1446
1122
  function generateInstallments(count) {
1447
1123
  if (count < 1 || totalAmount <= 0) return;
1448
1124
  const perInstallment = Math.floor(totalAmount * 100 / count) / 100;
@@ -1466,6 +1142,10 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1466
1142
  toast.error(t2("common.formIncomplete"));
1467
1143
  return;
1468
1144
  }
1145
+ if (!isEdit && installments.some((i) => i.receiptUrl && (!i.payMethodTypeId || !i.payAccountId))) {
1146
+ toast.error(t2("financial.invoiceForm.paidNeedsMethod"));
1147
+ return;
1148
+ }
1469
1149
  if (!isEdit && await guardMovements(Math.max(1, installments.length)) === "blocked") return;
1470
1150
  setSaving(true);
1471
1151
  try {
@@ -1485,6 +1165,11 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1485
1165
  installmentNumber: 1
1486
1166
  });
1487
1167
  }
1168
+ installments.forEach((inst, i) => {
1169
+ if (inst.receiptUrl && movInputs[i]) {
1170
+ movInputs[i] = { ...movInputs[i], metadata: { ...movInputs[i].metadata ?? {}, receiptUrl: inst.receiptUrl } };
1171
+ }
1172
+ });
1488
1173
  if (isEdit && editId) {
1489
1174
  await provider.updateInvoice(editId, {
1490
1175
  invoiceDate,
@@ -1509,6 +1194,22 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1509
1194
  installments: movInputs
1510
1195
  });
1511
1196
  invalidateLimit("movements_month");
1197
+ const paidOnes = installments.map((inst, i) => ({ inst, number: i + 1 })).filter(({ inst }) => inst.receiptUrl);
1198
+ if (paidOnes.length > 0) {
1199
+ const movements = await provider.getMovements({ direction, status: "pending" });
1200
+ const bills = movements.data.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
1201
+ for (const { inst, number } of paidOnes) {
1202
+ const bill = bills.find((m) => (m.installmentNumber ?? 1) === number);
1203
+ if (!bill) continue;
1204
+ await payMovement({
1205
+ movementId: bill.id,
1206
+ amount: bill.amount,
1207
+ paymentDate: inst.dueDate,
1208
+ paymentMethodTypeId: inst.payMethodTypeId || void 0,
1209
+ bankAccountId: inst.payAccountId || void 0
1210
+ });
1211
+ }
1212
+ }
1512
1213
  onSaved?.(invoice.id);
1513
1214
  }
1514
1215
  } finally {
@@ -1578,26 +1279,64 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1578
1279
  },
1579
1280
  n
1580
1281
  )) }),
1581
- installments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: totalAmount <= 0 ? t2("financial.invoiceForm.addItemsFirst") : t2("financial.invoiceForm.selectInstallments") }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: installments.map((inst, i) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 rounded border p-2.5", children: [
1582
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1583
- /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-muted-foreground", children: [
1584
- "#",
1585
- i + 1
1282
+ installments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: totalAmount <= 0 ? t2("financial.invoiceForm.addItemsFirst") : t2("financial.invoiceForm.selectInstallments") }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: installments.map((inst, i) => {
1283
+ const patch = (next) => {
1284
+ const updated = [...installments];
1285
+ updated[i] = { ...inst, ...next };
1286
+ setInstallments(updated);
1287
+ };
1288
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 rounded border p-2.5", children: [
1289
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1290
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-muted-foreground", children: [
1291
+ "#",
1292
+ i + 1
1293
+ ] }),
1294
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: money(inst.amount) })
1586
1295
  ] }),
1587
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: money(inst.amount) })
1588
- ] }),
1589
- /* @__PURE__ */ jsx(
1590
- DatePicker,
1591
- {
1592
- value: inst.dueDate,
1593
- onChange: (v) => {
1594
- const updated = [...installments];
1595
- updated[i] = { ...inst, dueDate: v };
1596
- setInstallments(updated);
1296
+ /* @__PURE__ */ jsx(
1297
+ DatePicker,
1298
+ {
1299
+ value: inst.dueDate,
1300
+ onChange: (v) => patch({ dueDate: v })
1597
1301
  }
1598
- }
1599
- )
1600
- ] }, inst._id)) })
1302
+ ),
1303
+ /* @__PURE__ */ jsx(
1304
+ ReceiptField,
1305
+ {
1306
+ value: inst.receiptUrl,
1307
+ onChange: (url) => patch({ receiptUrl: url }),
1308
+ autoOpen: focusReceipt && i === 0 && !inst.receiptUrl
1309
+ }
1310
+ ),
1311
+ inst.receiptUrl && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 border-t pt-1.5", children: [
1312
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium text-success", children: t2("financial.invoiceForm.willBePaid") }),
1313
+ /* @__PURE__ */ jsxs(
1314
+ "select",
1315
+ {
1316
+ value: inst.payMethodTypeId ?? "",
1317
+ onChange: (e) => patch({ payMethodTypeId: e.target.value }),
1318
+ className: "w-full rounded-input border border-input bg-card px-2 py-1.5 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
1319
+ children: [
1320
+ /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectMethod") }),
1321
+ paymentMethodTypes.map((tp) => /* @__PURE__ */ jsx("option", { value: tp.id, children: tp.name }, tp.id))
1322
+ ]
1323
+ }
1324
+ ),
1325
+ /* @__PURE__ */ jsxs(
1326
+ "select",
1327
+ {
1328
+ value: inst.payAccountId ?? (bankAccounts.length === 1 ? bankAccounts[0].id : ""),
1329
+ onChange: (e) => patch({ payAccountId: e.target.value }),
1330
+ className: "w-full rounded-input border border-input bg-card px-2 py-1.5 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
1331
+ children: [
1332
+ /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectAccount") }),
1333
+ bankAccounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
1334
+ ]
1335
+ }
1336
+ )
1337
+ ] })
1338
+ ] }, inst._id);
1339
+ }) })
1601
1340
  ] }) });
1602
1341
  const scheduleSection = isEdit ? /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1603
1342
  hasPayments && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.invoiceForm.scheduleLocked") }),
@@ -1667,6 +1406,13 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1667
1406
  const results = await config.contactLookup.search(q);
1668
1407
  return results.map((r) => ({ id: r.id, label: r.label, subtitle: r.subtitle, group: r.group, data: r }));
1669
1408
  },
1409
+ createLabel: t2("financial.invoiceForm.createContact"),
1410
+ clearLabel: t2("financial.invoiceForm.clear"),
1411
+ onCreate: config.contactLookup?.create ? async (name) => {
1412
+ const created = await config.contactLookup.create(name);
1413
+ setContactId(created.id);
1414
+ setContactName(created.label);
1415
+ } : void 0,
1670
1416
  placeholder: t2("financial.invoiceForm.searchType")
1671
1417
  }
1672
1418
  ) }),
@@ -1854,6 +1600,13 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1854
1600
  const [payments, setPayments] = useState([]);
1855
1601
  const [loading, setLoading] = useState(true);
1856
1602
  const [payingMovement, setPayingMovement] = useState(null);
1603
+ const [receiptFor, setReceiptFor] = useState(null);
1604
+ const [pendingReceipt, setPendingReceipt] = useState();
1605
+ const receiptPicker = useReceiptPicker((dataUrl) => {
1606
+ setPendingReceipt(dataUrl);
1607
+ setPayingMovement(receiptFor);
1608
+ setReceiptFor(null);
1609
+ });
1857
1610
  const [expandedMovId, setExpandedMovId] = useState(null);
1858
1611
  const [menuOpen, setMenuOpen] = useState(false);
1859
1612
  const [confirmCancel, setConfirmCancel] = useState(false);
@@ -2053,6 +1806,20 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
2053
1806
  mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
2054
1807
  ] })
2055
1808
  ] }),
1809
+ !isPaid && invoice.status !== "cancelled" && /* @__PURE__ */ jsx(
1810
+ "button",
1811
+ {
1812
+ onClick: (e) => {
1813
+ e.stopPropagation();
1814
+ setReceiptFor(mov);
1815
+ receiptPicker.open();
1816
+ },
1817
+ title: t2("financial.quickTx.receiptCapture"),
1818
+ "aria-label": t2("financial.quickTx.receiptCapture"),
1819
+ className: "inline-flex shrink-0 items-center gap-1 rounded-lg border bg-card px-2.5 py-1.5 text-xs font-medium shadow-button transition-colors hover:bg-muted active:shadow-button-inset",
1820
+ children: /* @__PURE__ */ jsx(Camera, { className: "h-3 w-3" })
1821
+ }
1822
+ ),
2056
1823
  !isPaid && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
2057
1824
  "button",
2058
1825
  {
@@ -2153,13 +1920,19 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
2153
1920
  extraSections: sideBlocks
2154
1921
  }
2155
1922
  ),
1923
+ receiptPicker.input,
2156
1924
  payingMovement && /* @__PURE__ */ jsx(
2157
1925
  PaymentModal,
2158
1926
  {
2159
1927
  movement: payingMovement,
2160
- onClose: () => setPayingMovement(null),
1928
+ receiptUrl: pendingReceipt,
1929
+ onClose: () => {
1930
+ setPayingMovement(null);
1931
+ setPendingReceipt(void 0);
1932
+ },
2161
1933
  onPaid: () => {
2162
1934
  setPayingMovement(null);
1935
+ setPendingReceipt(void 0);
2163
1936
  loadData();
2164
1937
  }
2165
1938
  }
@@ -2212,7 +1985,7 @@ function referrerLabel(view, t2) {
2212
1985
  function PayablesView({ intent, onNavigate, previousView, back }) {
2213
1986
  const t2 = useTranslation();
2214
1987
  if (intent.mode === "new") {
2215
- return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", onSaved: (id) => onNavigate(id ? `payables-detail:${id}` : "payables-list") });
1988
+ return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", focusReceipt: intent.receipt, onSaved: (id) => onNavigate(id ? `payables-detail:${id}` : "payables-list") });
2216
1989
  }
2217
1990
  if (intent.mode === "edit" && intent.editId) {
2218
1991
  return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", editId: intent.editId, onSaved: () => onNavigate(`payables-detail:${intent.editId}`) });
@@ -2235,6 +2008,7 @@ function PayablesView({ intent, onNavigate, previousView, back }) {
2235
2008
  {
2236
2009
  direction: "debit",
2237
2010
  onNew: () => onNavigate("payables-new"),
2011
+ onNewReceipt: () => onNavigate("payables-new-receipt"),
2238
2012
  onEdit: (id) => onNavigate(`payables-detail:${id}`)
2239
2013
  }
2240
2014
  );
@@ -2242,7 +2016,7 @@ function PayablesView({ intent, onNavigate, previousView, back }) {
2242
2016
  function ReceivablesView({ intent, onNavigate, previousView, back }) {
2243
2017
  const t2 = useTranslation();
2244
2018
  if (intent.mode === "new") {
2245
- return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", onSaved: (id) => onNavigate(id ? `receivables-detail:${id}` : "receivables-list") });
2019
+ return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", focusReceipt: intent.receipt, onSaved: (id) => onNavigate(id ? `receivables-detail:${id}` : "receivables-list") });
2246
2020
  }
2247
2021
  if (intent.mode === "edit" && intent.editId) {
2248
2022
  return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", editId: intent.editId, onSaved: () => onNavigate(`receivables-detail:${intent.editId}`) });
@@ -2265,6 +2039,7 @@ function ReceivablesView({ intent, onNavigate, previousView, back }) {
2265
2039
  {
2266
2040
  direction: "credit",
2267
2041
  onNew: () => onNavigate("receivables-new"),
2042
+ onNewReceipt: () => onNavigate("receivables-new-receipt"),
2268
2043
  onEdit: (id) => onNavigate(`receivables-detail:${id}`)
2269
2044
  }
2270
2045
  );
@@ -3083,7 +2858,7 @@ function StatementsView({ onNavigate }) {
3083
2858
  );
3084
2859
  };
3085
2860
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3086
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center justify-end gap-3", children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setShowTransfer(true), disabled: bankAccounts.length < 2, children: [
2861
+ /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setShowTransfer(true), disabled: bankAccounts.length < 2, children: [
3087
2862
  /* @__PURE__ */ jsx(ArrowRightLeft, { className: "h-3.5 w-3.5" }),
3088
2863
  t2("financial.statements.newTransfer")
3089
2864
  ] }) }),
@@ -3183,15 +2958,13 @@ function cardMath(acc) {
3183
2958
  const pct = limit > 0 ? Math.min(100, used / limit * 100) : 0;
3184
2959
  return { limit, used, available, pct };
3185
2960
  }
3186
- function CardsView() {
2961
+ function CardsView({ onNavigate }) {
3187
2962
  const t2 = useTranslation();
3188
2963
  const { currency } = useFinancialConfig();
3189
2964
  const bankAccounts = useFinancialStore((s) => s.bankAccounts);
3190
2965
  const bankAccountsLoading = useFinancialStore((s) => s.bankAccountsLoading);
3191
2966
  const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
3192
2967
  const [tab, setTab] = useState("open");
3193
- const [addOpen, setAddOpen] = useState(false);
3194
- const [addCardId, setAddCardId] = useState();
3195
2968
  useEffect(() => {
3196
2969
  fetchBankAccounts();
3197
2970
  }, []);
@@ -3212,9 +2985,8 @@ function CardsView() {
3212
2985
  }
3213
2986
  return { totalAvailable, totalBills };
3214
2987
  }, [openCards]);
3215
- function openAddExpense(cardId) {
3216
- setAddCardId(cardId);
3217
- setAddOpen(true);
2988
+ function openAddExpense(_cardId) {
2989
+ onNavigate?.("payables-new");
3218
2990
  }
3219
2991
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3220
2992
  /* @__PURE__ */ jsx(SubpageHeader, { title: t2("financial.cards.title"), subtitle: t2("financial.cards.subtitle") }),
@@ -3301,16 +3073,7 @@ function CardsView() {
3301
3073
  ] })
3302
3074
  ] }, card.id);
3303
3075
  }) })
3304
- ] }),
3305
- /* @__PURE__ */ jsx(
3306
- QuickTransactionForm,
3307
- {
3308
- open: addOpen,
3309
- onOpenChange: setAddOpen,
3310
- defaultType: "expense",
3311
- defaultAccountId: addCardId
3312
- }
3313
- )
3076
+ ] })
3314
3077
  ] });
3315
3078
  }
3316
3079
  function ReconciliationView() {
@@ -3692,8 +3455,12 @@ function FinancialOnboarding({ onComplete }) {
3692
3455
  }
3693
3456
  function parseIntent(activeView) {
3694
3457
  const { base, id } = parseViewId(activeView);
3695
- const m = base.match(/^(payables|receivables)-(new|list|detail|edit)$/);
3696
- if (m) return { view: m[1], mode: m[2], editId: id };
3458
+ const m = base.match(/^(payables|receivables)-(new-receipt|new|list|detail|edit)$/);
3459
+ if (m) {
3460
+ const raw = m[2];
3461
+ const mode = raw === "new-receipt" ? "new" : raw;
3462
+ return { view: m[1], mode, editId: id, receipt: raw === "new-receipt" };
3463
+ }
3697
3464
  return { view: activeView };
3698
3465
  }
3699
3466
  function buildNav(config, activeView, navigate, t2) {
@@ -3713,7 +3480,7 @@ function buildNav(config, activeView, navigate, t2) {
3713
3480
  icon: "ArrowUpCircle",
3714
3481
  active: activeView.startsWith("payables"),
3715
3482
  children: [
3716
- { id: "payables-new", label: t2("financial.nav.new"), active: activeView === "payables-new", onClick: () => navigate("payables-new") },
3483
+ { id: "payables-new", label: t2("financial.nav.new"), active: activeView.startsWith("payables-new"), onClick: () => navigate("payables-new") },
3717
3484
  { id: "payables-list", label: t2("financial.nav.list"), active: activeView === "payables-list", onClick: () => navigate("payables-list") },
3718
3485
  { id: "payables-recurring", label: t2("financial.nav.recurringExpenses"), active: activeView === "payables-recurring", onClick: () => navigate("payables-recurring") }
3719
3486
  ]
@@ -3726,7 +3493,7 @@ function buildNav(config, activeView, navigate, t2) {
3726
3493
  icon: "ArrowDownCircle",
3727
3494
  active: activeView.startsWith("receivables"),
3728
3495
  children: [
3729
- { id: "receivables-new", label: t2("financial.nav.new"), active: activeView === "receivables-new", onClick: () => navigate("receivables-new") },
3496
+ { id: "receivables-new", label: t2("financial.nav.new"), active: activeView.startsWith("receivables-new"), onClick: () => navigate("receivables-new") },
3730
3497
  { id: "receivables-list", label: t2("financial.nav.list"), active: activeView === "receivables-list", onClick: () => navigate("receivables-list") }
3731
3498
  ]
3732
3499
  });
@@ -3793,7 +3560,7 @@ function buildNav(config, activeView, navigate, t2) {
3793
3560
  }
3794
3561
  return items;
3795
3562
  }
3796
- function FinancialPage({ config, provider, store: store2, registries }) {
3563
+ function FinancialPage({ config, provider, store: store6, registries }) {
3797
3564
  const { view, direction, navigate, previousView, back } = useModuleNavigation("/financial", {
3798
3565
  summary: 0,
3799
3566
  "payables-list": 0,
@@ -3832,26 +3599,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3832
3599
  const intent = parseIntent(view);
3833
3600
  const isSettings = view === "settings";
3834
3601
  const nav = useModuleNavAccess(buildNav(config, view, navigate, t2));
3835
- const [quickAddOpen, setQuickAddOpen] = useState(false);
3836
- const [quickAddType, setQuickAddType] = useState("expense");
3837
- const [quickAddReceipt, setQuickAddReceipt] = useState(false);
3838
- const openQuickAdd2 = useCallback((type = "expense") => {
3839
- setQuickAddType(type);
3840
- setQuickAddReceipt(false);
3841
- setQuickAddOpen(true);
3842
- }, []);
3843
- const pendingQuickAdd = usePendingQuickAdd();
3844
- useEffect(() => {
3845
- if (!pendingQuickAdd) return;
3846
- const req = consumeQuickAdd();
3847
- if (!req) return;
3848
- if (!canCreate) return;
3849
- setQuickAddType(req.type);
3850
- setQuickAddReceipt(req.receipt);
3851
- setQuickAddOpen(true);
3852
- }, [pendingQuickAdd]);
3853
3602
  const quickActions = useMemo(() => {
3854
- if (config.quickAdd) return [];
3855
3603
  if (!canCreate) return [];
3856
3604
  const actions = [];
3857
3605
  if (config.modules.payables) {
@@ -3882,7 +3630,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3882
3630
  });
3883
3631
  }
3884
3632
  return actions;
3885
- }, [config.modules, config.quickAdd, canCreate, navigate, t2]);
3633
+ }, [config.modules, canCreate, navigate, t2]);
3886
3634
  function handleOnboardingComplete() {
3887
3635
  setOnboardingComplete(true);
3888
3636
  try {
@@ -3891,10 +3639,10 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3891
3639
  }
3892
3640
  }
3893
3641
  if (!onboardingComplete) {
3894
- return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store2, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete: handleOnboardingComplete }) });
3642
+ return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete: handleOnboardingComplete }) });
3895
3643
  }
3896
3644
  if (isSettings && registries && registries.length > 0) {
3897
- return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store2, children: /* @__PURE__ */ jsx(PageTransition, { transitionKey: "settings", direction, children: /* @__PURE__ */ jsx("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx(
3645
+ return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(PageTransition, { transitionKey: "settings", direction, children: /* @__PURE__ */ jsx("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx(
3898
3646
  PluginSettingsPanel,
3899
3647
  {
3900
3648
  title: t2("financial.settingsPage.title"),
@@ -3923,7 +3671,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3923
3671
  case "commissions":
3924
3672
  return /* @__PURE__ */ jsx(CommissionsView, {});
3925
3673
  case "cards":
3926
- return /* @__PURE__ */ jsx(CardsView, {});
3674
+ return /* @__PURE__ */ jsx(CardsView, { onNavigate: navigate });
3927
3675
  case "notas-fiscais":
3928
3676
  return /* @__PURE__ */ jsx(NotasFiscaisView, {});
3929
3677
  default:
@@ -3935,7 +3683,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3935
3683
  if (!feature) return renderView();
3936
3684
  return /* @__PURE__ */ jsx(EntitlementGate, { feature, fallback: /* @__PURE__ */ jsx(UpgradePrompt, { feature }), children: renderView() });
3937
3685
  }
3938
- return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store2, children: /* @__PURE__ */ jsxs(
3686
+ return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(
3939
3687
  ModulePage,
3940
3688
  {
3941
3689
  title: config.labels.pageTitle,
@@ -3945,45 +3693,15 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3945
3693
  showHeader: intent.view === "summary" || view === "summary",
3946
3694
  viewKey: view,
3947
3695
  direction,
3948
- headerAction: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3949
- config.quickAdd && // One-primary-action rule (FAY-1247): quick-add is the single primary
3950
- // add affordance in B2C mode. The receipt entry point lives inside the
3951
- // sheet's "Fotografar ou anexar recibo" attachment step, so no separate
3952
- // "Enviar recibo" button is needed here.
3953
- // Gated on the role's `create` action so read-only members don't see it.
3954
- /* @__PURE__ */ jsx(PermissionGate, { feature: "financial", action: "create", children: /* @__PURE__ */ jsxs(
3955
- Button,
3956
- {
3957
- size: "sm",
3958
- className: "hidden md:inline-flex",
3959
- onClick: () => openQuickAdd2("expense"),
3960
- children: [
3961
- /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
3962
- t2("financial.quickTx.newTransaction")
3963
- ]
3964
- }
3965
- ) }),
3966
- /* @__PURE__ */ jsx(
3967
- ModuleActionBar,
3968
- {
3969
- quickActions,
3970
- settingsPath: registries && registries.length > 0 ? "/settings/financial" : void 0,
3971
- settingsLabel: t2("financial.settingsPage.title")
3972
- }
3973
- )
3974
- ] }),
3975
- children: [
3976
- renderGatedView(),
3977
- /* @__PURE__ */ jsx(
3978
- QuickTransactionForm,
3979
- {
3980
- open: quickAddOpen,
3981
- onOpenChange: setQuickAddOpen,
3982
- defaultType: quickAddType,
3983
- focusReceipt: quickAddReceipt
3984
- }
3985
- )
3986
- ]
3696
+ headerAction: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
3697
+ ModuleActionBar,
3698
+ {
3699
+ quickActions,
3700
+ settingsPath: registries && registries.length > 0 ? "/settings/financial" : void 0,
3701
+ settingsLabel: t2("financial.settingsPage.title")
3702
+ }
3703
+ ) }),
3704
+ children: renderGatedView()
3987
3705
  }
3988
3706
  ) });
3989
3707
  }
@@ -4149,7 +3867,7 @@ function BalanceHero() {
4149
3867
  const summary = useFinancialStore((s) => s.summary);
4150
3868
  const navigate = useDashboardNavigate();
4151
3869
  useEnsureSummary();
4152
- const [hidden, setHidden] = React18__default.useState(false);
3870
+ const [hidden, setHidden] = React17.useState(false);
4153
3871
  const money = (v) => hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022" : formatCurrency(v ?? 0, currency);
4154
3872
  const inflow = summary?.monthlyInflow ?? 0;
4155
3873
  const outflow = summary?.monthlyOutflow ?? 0;
@@ -4222,7 +3940,7 @@ function categorizeSpend(notes) {
4222
3940
  function SpendByCategoryChart() {
4223
3941
  const t2 = useTranslation();
4224
3942
  const provider = useFinancialProvider();
4225
- const [rows, setRows] = React18__default.useState([]);
3943
+ const [rows, setRows] = React17.useState([]);
4226
3944
  useEffect(() => {
4227
3945
  let alive = true;
4228
3946
  void (async () => {
@@ -4265,14 +3983,14 @@ function UpcomingBills() {
4265
3983
  const { currency } = useFinancialConfig();
4266
3984
  const provider = useFinancialProvider();
4267
3985
  const dueLabel = useDueLabel();
4268
- const [bills, setBills] = React18__default.useState([]);
3986
+ const [bills, setBills] = React17.useState([]);
4269
3987
  useEffect(() => {
4270
3988
  let alive = true;
4271
3989
  void (async () => {
4272
- const pending2 = await provider.getMovements({ status: "pending" });
3990
+ const pending = await provider.getMovements({ status: "pending" });
4273
3991
  if (!alive) return;
4274
3992
  setBills(
4275
- pending2.data.filter((m) => m.direction === "debit" && m.movementKind === "bill").sort((a, b) => a.dueDate.localeCompare(b.dueDate))
3993
+ pending.data.filter((m) => m.direction === "debit" && m.movementKind === "bill").sort((a, b) => a.dueDate.localeCompare(b.dueDate))
4276
3994
  );
4277
3995
  })();
4278
3996
  return () => {
@@ -4445,7 +4163,7 @@ function createStore(options) {
4445
4163
  };
4446
4164
  }
4447
4165
  function createMockFinancialProvider(options) {
4448
- const store2 = createStore(options);
4166
+ const store6 = createStore(options);
4449
4167
  const tenantId = "mock-tenant";
4450
4168
  function recalcInvoiceStatus(invoice) {
4451
4169
  if (invoice.paidAmount >= invoice.totalAmount) {
@@ -4453,7 +4171,7 @@ function createMockFinancialProvider(options) {
4453
4171
  } else if (invoice.paidAmount > 0) {
4454
4172
  invoice.status = "partial";
4455
4173
  } else {
4456
- const movements = store2.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4174
+ const movements = store6.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4457
4175
  const hasOverdue = movements.some((m) => m.status === "overdue" || m.status === "pending" && m.dueDate < today());
4458
4176
  invoice.status = hasOverdue ? "overdue" : "open";
4459
4177
  }
@@ -4462,16 +4180,16 @@ function createMockFinancialProvider(options) {
4462
4180
  const provider = {
4463
4181
  // --- Invoices ---
4464
4182
  async getInvoices(query) {
4465
- let results = [...store2.invoices];
4183
+ let results = [...store6.invoices];
4466
4184
  if (query.direction) results = results.filter((i) => i.direction === query.direction);
4467
4185
  if (query.status) results = results.filter((i) => matchesStatus(i.status, query.status));
4468
4186
  if (query.contactId) results = results.filter((i) => i.contactId === query.contactId);
4469
4187
  if (query.accountId) {
4470
- const invoiceIds = new Set(store2.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
4188
+ const invoiceIds = new Set(store6.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
4471
4189
  results = results.filter((invoice) => invoiceIds.has(invoice.id));
4472
4190
  }
4473
4191
  if (query.costCenterId) {
4474
- const invoiceIds = new Set(store2.invoiceItems.filter((item) => item.costCenterId === query.costCenterId).map((item) => item.invoiceId));
4192
+ const invoiceIds = new Set(store6.invoiceItems.filter((item) => item.costCenterId === query.costCenterId).map((item) => item.invoiceId));
4475
4193
  results = results.filter((invoice) => invoiceIds.has(invoice.id));
4476
4194
  }
4477
4195
  if (query.dateRange) results = results.filter((i) => matchesDateRange(i.invoiceDate, query.dateRange));
@@ -4485,10 +4203,10 @@ function createMockFinancialProvider(options) {
4485
4203
  return paginate(results, query.page, query.pageSize);
4486
4204
  },
4487
4205
  async getInvoiceById(id) {
4488
- return store2.invoices.find((i) => i.id === id) ?? null;
4206
+ return store6.invoices.find((i) => i.id === id) ?? null;
4489
4207
  },
4490
4208
  async getInvoiceItems(invoiceId) {
4491
- return store2.invoiceItems.filter((i) => i.invoiceId === invoiceId);
4209
+ return store6.invoiceItems.filter((i) => i.invoiceId === invoiceId);
4492
4210
  },
4493
4211
  async createInvoice(input) {
4494
4212
  const invoiceId = uid();
@@ -4517,7 +4235,7 @@ function createMockFinancialProvider(options) {
4517
4235
  createdAt: now(),
4518
4236
  updatedAt: now()
4519
4237
  };
4520
- store2.invoices.push(invoice);
4238
+ store6.invoices.push(invoice);
4521
4239
  for (const itemInput of input.items) {
4522
4240
  const itemTotal = itemInput.quantity * itemInput.unitPrice - (itemInput.discount ?? 0) + (itemInput.surcharge ?? 0);
4523
4241
  const item = {
@@ -4536,7 +4254,7 @@ function createMockFinancialProvider(options) {
4536
4254
  metadata: itemInput.metadata,
4537
4255
  createdAt: now()
4538
4256
  };
4539
- store2.invoiceItems.push(item);
4257
+ store6.invoiceItems.push(item);
4540
4258
  }
4541
4259
  for (const movInput of input.installments) {
4542
4260
  const movement = {
@@ -4557,29 +4275,29 @@ function createMockFinancialProvider(options) {
4557
4275
  createdAt: now(),
4558
4276
  updatedAt: now()
4559
4277
  };
4560
- store2.movements.push(movement);
4278
+ store6.movements.push(movement);
4561
4279
  }
4562
4280
  return invoice;
4563
4281
  },
4564
4282
  async updateInvoice(id, data) {
4565
- const invoice = store2.invoices.find((i) => i.id === id);
4283
+ const invoice = store6.invoices.find((i) => i.id === id);
4566
4284
  if (!invoice) throw new Error(`Invoice ${id} not found`);
4567
4285
  Object.assign(invoice, data, { updatedAt: now() });
4568
4286
  return invoice;
4569
4287
  },
4570
4288
  async cancelInvoice(id) {
4571
- const invoice = store2.invoices.find((i) => i.id === id);
4289
+ const invoice = store6.invoices.find((i) => i.id === id);
4572
4290
  if (!invoice) throw new Error(`Invoice ${id} not found`);
4573
4291
  invoice.status = "cancelled";
4574
4292
  invoice.updatedAt = now();
4575
- store2.movements.filter((m) => m.invoiceId === id && m.status === "pending").forEach((m) => {
4293
+ store6.movements.filter((m) => m.invoiceId === id && m.status === "pending").forEach((m) => {
4576
4294
  m.status = "cancelled";
4577
4295
  m.updatedAt = now();
4578
4296
  });
4579
4297
  },
4580
4298
  // --- Movements ---
4581
4299
  async getMovements(query) {
4582
- let results = [...store2.movements];
4300
+ let results = [...store6.movements];
4583
4301
  if (query.direction) results = results.filter((m) => m.direction === query.direction);
4584
4302
  if (query.status) results = results.filter((m) => matchesStatus(m.status, query.status));
4585
4303
  if (query.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
@@ -4589,9 +4307,9 @@ function createMockFinancialProvider(options) {
4589
4307
  return paginate(results, query.page, query.pageSize);
4590
4308
  },
4591
4309
  async payMovement(input) {
4592
- const bill = store2.movements.find((m) => m.id === input.movementId);
4310
+ const bill = store6.movements.find((m) => m.id === input.movementId);
4593
4311
  if (!bill) throw new Error(`Movement ${input.movementId} not found`);
4594
- const method = input.paymentMethodId ? store2.paymentMethods.find((pm) => pm.id === input.paymentMethodId) : void 0;
4312
+ const method = input.paymentMethodId ? store6.paymentMethods.find((pm) => pm.id === input.paymentMethodId) : void 0;
4595
4313
  const feePct = bill.direction === "credit" ? method?.discountValue ?? 0 : 0;
4596
4314
  const thisFee = round2(input.amount * (feePct / 100));
4597
4315
  const payment = {
@@ -4613,18 +4331,19 @@ function createMockFinancialProvider(options) {
4613
4331
  cardBrand: input.cardBrand,
4614
4332
  cardInstallments: input.cardInstallments,
4615
4333
  notes: input.notes,
4334
+ metadata: input.metadata,
4616
4335
  tenantId,
4617
4336
  createdAt: now(),
4618
4337
  updatedAt: now()
4619
4338
  };
4620
- store2.movements.push(payment);
4339
+ store6.movements.push(payment);
4621
4340
  bill.paidAmount = round2(bill.paidAmount + input.amount);
4622
4341
  bill.status = bill.paidAmount >= bill.amount ? "paid" : "partial";
4623
4342
  bill.paymentDate = void 0;
4624
4343
  bill.bankAccountId = void 0;
4625
4344
  bill.updatedAt = now();
4626
4345
  if (input.bankAccountId) {
4627
- const account = store2.bankAccounts.find((a) => a.id === input.bankAccountId);
4346
+ const account = store6.bankAccounts.find((a) => a.id === input.bankAccountId);
4628
4347
  if (account) {
4629
4348
  if (bill.direction === "credit") {
4630
4349
  account.currentBalance = round2(account.currentBalance + (input.amount - thisFee));
@@ -4635,9 +4354,9 @@ function createMockFinancialProvider(options) {
4635
4354
  }
4636
4355
  }
4637
4356
  if (bill.invoiceId) {
4638
- const invoice = store2.invoices.find((i) => i.id === bill.invoiceId);
4357
+ const invoice = store6.invoices.find((i) => i.id === bill.invoiceId);
4639
4358
  if (invoice) {
4640
- const bills = store2.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4359
+ const bills = store6.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4641
4360
  invoice.paidAmount = bills.reduce((sum, m) => sum + m.paidAmount, 0);
4642
4361
  recalcInvoiceStatus(invoice);
4643
4362
  }
@@ -4645,7 +4364,7 @@ function createMockFinancialProvider(options) {
4645
4364
  return payment;
4646
4365
  },
4647
4366
  async cancelMovement(id) {
4648
- const movement = store2.movements.find((m) => m.id === id);
4367
+ const movement = store6.movements.find((m) => m.id === id);
4649
4368
  if (!movement) throw new Error(`Movement ${id} not found`);
4650
4369
  movement.status = "cancelled";
4651
4370
  movement.updatedAt = now();
@@ -4672,22 +4391,22 @@ function createMockFinancialProvider(options) {
4672
4391
  createdAt: now(),
4673
4392
  updatedAt: now()
4674
4393
  };
4675
- store2.movements.push({
4394
+ store6.movements.push({
4676
4395
  ...base,
4677
4396
  id: debitMovementId,
4678
4397
  direction: "debit",
4679
4398
  bankAccountId: input.fromAccountId,
4680
4399
  metadata: { transferId, transferRole: "out", counterAccountId: input.toAccountId }
4681
4400
  });
4682
- store2.movements.push({
4401
+ store6.movements.push({
4683
4402
  ...base,
4684
4403
  id: creditMovementId,
4685
4404
  direction: "credit",
4686
4405
  bankAccountId: input.toAccountId,
4687
4406
  metadata: { transferId, transferRole: "in", counterAccountId: input.fromAccountId }
4688
4407
  });
4689
- const from = store2.bankAccounts.find((a) => a.id === input.fromAccountId);
4690
- const to = store2.bankAccounts.find((a) => a.id === input.toAccountId);
4408
+ const from = store6.bankAccounts.find((a) => a.id === input.fromAccountId);
4409
+ const to = store6.bankAccounts.find((a) => a.id === input.toAccountId);
4691
4410
  if (from) {
4692
4411
  from.currentBalance = round2(from.currentBalance - input.amount);
4693
4412
  from.updatedAt = now();
@@ -4700,7 +4419,7 @@ function createMockFinancialProvider(options) {
4700
4419
  },
4701
4420
  // --- Bank Accounts ---
4702
4421
  async getBankAccounts() {
4703
- return store2.bankAccounts.filter((a) => a.isActive);
4422
+ return store6.bankAccounts.filter((a) => a.isActive);
4704
4423
  },
4705
4424
  async createBankAccount(data) {
4706
4425
  const account = {
@@ -4721,26 +4440,26 @@ function createMockFinancialProvider(options) {
4721
4440
  createdAt: now(),
4722
4441
  updatedAt: now()
4723
4442
  };
4724
- store2.bankAccounts.push(account);
4443
+ store6.bankAccounts.push(account);
4725
4444
  return account;
4726
4445
  },
4727
4446
  async updateBankAccount(id, data) {
4728
- const account = store2.bankAccounts.find((a) => a.id === id);
4447
+ const account = store6.bankAccounts.find((a) => a.id === id);
4729
4448
  if (!account) throw new Error(`Bank account ${id} not found`);
4730
4449
  Object.assign(account, data, { updatedAt: now() });
4731
4450
  return account;
4732
4451
  },
4733
4452
  // --- Cash Sessions ---
4734
4453
  async getCashSessions(bankAccountId) {
4735
- let results = [...store2.cashSessions];
4454
+ let results = [...store6.cashSessions];
4736
4455
  if (bankAccountId) results = results.filter((s) => s.bankAccountId === bankAccountId);
4737
4456
  return results.sort((a, b) => b.openedAt.localeCompare(a.openedAt));
4738
4457
  },
4739
4458
  async getOpenSession(bankAccountId) {
4740
- return store2.cashSessions.find((s) => s.bankAccountId === bankAccountId && s.status === "open") ?? null;
4459
+ return store6.cashSessions.find((s) => s.bankAccountId === bankAccountId && s.status === "open") ?? null;
4741
4460
  },
4742
4461
  async openCashSession(input) {
4743
- const account = store2.bankAccounts.find((a) => a.id === input.bankAccountId);
4462
+ const account = store6.bankAccounts.find((a) => a.id === input.bankAccountId);
4744
4463
  const session = {
4745
4464
  id: uid(),
4746
4465
  bankAccountId: input.bankAccountId,
@@ -4754,13 +4473,13 @@ function createMockFinancialProvider(options) {
4754
4473
  tenantId,
4755
4474
  createdAt: now()
4756
4475
  };
4757
- store2.cashSessions.push(session);
4476
+ store6.cashSessions.push(session);
4758
4477
  return session;
4759
4478
  },
4760
4479
  async closeCashSession(input) {
4761
- const session = store2.cashSessions.find((s) => s.id === input.sessionId);
4480
+ const session = store6.cashSessions.find((s) => s.id === input.sessionId);
4762
4481
  if (!session) throw new Error(`Cash session ${input.sessionId} not found`);
4763
- const sessionMovements = store2.movements.filter((m) => m.cashSessionId === session.id && m.status === "paid");
4482
+ const sessionMovements = store6.movements.filter((m) => m.cashSessionId === session.id && m.status === "paid");
4764
4483
  const inflow = sessionMovements.filter((m) => m.direction === "credit").reduce((s, m) => s + m.paidAmount, 0);
4765
4484
  const outflow = sessionMovements.filter((m) => m.direction === "debit").reduce((s, m) => s + m.paidAmount, 0);
4766
4485
  const expectedBalance = session.openingBalance + inflow - outflow;
@@ -4775,9 +4494,9 @@ function createMockFinancialProvider(options) {
4775
4494
  return session;
4776
4495
  },
4777
4496
  async getCashSessionSummary(sessionId) {
4778
- const session = store2.cashSessions.find((s) => s.id === sessionId);
4497
+ const session = store6.cashSessions.find((s) => s.id === sessionId);
4779
4498
  if (!session) throw new Error(`Cash session ${sessionId} not found`);
4780
- const sessionMovements = store2.movements.filter((m) => m.cashSessionId === sessionId && m.status === "paid");
4499
+ const sessionMovements = store6.movements.filter((m) => m.cashSessionId === sessionId && m.status === "paid");
4781
4500
  return {
4782
4501
  session,
4783
4502
  movementCount: sessionMovements.length,
@@ -4787,21 +4506,21 @@ function createMockFinancialProvider(options) {
4787
4506
  },
4788
4507
  // --- Payment Methods ---
4789
4508
  async getPaymentMethods() {
4790
- return store2.paymentMethods.filter((pm) => pm.isActive);
4509
+ return store6.paymentMethods.filter((pm) => pm.isActive);
4791
4510
  },
4792
4511
  async getPaymentMethodTypes() {
4793
- return store2.paymentMethodTypes.filter((t2) => t2.isActive);
4512
+ return store6.paymentMethodTypes.filter((t2) => t2.isActive);
4794
4513
  },
4795
4514
  // --- Chart of Accounts ---
4796
4515
  async getChartOfAccounts() {
4797
- return store2.chartOfAccounts.filter((n) => n.isActive);
4516
+ return store6.chartOfAccounts.filter((n) => n.isActive);
4798
4517
  },
4799
4518
  async getCostCenters() {
4800
- return store2.costCenters.filter((c) => c.isActive);
4519
+ return store6.costCenters.filter((c) => c.isActive);
4801
4520
  },
4802
4521
  // --- Card Transactions ---
4803
4522
  async getCardTransactions(dateRange) {
4804
- let results = [...store2.cardTransactions];
4523
+ let results = [...store6.cardTransactions];
4805
4524
  if (dateRange) results = results.filter((t2) => matchesDateRange(t2.expectedDate, dateRange));
4806
4525
  return results.sort((a, b) => a.expectedDate.localeCompare(b.expectedDate));
4807
4526
  },
@@ -4810,15 +4529,15 @@ function createMockFinancialProvider(options) {
4810
4529
  const todayStr = today();
4811
4530
  const isRealized = (m) => m.status !== "cancelled" && !!m.paymentDate;
4812
4531
  const consolidated = !query.bankAccountId;
4813
- const scopeAccounts = consolidated ? store2.bankAccounts.filter((a) => a.isActive) : store2.bankAccounts.filter((a) => a.id === query.bankAccountId);
4532
+ const scopeAccounts = consolidated ? store6.bankAccounts.filter((a) => a.isActive) : store6.bankAccounts.filter((a) => a.id === query.bankAccountId);
4814
4533
  const accountIds = new Set(scopeAccounts.map((a) => a.id));
4815
- const accountNameById = new Map(store2.bankAccounts.map((a) => [a.id, a.name]));
4534
+ const accountNameById = new Map(store6.bankAccounts.map((a) => [a.id, a.name]));
4816
4535
  const initialSum = scopeAccounts.reduce((s, a) => s + a.initialBalance, 0);
4817
4536
  if (!consolidated && accountIds.size === 0) {
4818
4537
  return { entries: [], openingBalance: 0, closingBalance: 0, totalCredits: 0, totalDebits: 0, totalFees: 0, net: 0, accountId: query.bankAccountId };
4819
4538
  }
4820
4539
  const netOf = (m) => m.direction === "credit" ? m.paidAmount - (m.feeAmount ?? 0) : -m.paidAmount;
4821
- const inScope = store2.movements.filter((m) => isRealized(m) && (consolidated || m.bankAccountId && accountIds.has(m.bankAccountId)));
4540
+ const inScope = store6.movements.filter((m) => isRealized(m) && (consolidated || m.bankAccountId && accountIds.has(m.bankAccountId)));
4822
4541
  const openingBalance = round2(initialSum + inScope.filter((m) => (m.paymentDate ?? "") < query.dateRange.from).reduce((s, m) => s + netOf(m), 0));
4823
4542
  const rangeMovs = inScope.filter((m) => matchesDateRange(m.paymentDate, query.dateRange)).sort((a, b) => (a.paymentDate ?? "").localeCompare(b.paymentDate ?? "") || a.createdAt.localeCompare(b.createdAt));
4824
4543
  let balance = openingBalance;
@@ -4840,7 +4559,7 @@ function createMockFinancialProvider(options) {
4840
4559
  }
4841
4560
  return {
4842
4561
  movement,
4843
- invoice: movement.invoiceId ? store2.invoices.find((i) => i.id === movement.invoiceId) : void 0,
4562
+ invoice: movement.invoiceId ? store6.invoices.find((i) => i.id === movement.invoiceId) : void 0,
4844
4563
  runningBalance: balance,
4845
4564
  gross,
4846
4565
  fee,
@@ -4852,9 +4571,9 @@ function createMockFinancialProvider(options) {
4852
4571
  });
4853
4572
  for (const e of entries) {
4854
4573
  const mid = e.movement.matchedMovementId;
4855
- const mm = mid ? store2.movements.find((m) => m.id === mid) : void 0;
4574
+ const mm = mid ? store6.movements.find((m) => m.id === mid) : void 0;
4856
4575
  if (!mm) continue;
4857
- const inv = mm.invoiceId ? store2.invoices.find((i) => i.id === mm.invoiceId) : void 0;
4576
+ const inv = mm.invoiceId ? store6.invoices.find((i) => i.id === mm.invoiceId) : void 0;
4858
4577
  e.reconciledWith = {
4859
4578
  movementId: mm.id,
4860
4579
  description: mm.notes ?? void 0,
@@ -4878,11 +4597,11 @@ function createMockFinancialProvider(options) {
4878
4597
  },
4879
4598
  // --- Summary ---
4880
4599
  async getSummary(dateRange) {
4881
- const totalBalance = store2.bankAccounts.filter((a) => a.isActive).reduce((sum, a) => sum + a.currentBalance, 0);
4882
- const pendingReceivable = store2.movements.filter(
4600
+ const totalBalance = store6.bankAccounts.filter((a) => a.isActive).reduce((sum, a) => sum + a.currentBalance, 0);
4601
+ const pendingReceivable = store6.movements.filter(
4883
4602
  (m) => m.direction === "credit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
4884
4603
  );
4885
- const pendingPayable = store2.movements.filter(
4604
+ const pendingPayable = store6.movements.filter(
4886
4605
  (m) => m.direction === "debit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
4887
4606
  );
4888
4607
  const todayStr = today();
@@ -4890,7 +4609,7 @@ function createMockFinancialProvider(options) {
4890
4609
  monthAgo.setDate(monthAgo.getDate() - 30);
4891
4610
  const monthStart = monthAgo.toISOString().slice(0, 10);
4892
4611
  const monthEnd = todayStr;
4893
- const monthMovements = store2.movements.filter(
4612
+ const monthMovements = store6.movements.filter(
4894
4613
  (m) => m.movementKind === "payment" && matchesDateRange(m.paymentDate, dateRange ?? { from: monthStart, to: monthEnd })
4895
4614
  );
4896
4615
  const overdueReceivable = pendingReceivable.filter((m) => m.dueDate < todayStr);
@@ -4911,12 +4630,12 @@ function createMockFinancialProvider(options) {
4911
4630
  async importBankTransactions(input) {
4912
4631
  let imported = 0, duplicates = 0;
4913
4632
  for (const line of input.lines) {
4914
- const dup = store2.movements.some((m) => m.externalSource === line.externalSource && m.externalId === line.externalId);
4633
+ const dup = store6.movements.some((m) => m.externalSource === line.externalSource && m.externalId === line.externalId);
4915
4634
  if (dup) {
4916
4635
  duplicates++;
4917
4636
  continue;
4918
4637
  }
4919
- store2.movements.push({
4638
+ store6.movements.push({
4920
4639
  id: uid(),
4921
4640
  direction: line.direction,
4922
4641
  movementKind: "payment",
@@ -4938,38 +4657,38 @@ function createMockFinancialProvider(options) {
4938
4657
  return { imported, duplicates };
4939
4658
  },
4940
4659
  async getUnreconciled(query) {
4941
- let results = store2.movements.filter((m) => m.externalSource && !m.reconciledAt);
4660
+ let results = store6.movements.filter((m) => m.externalSource && !m.reconciledAt);
4942
4661
  if (query?.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
4943
4662
  if (query?.dateRange) results = results.filter((m) => matchesDateRange(m.paymentDate, query.dateRange));
4944
4663
  return results.sort((a, b) => (b.paymentDate ?? "").localeCompare(a.paymentDate ?? ""));
4945
4664
  },
4946
4665
  async suggestReconciliation(bankMovementId) {
4947
- const line = store2.movements.find((m) => m.id === bankMovementId);
4666
+ const line = store6.movements.find((m) => m.id === bankMovementId);
4948
4667
  if (!line) return [];
4949
4668
  const amount = line.amount;
4950
4669
  const tol = Math.max(0.01, round2(amount * 0.02));
4951
4670
  const lineDate = line.paymentDate ?? line.dueDate;
4952
- return store2.movements.filter((m) => !m.externalSource && !m.reconciledAt && m.direction === line.direction && m.id !== bankMovementId && Math.abs(m.amount - amount) <= tol).map((m) => {
4671
+ return store6.movements.filter((m) => !m.externalSource && !m.reconciledAt && m.direction === line.direction && m.id !== bankMovementId && Math.abs(m.amount - amount) <= tol).map((m) => {
4953
4672
  const amtScore = amount > 0 ? Math.max(0, 1 - Math.abs(m.amount - amount) / amount) : 0;
4954
4673
  const refDate = m.dueDate ?? m.paymentDate;
4955
4674
  const days = lineDate && refDate ? Math.abs((new Date(lineDate).getTime() - new Date(refDate).getTime()) / 864e5) : 30;
4956
4675
  const dateScore = Math.max(0, 1 - days / 30);
4957
4676
  return {
4958
4677
  movement: m,
4959
- invoice: m.invoiceId ? store2.invoices.find((i) => i.id === m.invoiceId) : void 0,
4678
+ invoice: m.invoiceId ? store6.invoices.find((i) => i.id === m.invoiceId) : void 0,
4960
4679
  score: round2(0.7 * amtScore + 0.3 * dateScore)
4961
4680
  };
4962
4681
  }).sort((a, b) => b.score - a.score).slice(0, 25);
4963
4682
  },
4964
4683
  async reconcileMovement(input) {
4965
- const line = store2.movements.find((m) => m.id === input.bankMovementId);
4684
+ const line = store6.movements.find((m) => m.id === input.bankMovementId);
4966
4685
  if (line) {
4967
4686
  line.reconciledAt = now();
4968
4687
  line.matchedMovementId = input.matchedMovementId;
4969
4688
  line.updatedAt = now();
4970
4689
  }
4971
4690
  if (input.matchedMovementId) {
4972
- const internal = store2.movements.find((m) => m.id === input.matchedMovementId);
4691
+ const internal = store6.movements.find((m) => m.id === input.matchedMovementId);
4973
4692
  if (internal) {
4974
4693
  internal.reconciledAt = now();
4975
4694
  internal.updatedAt = now();
@@ -4977,14 +4696,14 @@ function createMockFinancialProvider(options) {
4977
4696
  }
4978
4697
  },
4979
4698
  async unreconcileMovement(bankMovementId) {
4980
- const line = store2.movements.find((m) => m.id === bankMovementId);
4699
+ const line = store6.movements.find((m) => m.id === bankMovementId);
4981
4700
  if (!line) return;
4982
4701
  const matchedId = line.matchedMovementId;
4983
4702
  line.reconciledAt = void 0;
4984
4703
  line.matchedMovementId = void 0;
4985
4704
  line.updatedAt = now();
4986
4705
  if (matchedId) {
4987
- const internal = store2.movements.find((m) => m.id === matchedId);
4706
+ const internal = store6.movements.find((m) => m.id === matchedId);
4988
4707
  if (internal) {
4989
4708
  internal.reconciledAt = void 0;
4990
4709
  internal.updatedAt = now();
@@ -4993,7 +4712,7 @@ function createMockFinancialProvider(options) {
4993
4712
  },
4994
4713
  // --- Notas fiscais (NF-e / NFC-e / NFS-e) ---
4995
4714
  async getNotas(query) {
4996
- let list = [...store2.notas].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
4715
+ let list = [...store6.notas].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
4997
4716
  if (query.status) list = list.filter((n) => matchesStatus(n.status, query.status));
4998
4717
  if (query.model) list = list.filter((n) => n.model === query.model);
4999
4718
  if (query.orderId) list = list.filter((n) => n.orderId === query.orderId);
@@ -5004,7 +4723,7 @@ function createMockFinancialProvider(options) {
5004
4723
  return paginate(list, query.page, query.pageSize);
5005
4724
  },
5006
4725
  async getNotaById(id) {
5007
- return store2.notas.find((n) => n.id === id) ?? null;
4726
+ return store6.notas.find((n) => n.id === id) ?? null;
5008
4727
  },
5009
4728
  async createNota(input) {
5010
4729
  const nota = {
@@ -5022,19 +4741,19 @@ function createMockFinancialProvider(options) {
5022
4741
  createdAt: now(),
5023
4742
  updatedAt: now()
5024
4743
  };
5025
- store2.notas.unshift(nota);
4744
+ store6.notas.unshift(nota);
5026
4745
  return nota;
5027
4746
  },
5028
4747
  async updateNota(id, patch) {
5029
- const nota = store2.notas.find((n) => n.id === id);
4748
+ const nota = store6.notas.find((n) => n.id === id);
5030
4749
  if (!nota) throw new Error("Nota fiscal not found");
5031
4750
  Object.assign(nota, patch, { updatedAt: now() });
5032
4751
  return nota;
5033
4752
  },
5034
4753
  async issueNota(id) {
5035
- const nota = store2.notas.find((n) => n.id === id);
4754
+ const nota = store6.notas.find((n) => n.id === id);
5036
4755
  if (!nota) throw new Error("Nota fiscal not found");
5037
- const issuedCount = store2.notas.filter((n) => n.status === "issued").length;
4756
+ const issuedCount = store6.notas.filter((n) => n.status === "issued").length;
5038
4757
  nota.status = "issued";
5039
4758
  nota.number = String(issuedCount + 1).padStart(6, "0");
5040
4759
  nota.series = nota.series ?? "1";
@@ -5044,7 +4763,7 @@ function createMockFinancialProvider(options) {
5044
4763
  return nota;
5045
4764
  },
5046
4765
  async cancelNota(id, reason) {
5047
- const nota = store2.notas.find((n) => n.id === id);
4766
+ const nota = store6.notas.find((n) => n.id === id);
5048
4767
  if (!nota) throw new Error("Nota fiscal not found");
5049
4768
  nota.status = "cancelled";
5050
4769
  nota.errorMessage = reason;
@@ -5331,7 +5050,10 @@ function createSupabaseFinancialProvider() {
5331
5050
  paid_amount: 0,
5332
5051
  status: "pending",
5333
5052
  due_date: inst.dueDate,
5334
- installment_number: inst.installmentNumber ?? i + 1
5053
+ installment_number: inst.installmentNumber ?? i + 1,
5054
+ // O metadata da parcela vinha sendo descartado aqui: o comprovante
5055
+ // anexado no cadastro sobrevivia no mock e sumia no banco.
5056
+ metadata: inst.metadata ?? null
5335
5057
  }))
5336
5058
  );
5337
5059
  }
@@ -5409,7 +5131,8 @@ function createSupabaseFinancialProvider() {
5409
5131
  cash_session_id: input.cashSessionId ?? null,
5410
5132
  card_brand: input.cardBrand ?? null,
5411
5133
  card_installments: input.cardInstallments ?? null,
5412
- notes: input.notes ?? null
5134
+ notes: input.notes ?? null,
5135
+ metadata: input.metadata ?? null
5413
5136
  };
5414
5137
  let { data, error } = await pub.from(T.movements).insert(paymentRow).select().single();
5415
5138
  if (error && /fee_amount/i.test(error.message ?? "")) {
@@ -6097,82 +5820,6 @@ function createFinancialStore(provider) {
6097
5820
  throw err;
6098
5821
  }
6099
5822
  },
6100
- async createQuickTransaction(input) {
6101
- try {
6102
- if (input.type === "transfer") {
6103
- if (!input.bankAccountId || !input.toAccountId) {
6104
- throw new Error("Select source and destination accounts");
6105
- }
6106
- await provider.createTransfer({
6107
- fromAccountId: input.bankAccountId,
6108
- toAccountId: input.toAccountId,
6109
- amount: input.amount,
6110
- date: input.date,
6111
- notes: input.description?.trim() || void 0
6112
- });
6113
- } else {
6114
- const direction = input.type === "income" ? "credit" : "debit";
6115
- const desc = input.description?.trim() || (input.type === "income" ? "Receita" : "Despesa");
6116
- const invoiceMeta = {};
6117
- if (input.recurring) invoiceMeta.recurring = true;
6118
- if (input.receiptUrl) invoiceMeta.receiptUrl = input.receiptUrl;
6119
- const invoice = await provider.createInvoice({
6120
- direction,
6121
- invoiceDate: input.date,
6122
- contactName: desc,
6123
- metadata: Object.keys(invoiceMeta).length > 0 ? invoiceMeta : void 0,
6124
- items: [{
6125
- itemKind: "other",
6126
- description: desc,
6127
- quantity: 1,
6128
- unitPrice: input.amount,
6129
- accountId: input.categoryId
6130
- }],
6131
- installments: [{
6132
- direction,
6133
- movementKind: "bill",
6134
- amount: input.amount,
6135
- dueDate: input.date,
6136
- installmentNumber: 1,
6137
- bankAccountId: input.bankAccountId,
6138
- notes: desc
6139
- }]
6140
- });
6141
- if (input.paid) {
6142
- const movements = await provider.getMovements({ direction, status: "pending" });
6143
- const bill = movements.data.find((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
6144
- if (bill) {
6145
- await provider.payMovement({
6146
- movementId: bill.id,
6147
- amount: input.amount,
6148
- paymentDate: input.date,
6149
- bankAccountId: input.bankAccountId,
6150
- notes: desc
6151
- });
6152
- }
6153
- }
6154
- }
6155
- const invoiceQuery = get().invoiceQuery;
6156
- const statementQuery = get().statementQuery;
6157
- const [summary, bankAccounts, invoicesRes, statement] = await Promise.all([
6158
- provider.getSummary(),
6159
- provider.getBankAccounts(),
6160
- provider.getInvoices(invoiceQuery),
6161
- statementQuery ? provider.getStatement(statementQuery) : Promise.resolve(get().statement)
6162
- ]);
6163
- set({
6164
- summary,
6165
- bankAccounts,
6166
- invoices: invoicesRes.data,
6167
- invoicesTotal: invoicesRes.total,
6168
- statement
6169
- });
6170
- toast.success("Transaction saved");
6171
- } catch (err) {
6172
- toast.error("Failed to save transaction", { description: err?.message });
6173
- throw err;
6174
- }
6175
- },
6176
5823
  async payMovement(input) {
6177
5824
  try {
6178
5825
  await provider.payMovement(input);
@@ -6567,6 +6214,17 @@ var en = {
6567
6214
  "financial.invoice.items": "Items",
6568
6215
  "financial.invoice.keepInvoice": "Keep Invoice",
6569
6216
  "financial.invoice.new": "New",
6217
+ "financial.invoice.newManual": "Fill it in by hand",
6218
+ "financial.invoiceForm.createContact": "Create",
6219
+ "financial.invoiceForm.alreadyPaid": "Already paid",
6220
+ "financial.invoiceForm.paidWith": "Payment method",
6221
+ "financial.invoiceForm.paidFrom": "Paid from",
6222
+ "financial.invoiceForm.selectMethod": "Select a method",
6223
+ "financial.invoiceForm.selectAccount": "Select an account",
6224
+ "financial.invoiceForm.paidNeedsMethod": "Tell us the payment method and the account",
6225
+ "financial.invoiceForm.willBePaid": "This installment will be settled",
6226
+ "financial.invoiceForm.clear": "Clear",
6227
+ "financial.invoice.newManualHint": "Open the full invoice form",
6570
6228
  "financial.invoice.noDetails": "No additional details recorded",
6571
6229
  "financial.invoice.noInstallments": "No installments",
6572
6230
  "financial.invoice.noInvoices": "No invoices yet",
@@ -7068,6 +6726,17 @@ var ptBR = {
7068
6726
  "financial.invoice.items": "Itens",
7069
6727
  "financial.invoice.keepInvoice": "Manter Fatura",
7070
6728
  "financial.invoice.new": "Nova",
6729
+ "financial.invoice.newManual": "Cadastrar manualmente",
6730
+ "financial.invoiceForm.createContact": "Cadastrar",
6731
+ "financial.invoiceForm.alreadyPaid": "J\xE1 foi pago",
6732
+ "financial.invoiceForm.paidWith": "Forma de pagamento",
6733
+ "financial.invoiceForm.paidFrom": "Saiu de",
6734
+ "financial.invoiceForm.selectMethod": "Selecione a forma",
6735
+ "financial.invoiceForm.selectAccount": "Selecione a conta",
6736
+ "financial.invoiceForm.paidNeedsMethod": "Diga a forma de pagamento e a conta",
6737
+ "financial.invoiceForm.willBePaid": "Esta parcela entra como paga",
6738
+ "financial.invoiceForm.clear": "Limpar",
6739
+ "financial.invoice.newManualHint": "Preencher os campos da conta",
7071
6740
  "financial.invoice.noDetails": "Nenhum detalhe adicional registrado",
7072
6741
  "financial.invoice.noInstallments": "Sem parcelas",
7073
6742
  "financial.invoice.noInvoices": "Nenhuma fatura ainda",
@@ -7657,6 +7326,306 @@ var stripeConnectorDef = {
7657
7326
  await store.disconnect();
7658
7327
  }
7659
7328
  };
7329
+
7330
+ // src/integrations/acquirers/shared.ts
7331
+ function receivablePermissions(provider) {
7332
+ return [
7333
+ {
7334
+ kind: "data",
7335
+ resource: "payment",
7336
+ actions: ["write"],
7337
+ reason: `Lan\xE7ar no Financeiro cada venda que o ${provider} recebeu: valor, forma de pagamento, parcelas, taxa cobrada e a data em que o dinheiro cai na conta.`,
7338
+ sensitive: true
7339
+ },
7340
+ {
7341
+ kind: "data",
7342
+ resource: "invoice",
7343
+ actions: ["read", "write"],
7344
+ reason: `Encontrar o receb\xEDvel que corresponde \xE0quela venda e dar a baixa quando o ${provider} confirmar o repasse. O valor do receb\xEDvel n\xE3o \xE9 alterado.`,
7345
+ sensitive: true
7346
+ }
7347
+ ];
7348
+ }
7349
+ var SETTLE_ON_SETTING = {
7350
+ key: "settleOn",
7351
+ label: "Quando dar a baixa",
7352
+ description: "A venda no cart\xE3o entra hoje e o dinheiro cai depois. Escolha o que conta como recebido.",
7353
+ type: "select",
7354
+ options: [
7355
+ { value: "payout", label: "Quando o dinheiro cai na conta (repasse)" },
7356
+ { value: "sale", label: "Na hora da venda" }
7357
+ ],
7358
+ defaultValue: "payout"
7359
+ };
7360
+ function methodsSetting(options) {
7361
+ return {
7362
+ key: "methods",
7363
+ label: "Quais vendas trazer",
7364
+ description: "S\xF3 o que estiver marcado aqui vira lan\xE7amento no Financeiro.",
7365
+ type: "multi-select",
7366
+ options,
7367
+ // Nothing marked is a feed with nothing to bring: the connection is real and
7368
+ // reads as "conectado, falta configurar".
7369
+ requiredToOperate: true
7370
+ };
7371
+ }
7372
+ function methodsDetail2(labels, value) {
7373
+ const list = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
7374
+ const named = list.map((v) => labels[String(v).trim()] ?? String(v).trim()).filter(Boolean);
7375
+ return named.length > 0 ? named.join(", ") : void 0;
7376
+ }
7377
+
7378
+ // src/integrations/mercadopago/connectorDef.tsx
7379
+ var MERCADOPAGO_CONNECTOR_ID = "mercadopago";
7380
+ var METHOD_LABELS2 = {
7381
+ credit_card: "Cr\xE9dito",
7382
+ debit_card: "D\xE9bito",
7383
+ pix: "Pix",
7384
+ ticket: "Boleto",
7385
+ account_money: "Saldo em conta"
7386
+ };
7387
+ var MERCADOPAGO_SETTINGS = [
7388
+ methodsSetting([
7389
+ { value: "credit_card", label: METHOD_LABELS2.credit_card },
7390
+ { value: "debit_card", label: METHOD_LABELS2.debit_card },
7391
+ { value: "pix", label: METHOD_LABELS2.pix },
7392
+ { value: "ticket", label: METHOD_LABELS2.ticket },
7393
+ { value: "account_money", label: METHOD_LABELS2.account_money }
7394
+ ]),
7395
+ SETTLE_ON_SETTING
7396
+ ];
7397
+ var store2 = createConnectionStore(MERCADOPAGO_CONNECTOR_ID, {
7398
+ settings: MERCADOPAGO_SETTINGS,
7399
+ settingsKeys: ["collectorId"]
7400
+ });
7401
+ var mercadoPagoConnectorDef = {
7402
+ id: MERCADOPAGO_CONNECTOR_ID,
7403
+ hostPluginId: "financial",
7404
+ name: "Mercado Pago",
7405
+ description: "Traga para o Financeiro tudo que entrou no Mercado Pago \u2014 Pix, cart\xE3o e maquininha \u2014 com a taxa e a data do repasse.",
7406
+ icon: "CreditCard",
7407
+ category: "payments",
7408
+ authKind: "oauth",
7409
+ // We reach Mercado Pago for what it received; nothing here charges anyone.
7410
+ direction: "inbound",
7411
+ planes: ["sync"],
7412
+ docsUrl: "https://www.mercadopago.com.br/developers/pt/docs/checkout-api/additional-content/security/oauth/creation",
7413
+ permissions: receivablePermissions("Mercado Pago"),
7414
+ fields: [],
7415
+ settings: MERCADOPAGO_SETTINGS,
7416
+ howItSyncs: [
7417
+ "De tempos em tempos, o que o Mercado Pago recebeu vira lan\xE7amento no Financeiro.",
7418
+ "Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem no Mercado Pago.",
7419
+ "A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
7420
+ ],
7421
+ ...connectionStatus(
7422
+ MERCADOPAGO_CONNECTOR_ID,
7423
+ (connection) => ({
7424
+ connected: !!connection?.active,
7425
+ detail: methodsDetail2(METHOD_LABELS2, connection?.settings?.methods),
7426
+ plane: "sync",
7427
+ ...settingsStatus(MERCADOPAGO_SETTINGS, connection)
7428
+ }),
7429
+ { store: () => store2 }
7430
+ ),
7431
+ ...connectionSettings(store2),
7432
+ ...connectionRuns(store2),
7433
+ startOAuth: fayzConnectorOAuth(MERCADOPAGO_CONNECTOR_ID),
7434
+ saveConnection: async () => {
7435
+ await store2.save({ active: true });
7436
+ },
7437
+ disconnect: () => store2.disconnect(),
7438
+ disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
7439
+ };
7440
+ var PAGBANK_CONNECTOR_ID = "pagbank";
7441
+ var METHOD_LABELS3 = {
7442
+ CREDIT_CARD: "Cr\xE9dito",
7443
+ DEBIT_CARD: "D\xE9bito",
7444
+ PIX: "Pix",
7445
+ BOLETO: "Boleto"
7446
+ };
7447
+ var PAGBANK_SETTINGS = [
7448
+ methodsSetting([
7449
+ { value: "CREDIT_CARD", label: METHOD_LABELS3.CREDIT_CARD },
7450
+ { value: "DEBIT_CARD", label: METHOD_LABELS3.DEBIT_CARD },
7451
+ { value: "PIX", label: METHOD_LABELS3.PIX },
7452
+ { value: "BOLETO", label: METHOD_LABELS3.BOLETO }
7453
+ ]),
7454
+ SETTLE_ON_SETTING
7455
+ ];
7456
+ var store3 = createConnectionStore(PAGBANK_CONNECTOR_ID, {
7457
+ settings: PAGBANK_SETTINGS,
7458
+ settingsKeys: ["accountId"]
7459
+ });
7460
+ var pagBankConnectorDef = {
7461
+ id: PAGBANK_CONNECTOR_ID,
7462
+ hostPluginId: "financial",
7463
+ name: "PagBank",
7464
+ description: "Traga para o Financeiro tudo que entrou no PagBank \u2014 Pix, cart\xE3o e maquininha \u2014 com a taxa e a data do repasse.",
7465
+ icon: "CreditCard",
7466
+ // The catalogue carries no `pagbank` row yet, so the mark is declared here
7467
+ // rather than borrowed. Without it the row renders as a grey plug.
7468
+ logoUrl: "https://assets.fayz.ai/connectors/pagbank_pagseguro.png",
7469
+ category: "payments",
7470
+ authKind: "oauth",
7471
+ direction: "inbound",
7472
+ planes: ["sync"],
7473
+ docsUrl: "https://developer.pagbank.com.br/docs/connect-authorization",
7474
+ permissions: receivablePermissions("PagBank"),
7475
+ fields: [],
7476
+ settings: PAGBANK_SETTINGS,
7477
+ howItSyncs: [
7478
+ "De tempos em tempos, o que o PagBank recebeu vira lan\xE7amento no Financeiro.",
7479
+ "Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem no PagBank.",
7480
+ "A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
7481
+ ],
7482
+ ...connectionStatus(
7483
+ PAGBANK_CONNECTOR_ID,
7484
+ (connection) => ({
7485
+ connected: !!connection?.active,
7486
+ detail: methodsDetail2(METHOD_LABELS3, connection?.settings?.methods),
7487
+ plane: "sync",
7488
+ ...settingsStatus(PAGBANK_SETTINGS, connection)
7489
+ }),
7490
+ { store: () => store3 }
7491
+ ),
7492
+ ...connectionSettings(store3),
7493
+ ...connectionRuns(store3),
7494
+ startOAuth: fayzConnectorOAuth(PAGBANK_CONNECTOR_ID),
7495
+ saveConnection: async () => {
7496
+ await store3.save({ active: true });
7497
+ },
7498
+ disconnect: () => store3.disconnect(),
7499
+ disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
7500
+ };
7501
+ var CIELO_CONNECTOR_ID = "cielo";
7502
+ var CIELO_FIELDS = [
7503
+ { key: "merchantId", label: "Merchant ID", type: "text", secret: true, placeholder: "Cielo \u2192 Gest\xE3o API \u2192 Credencial API" },
7504
+ { key: "merchantKey", label: "Merchant Key", type: "password", placeholder: "Mostrada uma \xFAnica vez, ao gerar" }
7505
+ ];
7506
+ var METHOD_LABELS4 = {
7507
+ CreditCard: "Cr\xE9dito",
7508
+ DebitCard: "D\xE9bito",
7509
+ Pix: "Pix"
7510
+ };
7511
+ var CIELO_SETTINGS = [
7512
+ methodsSetting([
7513
+ { value: "CreditCard", label: METHOD_LABELS4.CreditCard },
7514
+ { value: "DebitCard", label: METHOD_LABELS4.DebitCard },
7515
+ { value: "Pix", label: METHOD_LABELS4.Pix }
7516
+ ]),
7517
+ SETTLE_ON_SETTING
7518
+ ];
7519
+ var store4 = createConnectionStore(CIELO_CONNECTOR_ID, {
7520
+ fields: CIELO_FIELDS,
7521
+ settings: CIELO_SETTINGS
7522
+ });
7523
+ var cieloConnectorDef = {
7524
+ id: CIELO_CONNECTOR_ID,
7525
+ hostPluginId: "financial",
7526
+ name: "Cielo",
7527
+ description: "Traga para o Financeiro tudo que entrou na Cielo \u2014 cr\xE9dito, d\xE9bito e Pix \u2014 com a taxa e a data do repasse.",
7528
+ icon: "CreditCard",
7529
+ // The catalogue carries no `cielo` row yet, so the mark is declared here.
7530
+ logoUrl: "https://assets.fayz.ai/connectors/cielo.jpg",
7531
+ category: "payments",
7532
+ authKind: "api-key",
7533
+ direction: "inbound",
7534
+ planes: ["sync"],
7535
+ docsUrl: "https://developercielo.github.io/manual/cielo-ecommerce",
7536
+ providerInstall: { url: "https://www.cielo.com.br/" },
7537
+ permissions: receivablePermissions("Cielo"),
7538
+ fields: CIELO_FIELDS,
7539
+ settings: CIELO_SETTINGS,
7540
+ howItSyncs: [
7541
+ "De tempos em tempos, o que a Cielo recebeu vira lan\xE7amento no Financeiro.",
7542
+ "Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem na Cielo.",
7543
+ "A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
7544
+ ],
7545
+ ...connectionStatus(
7546
+ CIELO_CONNECTOR_ID,
7547
+ (connection) => ({
7548
+ connected: !!connection?.active,
7549
+ detail: methodsDetail2(METHOD_LABELS4, connection?.settings?.methods),
7550
+ plane: "sync",
7551
+ ...settingsStatus(CIELO_SETTINGS, connection)
7552
+ }),
7553
+ { store: () => store4 }
7554
+ ),
7555
+ ...connectionSettings(store4),
7556
+ ...connectionRuns(store4),
7557
+ // Reached only after the vault accepted the pair; neither half arrives here.
7558
+ saveConnection: async () => {
7559
+ await store4.save({ active: true });
7560
+ },
7561
+ disconnect: () => store4.disconnect(),
7562
+ disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
7563
+ };
7564
+ var STONE_CONNECTOR_ID = "stone";
7565
+ var STONE_FIELDS = [
7566
+ { key: "stoneCode", label: "Stone Code", type: "text", placeholder: "N\xFAmero de afilia\xE7\xE3o da sua loja" },
7567
+ { key: "secretKey", label: "Secret Key", type: "password", placeholder: "Stone \u2192 painel \u2192 chaves de API" }
7568
+ ];
7569
+ var METHOD_LABELS5 = {
7570
+ credit: "Cr\xE9dito",
7571
+ debit: "D\xE9bito",
7572
+ pix: "Pix",
7573
+ voucher: "Vale-refei\xE7\xE3o"
7574
+ };
7575
+ var STONE_SETTINGS = [
7576
+ methodsSetting([
7577
+ { value: "credit", label: METHOD_LABELS5.credit },
7578
+ { value: "debit", label: METHOD_LABELS5.debit },
7579
+ { value: "pix", label: METHOD_LABELS5.pix },
7580
+ { value: "voucher", label: METHOD_LABELS5.voucher }
7581
+ ]),
7582
+ SETTLE_ON_SETTING
7583
+ ];
7584
+ var store5 = createConnectionStore(STONE_CONNECTOR_ID, {
7585
+ fields: STONE_FIELDS,
7586
+ settings: STONE_SETTINGS
7587
+ });
7588
+ var stoneConnectorDef = {
7589
+ id: STONE_CONNECTOR_ID,
7590
+ hostPluginId: "financial",
7591
+ name: "Stone",
7592
+ description: "Traga para o Financeiro tudo que entrou na Stone \u2014 cr\xE9dito, d\xE9bito, Pix e vale \u2014 com a taxa e a data do repasse.",
7593
+ icon: "CreditCard",
7594
+ category: "payments",
7595
+ authKind: "api-key",
7596
+ direction: "inbound",
7597
+ planes: ["sync"],
7598
+ docsUrl: "https://docs.stone.com.br",
7599
+ permissions: receivablePermissions("Stone"),
7600
+ fields: STONE_FIELDS,
7601
+ settings: STONE_SETTINGS,
7602
+ howItSyncs: [
7603
+ "De tempos em tempos, o que a Stone recebeu vira lan\xE7amento no Financeiro.",
7604
+ "Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem na Stone.",
7605
+ "A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
7606
+ ],
7607
+ ...connectionStatus(
7608
+ STONE_CONNECTOR_ID,
7609
+ (connection) => ({
7610
+ connected: !!connection?.active,
7611
+ detail: methodsDetail2(METHOD_LABELS5, connection?.settings?.methods),
7612
+ plane: "sync",
7613
+ ...settingsStatus(STONE_SETTINGS, connection)
7614
+ }),
7615
+ { store: () => store5 }
7616
+ ),
7617
+ ...connectionSettings(store5),
7618
+ ...connectionRuns(store5),
7619
+ saveConnection: async (values) => {
7620
+ await store5.save({
7621
+ active: true,
7622
+ providerAccountKey: values.stoneCode ?? null,
7623
+ settings: { stoneCode: values.stoneCode }
7624
+ });
7625
+ },
7626
+ disconnect: () => store5.disconnect(),
7627
+ disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
7628
+ };
7660
7629
  var STATUS_STYLE = {
7661
7630
  paid: "bg-success/15 text-success",
7662
7631
  partial: "bg-warning/15 text-warning",
@@ -7811,8 +7780,6 @@ function resolveConfig(options) {
7811
7780
  // Opt-in: only shown when an app explicitly enables fiscal-invoice tracking.
7812
7781
  notasFiscais: options?.modules?.notasFiscais === true
7813
7782
  },
7814
- // Opt-in: only render the plugin's own quick-add buttons/FAB when the host asks.
7815
- quickAdd: options?.quickAdd === true,
7816
7783
  labels: { ...DEFAULT_LABELS, ...options?.labels },
7817
7784
  currency: { ...DEFAULT_CURRENCY, ...options?.currency },
7818
7785
  moduleNav: options?.moduleNav,
@@ -7834,12 +7801,12 @@ function createFinancialPlugin(options) {
7834
7801
  registerTranslations(financialLocales);
7835
7802
  registerMessageDefaults(buildFinancialMessageDefaults());
7836
7803
  const provider = options?.dataProvider ?? createSafeFinancialProvider();
7837
- const store2 = createFinancialStore(provider);
7838
- const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store: store2 });
7804
+ const store6 = createFinancialStore(provider);
7805
+ const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store: store6 });
7839
7806
  const registries = [...financialRegistries, ...options?.settingsRegistries ?? []];
7840
- const PersonStatementBound = (props) => React18__default.createElement(PersonStatementWidget, { person: props.item, provider, config });
7807
+ const PersonStatementBound = (props) => React17.createElement(PersonStatementWidget, { person: props.item, provider, config });
7841
7808
  PersonStatementBound.displayName = "FinancialPersonStatement";
7842
- const PageComponent = () => React18__default.createElement(FinancialPage, { config, provider, store: store2, registries });
7809
+ const PageComponent = () => React17.createElement(FinancialPage, { config, provider, store: store6, registries });
7843
7810
  return {
7844
7811
  id: "financial",
7845
7812
  name: config.labels.pageTitle,
@@ -8045,10 +8012,10 @@ function createFinancialPlugin(options) {
8045
8012
  label: "Financial",
8046
8013
  icon: "DollarSign",
8047
8014
  component: (() => {
8048
- const FinancialSettingsTab = () => React18__default.createElement(PluginSettingsPanel, {
8015
+ const FinancialSettingsTab = () => React17.createElement(PluginSettingsPanel, {
8049
8016
  title: "Financial Settings",
8050
8017
  subtitle: "Payment methods, accounts, and configurations",
8051
- generalSettings: React18__default.createElement(FinancialGeneralSettings),
8018
+ generalSettings: React17.createElement(FinancialGeneralSettings),
8052
8019
  registries,
8053
8020
  hostPluginId: "financial",
8054
8021
  routeBase: "/settings/financial"
@@ -8063,7 +8030,18 @@ function createFinancialPlugin(options) {
8063
8030
  // Stripe is the first connector this module implements rather than merely
8064
8031
  // shelves — it stays in `integrations` too, because the union is what the
8065
8032
  // catalogue row joins on.
8066
- connectors: [stripeConnectorDef],
8033
+ // Stripe emits the receivable; the four below bring back what was actually
8034
+ // received, which is the other half of a conciliação. Mercado Pago and
8035
+ // PagBank broker the merchant's consent, so they ask for nothing; Cielo and
8036
+ // Stone issue their credential TO the merchant and have no consent flow to
8037
+ // broker — see src/integrations/acquirers/ACQUIRERS.md.
8038
+ connectors: [
8039
+ stripeConnectorDef,
8040
+ mercadoPagoConnectorDef,
8041
+ pagBankConnectorDef,
8042
+ cieloConnectorDef,
8043
+ stoneConnectorDef
8044
+ ],
8067
8045
  // TecnoSpeed is live already — the openbanking addon's `plugbank`
8068
8046
  // connector IS this slug. AbacatePay and Pagar.me were named as what
8069
8047
  // having Financeiro should put on the shelf; Mercado Pago, Bling and Stone
@@ -8073,6 +8051,6 @@ function createFinancialPlugin(options) {
8073
8051
  };
8074
8052
  }
8075
8053
 
8076
- export { FINANCIAL_NAV_FEATURES, buildFinancialDomainEvents, buildFinancialMessageDefaults, createFinancialPlugin, createMockFinancialProvider, createSafeFinancialProvider, openQuickAdd };
8054
+ export { FINANCIAL_NAV_FEATURES, buildFinancialDomainEvents, buildFinancialMessageDefaults, createFinancialPlugin, createMockFinancialProvider, createSafeFinancialProvider };
8077
8055
  //# sourceMappingURL=index.js.map
8078
8056
  //# sourceMappingURL=index.js.map