@fayz-ai/plugin-financial 0.9.3 → 0.11.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 (57) hide show
  1. package/README.md +2 -2
  2. package/dist/components/PaymentModal.d.ts +3 -1
  3. package/dist/components/PaymentModal.d.ts.map +1 -1
  4. package/dist/components/ReceiptPicker.d.ts +10 -0
  5. package/dist/components/ReceiptPicker.d.ts.map +1 -0
  6. package/dist/context.d.ts +0 -2
  7. package/dist/context.d.ts.map +1 -1
  8. package/dist/data/mock.d.ts.map +1 -1
  9. package/dist/data/registries.d.ts.map +1 -1
  10. package/dist/data/supabase.d.ts.map +1 -1
  11. package/dist/index.d.ts +0 -8
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1135 -1053
  14. package/dist/index.js.map +1 -1
  15. package/dist/integrations/acquirers/shared.d.ts +15 -0
  16. package/dist/integrations/acquirers/shared.d.ts.map +1 -0
  17. package/dist/integrations/cielo/connectorDef.d.ts +6 -0
  18. package/dist/integrations/cielo/connectorDef.d.ts.map +1 -0
  19. package/dist/integrations/mercadopago/connectorDef.d.ts +5 -0
  20. package/dist/integrations/mercadopago/connectorDef.d.ts.map +1 -0
  21. package/dist/integrations/pagbank/connectorDef.d.ts +5 -0
  22. package/dist/integrations/pagbank/connectorDef.d.ts.map +1 -0
  23. package/dist/integrations/stone/connectorDef.d.ts +6 -0
  24. package/dist/integrations/stone/connectorDef.d.ts.map +1 -0
  25. package/dist/lib/accounts.d.ts +16 -0
  26. package/dist/lib/accounts.d.ts.map +1 -0
  27. package/dist/lib/settlements.d.ts +22 -0
  28. package/dist/lib/settlements.d.ts.map +1 -0
  29. package/dist/locales/en.d.ts.map +1 -1
  30. package/dist/locales/pt-BR.d.ts.map +1 -1
  31. package/dist/migrations/index.d.ts +4 -0
  32. package/dist/migrations/index.d.ts.map +1 -1
  33. package/dist/store.d.ts +1 -2
  34. package/dist/store.d.ts.map +1 -1
  35. package/dist/types.d.ts +12 -31
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/views/CardsView.d.ts +3 -1
  38. package/dist/views/CardsView.d.ts.map +1 -1
  39. package/dist/views/CommissionsView.d.ts +7 -0
  40. package/dist/views/CommissionsView.d.ts.map +1 -1
  41. package/dist/views/FinancialPage.d.ts +2 -0
  42. package/dist/views/FinancialPage.d.ts.map +1 -1
  43. package/dist/views/InvoiceDetailView.d.ts.map +1 -1
  44. package/dist/views/InvoiceFormView.d.ts +3 -1
  45. package/dist/views/InvoiceFormView.d.ts.map +1 -1
  46. package/dist/views/InvoiceListView.d.ts +3 -1
  47. package/dist/views/InvoiceListView.d.ts.map +1 -1
  48. package/dist/views/PayablesView.d.ts.map +1 -1
  49. package/dist/views/ReceivablesView.d.ts.map +1 -1
  50. package/dist/views/StatementsView.d.ts.map +1 -1
  51. package/package.json +8 -7
  52. package/dist/data/capability.test.d.ts +0 -2
  53. package/dist/data/capability.test.d.ts.map +0 -1
  54. package/dist/lib/quick-add.d.ts +0 -13
  55. package/dist/lib/quick-add.d.ts.map +0 -1
  56. package/dist/views/QuickTransactionForm.d.ts +0 -16
  57. 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, ConfirmDialog, 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, useLookupCreate, 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, Users, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, ChevronDown, Clock, Paperclip, CheckCheck, X, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, Camera, CircleAlert, CircleEllipsis, Pencil, CalendarDays, 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';
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';
@@ -105,22 +104,22 @@ function MobileSummary() {
105
104
  }, [statement]);
106
105
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
107
106
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
108
- /* @__PURE__ */ jsxs("div", { className: "col-span-2 rounded-xl border bg-card p-4", children: [
107
+ /* @__PURE__ */ jsxs("div", { className: "col-span-2 rounded-card border bg-card p-4", children: [
109
108
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.summary.totalBalance") }),
110
109
  /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-2xl font-semibold", children: fmt(summary?.totalBalance ?? 0) })
111
110
  ] }),
112
- /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-card p-3", children: [
111
+ /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card p-3", children: [
113
112
  /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.summary.income") }),
114
113
  /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-base font-semibold text-success", children: fmt(inflow) })
115
114
  ] }),
116
- /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-card p-3", children: [
115
+ /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card p-3", children: [
117
116
  /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.summary.expenses") }),
118
117
  /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-base font-semibold text-destructive", children: fmt(outflow) })
119
118
  ] })
120
119
  ] }),
121
120
  groups.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-dashed py-12 text-center text-sm text-muted-foreground", children: t2("financial.summary.noTransactions") }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: groups.map(([date, rows]) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
122
121
  /* @__PURE__ */ jsx("p", { className: "px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: dateLabel(date) }),
123
- /* @__PURE__ */ jsx("div", { className: "divide-y overflow-hidden rounded-xl border bg-card", children: rows.map((e) => {
122
+ /* @__PURE__ */ jsx("div", { className: "divide-y overflow-hidden rounded-card border bg-card", children: rows.map((e) => {
124
123
  const isCredit = e.movement.direction === "credit";
125
124
  const title = e.invoice?.contactName || e.movement.notes || t2("financial.summary.transactionsTitle");
126
125
  const Icon = categoryIcon(title);
@@ -160,488 +159,14 @@ 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;
162
+
163
+ // src/lib/accounts.ts
164
+ function postableAccounts(accounts) {
165
+ const parentIds = new Set(accounts.map((a) => a.parentId).filter(Boolean));
166
+ return accounts.filter((a) => a.nodeType !== "group" && !parentIds.has(a.id));
642
167
  }
643
- function usePendingQuickAdd() {
644
- return React18.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
168
+ function accountLabel(account) {
169
+ return `${account.code} - ${account.name}`;
645
170
  }
646
171
  var STATUS_OPTIONS = [
647
172
  { value: "open", labelKey: "financial.invoice.statusOpen", color: "bg-info-soft text-info-soft-foreground", icon: CircleDashed },
@@ -728,21 +253,26 @@ function useInvoiceColumns(currency) {
728
253
  }
729
254
  ], [currency, t2]);
730
255
  }
731
- function InvoiceListView({ direction, onNew, onEdit }) {
256
+ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
732
257
  const t2 = useTranslation();
733
258
  const can = usePermissionOptional();
734
- const onNewGated = can("financial", "create") ? onNew : void 0;
259
+ const canCreate = can("financial", "create");
260
+ const canEdit = can("financial", "edit");
261
+ const onNewGated = canCreate ? onNew : void 0;
262
+ const onNewReceiptGated = canCreate ? onNewReceipt : void 0;
735
263
  const { currency } = useFinancialConfig();
736
264
  const provider = useFinancialProvider();
737
265
  const invoices = useFinancialStore((s) => s.invoices);
738
266
  useFinancialStore((s) => s.invoicesTotal);
739
267
  const invoicesLoading = useFinancialStore((s) => s.invoicesLoading);
740
268
  const fetchInvoices = useFinancialStore((s) => s.fetchInvoices);
269
+ const fetchSummary = useFinancialStore((s) => s.fetchSummary);
741
270
  const [search, setSearch] = useState("");
742
271
  const [statusFilter, setStatusFilter] = useState([]);
743
272
  const [accountId, setAccountId] = useState("");
744
273
  const [costCenterId, setCostCenterId] = useState("");
745
274
  const [accounts, setAccounts] = useState([]);
275
+ const postable = useMemo(() => postableAccounts(accounts), [accounts]);
746
276
  const [costCenters, setCostCenters] = useState([]);
747
277
  const [datePreset, setDatePreset] = useState("all");
748
278
  const [dateFrom, setDateFrom] = useState("");
@@ -778,16 +308,17 @@ function InvoiceListView({ direction, onNew, onEdit }) {
778
308
  active = false;
779
309
  };
780
310
  }, [provider]);
311
+ const query = useMemo(() => ({
312
+ direction,
313
+ status: statusFilter.length > 0 ? statusFilter : void 0,
314
+ search: search || void 0,
315
+ accountId: accountId || void 0,
316
+ costCenterId: costCenterId || void 0,
317
+ dateRange
318
+ }), [direction, statusFilter, search, accountId, costCenterId, dateRange]);
781
319
  useEffect(() => {
782
- fetchInvoices({
783
- direction,
784
- status: statusFilter.length > 0 ? statusFilter : void 0,
785
- search: search || void 0,
786
- accountId: accountId || void 0,
787
- costCenterId: costCenterId || void 0,
788
- dateRange
789
- });
790
- }, [direction, statusFilter, search, accountId, costCenterId, dateRange]);
320
+ fetchInvoices(query);
321
+ }, [query]);
791
322
  const filtered = invoices.filter((inv) => inv.direction === direction);
792
323
  const columns = useInvoiceColumns(currency);
793
324
  const tags = useMemo(
@@ -806,6 +337,46 @@ function InvoiceListView({ direction, onNew, onEdit }) {
806
337
  openAmount = totalAmount - paidAmount;
807
338
  return { totalAmount, paidAmount, openAmount };
808
339
  }, [filtered]);
340
+ const [cancelTarget, setCancelTarget] = useState(null);
341
+ const [bulkBusy, setBulkBusy] = useState(false);
342
+ const confirmBulkCancel = useCallback(async () => {
343
+ const targets = (cancelTarget ?? []).filter((inv) => inv.status !== "cancelled");
344
+ setBulkBusy(true);
345
+ let done = 0;
346
+ let failed = 0;
347
+ for (const invoice of targets) {
348
+ try {
349
+ await provider.cancelInvoice(invoice.id);
350
+ done++;
351
+ } catch (err) {
352
+ failed++;
353
+ console.warn("[financial] cancelInvoice failed", invoice.id, err);
354
+ }
355
+ }
356
+ setBulkBusy(false);
357
+ setCancelTarget(null);
358
+ await Promise.all([fetchInvoices(query), fetchSummary()]);
359
+ if (done > 0) toast.success(t2("financial.invoice.bulkCancelDone", { count: String(done) }));
360
+ if (failed > 0) toast.error(t2("financial.invoice.bulkCancelFailed", { count: String(failed) }));
361
+ }, [cancelTarget, provider, fetchInvoices, fetchSummary, query, t2]);
362
+ const bulkActions = useMemo(() => {
363
+ if (!canEdit) return void 0;
364
+ return [{
365
+ id: "cancel",
366
+ label: t2("financial.invoice.bulkCancel"),
367
+ icon: Ban,
368
+ destructive: true,
369
+ disabled: bulkBusy,
370
+ onSelect: (rows) => {
371
+ const targets = rows.filter((inv) => inv.status !== "cancelled");
372
+ if (targets.length === 0) {
373
+ toast.error(t2("financial.invoice.bulkCancelNone"));
374
+ return;
375
+ }
376
+ setCancelTarget(targets);
377
+ }
378
+ }];
379
+ }, [canEdit, bulkBusy, t2]);
809
380
  const receivedLabel = direction === "debit" ? t2("financial.invoice.summaryPaid") : t2("financial.invoice.summaryReceived");
810
381
  const pendingLabel = direction === "debit" ? t2("financial.invoice.summaryPayable") : t2("financial.invoice.summaryReceivable");
811
382
  const exportRows = () => {
@@ -857,7 +428,7 @@ function InvoiceListView({ direction, onNew, onEdit }) {
857
428
  children: t2("financial.invoice.filterClear")
858
429
  }
859
430
  ),
860
- accounts.length > 0 && /* @__PURE__ */ jsx(
431
+ postable.length > 0 && /* @__PURE__ */ jsx(
861
432
  FilterChip,
862
433
  {
863
434
  icon: Landmark,
@@ -865,9 +436,9 @@ function InvoiceListView({ direction, onNew, onEdit }) {
865
436
  allLabel: t2("financial.invoice.filterAllAccounts"),
866
437
  value: accountId || void 0,
867
438
  onChange: (next) => setAccountId(next ?? ""),
868
- options: accounts.map((account) => ({
439
+ options: postable.map((account) => ({
869
440
  value: account.id,
870
- label: `${account.code} - ${account.name}`
441
+ label: accountLabel(account)
871
442
  }))
872
443
  }
873
444
  ),
@@ -886,6 +457,29 @@ function InvoiceListView({ direction, onNew, onEdit }) {
886
457
  }
887
458
  )
888
459
  ] });
460
+ const newActions = onNewGated ? /* @__PURE__ */ jsx(
461
+ QuickActionsButton,
462
+ {
463
+ variant: "default",
464
+ label: t2("financial.invoice.new"),
465
+ actions: [
466
+ {
467
+ id: "manual",
468
+ label: t2("financial.invoice.newManual"),
469
+ description: t2("financial.invoice.newManualHint"),
470
+ icon: "FileText",
471
+ action: onNewGated
472
+ },
473
+ ...onNewReceiptGated ? [{
474
+ id: "receipt",
475
+ label: t2("financial.quickTx.sendReceipt"),
476
+ description: t2("financial.quickTx.receiptCapture"),
477
+ icon: "Camera",
478
+ action: onNewReceiptGated
479
+ }] : []
480
+ ]
481
+ }
482
+ ) : void 0;
889
483
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
890
484
  /* @__PURE__ */ jsx(
891
485
  ListView,
@@ -904,8 +498,8 @@ function InvoiceListView({ direction, onNew, onEdit }) {
904
498
  filters: listFilters,
905
499
  onExport: filtered.length > 0 ? exportRows : void 0,
906
500
  exportLabel: t2("crud.export.action"),
907
- newLabel: t2("financial.invoice.new"),
908
- onNew: onNewGated,
501
+ headerActions: newActions,
502
+ bulkActions,
909
503
  onRowClick: (row) => onEdit?.(row.id),
910
504
  emptyIcon: FileText,
911
505
  emptyMessage: t2("financial.invoice.noInvoices"),
@@ -928,9 +522,53 @@ function InvoiceListView({ direction, onNew, onEdit }) {
928
522
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: pendingLabel }),
929
523
  /* @__PURE__ */ jsx("span", { className: "font-semibold text-warning", children: formatCurrency(summary.openAmount, currency) })
930
524
  ] })
931
- ] })
525
+ ] }),
526
+ /* @__PURE__ */ jsx(
527
+ ConfirmDialog,
528
+ {
529
+ open: cancelTarget !== null,
530
+ variant: "destructive",
531
+ title: t2("financial.invoice.bulkCancelTitle", { count: String(cancelTarget?.length ?? 0) }),
532
+ description: t2("financial.invoice.cancelDesc"),
533
+ confirmLabel: t2("financial.invoice.yesCancel"),
534
+ cancelLabel: t2("financial.invoice.keepInvoice"),
535
+ loading: bulkBusy,
536
+ onConfirm: () => void confirmBulkCancel(),
537
+ onCancel: () => setCancelTarget(null)
538
+ }
539
+ )
932
540
  ] });
933
541
  }
542
+ function useReceiptPicker(onPick) {
543
+ const t2 = useTranslation();
544
+ const ref = useRef(null);
545
+ const input = /* @__PURE__ */ jsx(
546
+ "input",
547
+ {
548
+ ref,
549
+ type: "file",
550
+ accept: "image/*",
551
+ capture: "environment",
552
+ className: "hidden",
553
+ onChange: (e) => {
554
+ const file = e.target.files?.[0];
555
+ e.target.value = "";
556
+ if (!file) return;
557
+ if (!file.type.startsWith("image/")) {
558
+ toast.error(t2("financial.quickTx.receiptInvalid"));
559
+ return;
560
+ }
561
+ const reader = new FileReader();
562
+ reader.onload = () => {
563
+ if (typeof reader.result === "string") onPick(reader.result);
564
+ };
565
+ reader.onerror = () => toast.error(t2("financial.quickTx.receiptInvalid"));
566
+ reader.readAsDataURL(file);
567
+ }
568
+ }
569
+ );
570
+ return { open: () => ref.current?.click(), input };
571
+ }
934
572
  var ACCOUNT_TYPES_FOR_TX = {
935
573
  cash: ["cash_register"],
936
574
  credit_card: ["credit_card", "bank_account"],
@@ -964,7 +602,7 @@ var ICON_COLORS = {
964
602
  bank_transfer: "text-warning",
965
603
  check: "text-muted-foreground"
966
604
  };
967
- function PaymentModal({ movement, onClose, onPaid }) {
605
+ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
968
606
  const t2 = useTranslation();
969
607
  const { currency } = useFinancialConfig();
970
608
  const payMovement = useFinancialStore((s) => s.payMovement);
@@ -1035,6 +673,8 @@ function PaymentModal({ movement, onClose, onPaid }) {
1035
673
  setSaving(true);
1036
674
  try {
1037
675
  await payMovement({
676
+ // O comprovante viaja com o pagamento — é dele que ele é prova.
677
+ metadata: receiptUrl ? { receiptUrl } : void 0,
1038
678
  movementId: movement.id,
1039
679
  amount,
1040
680
  paymentDate,
@@ -1094,7 +734,7 @@ function PaymentModal({ movement, onClose, onPaid }) {
1094
734
  "button",
1095
735
  {
1096
736
  onClick: () => setExpandMethodPicker(true),
1097
- className: `flex items-center gap-2 mt-1.5 w-full rounded-lg border px-3 py-2.5 text-left transition-colors hover:bg-muted/30 ${TYPE_COLORS[txType] ?? ""}`,
737
+ className: `flex items-center gap-2 mt-1.5 w-full rounded-button border px-3 py-2.5 text-left transition-colors hover:bg-muted/30 ${TYPE_COLORS[txType] ?? ""}`,
1098
738
  "data-active": "true",
1099
739
  children: [
1100
740
  SelectedIcon && /* @__PURE__ */ jsx(SelectedIcon, { className: `h-4 w-4 ${ICON_COLORS[txType] ?? ""}` }),
@@ -1125,7 +765,7 @@ function PaymentModal({ movement, onClose, onPaid }) {
1125
765
  {
1126
766
  "data-active": isActive,
1127
767
  onClick: () => handleSelectType(type.id),
1128
- className: `flex flex-col items-center gap-1.5 rounded-lg border px-2 py-3 text-center transition-all ${TYPE_COLORS[tt] ?? TYPE_COLORS.cash}`,
768
+ className: `flex flex-col items-center gap-1.5 rounded-button border px-2 py-3 text-center transition-all ${TYPE_COLORS[tt] ?? TYPE_COLORS.cash}`,
1129
769
  children: [
1130
770
  /* @__PURE__ */ jsx(Icon, { className: `h-5 w-5 ${isActive ? ICON_COLORS[tt] ?? "" : "text-muted-foreground"}` }),
1131
771
  /* @__PURE__ */ jsx("span", { className: `text-[11px] font-medium leading-tight ${isActive ? "text-foreground" : "text-muted-foreground"}`, children: type.name })
@@ -1311,18 +951,38 @@ var formId = 1;
1311
951
  function fid() {
1312
952
  return `f${formId++}`;
1313
953
  }
1314
- function InvoiceFormView({ direction, editId, onSaved }) {
954
+ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1315
955
  const t2 = useTranslation();
1316
956
  const config = useFinancialConfig();
1317
957
  const { currency, itemTypes, locations } = config;
1318
958
  const provider = useFinancialProvider();
1319
959
  const hasItemLookups = itemTypes.some((tp) => config.entityLookups[tp.value]);
1320
960
  const defaultItemKind = itemTypes.find((tp) => tp.value === "other")?.value ?? itemTypes.find((tp) => !config.entityLookups[tp.value])?.value ?? itemTypes[0]?.value ?? "other";
961
+ const bankAccounts = useFinancialStore((s) => s.bankAccounts);
962
+ const paymentMethodTypes = useFinancialStore((s) => s.paymentMethodTypes);
963
+ const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
964
+ const fetchPaymentMethods = useFinancialStore((s) => s.fetchPaymentMethods);
965
+ const payMovement = useFinancialStore((s) => s.payMovement);
1321
966
  const createInvoice = useFinancialStore((s) => s.createInvoice);
1322
967
  const guardMovements = useLimitGuard("movements_month");
1323
968
  const can = usePermissionOptional();
1324
969
  const hasLocations = locations.length > 0;
1325
970
  const isEdit = !!editId;
971
+ useEffect(() => {
972
+ if (isEdit) return;
973
+ void fetchBankAccounts();
974
+ void fetchPaymentMethods();
975
+ }, [isEdit]);
976
+ useEffect(() => {
977
+ let cancelled = false;
978
+ void provider.getChartOfAccounts().then((rows) => {
979
+ if (!cancelled) setAccounts(rows);
980
+ }).catch(() => {
981
+ });
982
+ return () => {
983
+ cancelled = true;
984
+ };
985
+ }, [provider]);
1326
986
  const [loaded, setLoaded] = useState(!editId);
1327
987
  const [loading, setLoading] = useState(!!editId);
1328
988
  const [contactName, setContactName] = useState("");
@@ -1332,11 +992,32 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1332
992
  const [fiscalNumber, setFiscalNumber] = useState("");
1333
993
  const [observations, setObservations] = useState("");
1334
994
  const [items, setItems] = useState([]);
995
+ const [accounts, setAccounts] = useState([]);
996
+ const postable = useMemo(() => postableAccounts(accounts), [accounts]);
1335
997
  const [expandedItemId, setExpandedItemId] = useState(null);
1336
998
  const [addingItem, setAddingItem] = useState(false);
1337
999
  const [installments, setInstallments] = useState([]);
1338
1000
  const [savedMovements, setSavedMovements] = useState([]);
1339
1001
  const [payingMovement, setPayingMovement] = useState(null);
1002
+ const [pendingReceipt, setPendingReceipt] = useState();
1003
+ const contactCreate = useLookupCreate(config.contactLookup, (created) => {
1004
+ setContactId(created.id);
1005
+ setContactName(created.label);
1006
+ });
1007
+ const [receiptTarget, setReceiptTarget] = useState(null);
1008
+ const receiptPicker = useReceiptPicker((dataUrl) => {
1009
+ const i = receiptTarget;
1010
+ setReceiptTarget(null);
1011
+ if (i == null) return;
1012
+ if (isEdit) {
1013
+ const saved = savedMovements[i];
1014
+ if (!saved) return;
1015
+ setPendingReceipt(dataUrl);
1016
+ setPayingMovement(saved);
1017
+ return;
1018
+ }
1019
+ setInstallments((prev) => prev.map((inst, idx) => idx === i ? { ...inst, receiptUrl: dataUrl } : inst));
1020
+ });
1340
1021
  const [saving, setSaving] = useState(false);
1341
1022
  const [menuOpen, setMenuOpen] = useState(false);
1342
1023
  const [confirmCancel, setConfirmCancel] = useState(false);
@@ -1383,7 +1064,9 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1383
1064
  quantity: it.quantity,
1384
1065
  unitPrice: it.unitPrice,
1385
1066
  discount: it.discount,
1386
- surcharge: it.surcharge
1067
+ surcharge: it.surcharge,
1068
+ accountId: it.accountId,
1069
+ costCenterId: it.costCenterId
1387
1070
  })));
1388
1071
  const movements = await provider.getMovements({ direction: invoice.direction });
1389
1072
  const invoiceMovements = movements.data.filter((m) => m.invoiceId === editId && m.movementKind === "bill").sort((a, b) => (a.installmentNumber ?? 0) - (b.installmentNumber ?? 0));
@@ -1443,6 +1126,21 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1443
1126
  function removeItem(id) {
1444
1127
  setItems(items.filter((it) => it._id !== id));
1445
1128
  }
1129
+ useEffect(() => {
1130
+ if (isEdit || totalAmount <= 0 || installments.length > 0) return;
1131
+ generateInstallments(1);
1132
+ }, [isEdit, totalAmount, installments.length]);
1133
+ const receiptAutoOpened = useRef(false);
1134
+ useEffect(() => {
1135
+ if (!focusReceipt || isEdit || receiptAutoOpened.current) return;
1136
+ if (installments.length === 0 || installments[0]?.receiptUrl) return;
1137
+ receiptAutoOpened.current = true;
1138
+ const id = window.setTimeout(() => {
1139
+ setReceiptTarget(0);
1140
+ receiptPicker.open();
1141
+ }, 250);
1142
+ return () => window.clearTimeout(id);
1143
+ }, [focusReceipt, isEdit, installments.length]);
1446
1144
  function generateInstallments(count) {
1447
1145
  if (count < 1 || totalAmount <= 0) return;
1448
1146
  const perInstallment = Math.floor(totalAmount * 100 / count) / 100;
@@ -1466,6 +1164,10 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1466
1164
  toast.error(t2("common.formIncomplete"));
1467
1165
  return;
1468
1166
  }
1167
+ if (!isEdit && installments.some((i) => i.receiptUrl && (!i.payMethodTypeId || !i.payAccountId))) {
1168
+ toast.error(t2("financial.invoiceForm.paidNeedsMethod"));
1169
+ return;
1170
+ }
1469
1171
  if (!isEdit && await guardMovements(Math.max(1, installments.length)) === "blocked") return;
1470
1172
  setSaving(true);
1471
1173
  try {
@@ -1485,6 +1187,11 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1485
1187
  installmentNumber: 1
1486
1188
  });
1487
1189
  }
1190
+ installments.forEach((inst, i) => {
1191
+ if (inst.receiptUrl && movInputs[i]) {
1192
+ movInputs[i] = { ...movInputs[i], metadata: { ...movInputs[i].metadata ?? {}, receiptUrl: inst.receiptUrl } };
1193
+ }
1194
+ });
1488
1195
  if (isEdit && editId) {
1489
1196
  await provider.updateInvoice(editId, {
1490
1197
  invoiceDate,
@@ -1509,6 +1216,22 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1509
1216
  installments: movInputs
1510
1217
  });
1511
1218
  invalidateLimit("movements_month");
1219
+ const paidOnes = installments.map((inst, i) => ({ inst, number: i + 1 })).filter(({ inst }) => inst.receiptUrl);
1220
+ if (paidOnes.length > 0) {
1221
+ const movements = await provider.getMovements({ direction, status: "pending" });
1222
+ const bills = movements.data.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
1223
+ for (const { inst, number } of paidOnes) {
1224
+ const bill = bills.find((m) => (m.installmentNumber ?? 1) === number);
1225
+ if (!bill) continue;
1226
+ await payMovement({
1227
+ movementId: bill.id,
1228
+ amount: bill.amount,
1229
+ paymentDate: inst.dueDate,
1230
+ paymentMethodTypeId: inst.payMethodTypeId || void 0,
1231
+ bankAccountId: inst.payAccountId || void 0
1232
+ });
1233
+ }
1234
+ }
1512
1235
  onSaved?.(invoice.id);
1513
1236
  }
1514
1237
  } finally {
@@ -1541,7 +1264,7 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1541
1264
  });
1542
1265
  const title = isEdit ? t2("financial.invoiceForm.editTitle") : t2("financial.invoiceForm.newTitle");
1543
1266
  const subtitle = isEdit ? t2("financial.invoiceForm.editing") : t2("financial.invoiceForm.creating");
1544
- if (loading) return /* @__PURE__ */ jsx(OrderFormSkeleton, {});
1267
+ if (loading) return /* @__PURE__ */ jsx(OrderFormSkeleton, { parentLabel: direction === "debit" ? t2("financial.nav.payables") : t2("financial.nav.receivables"), onBack: onSaved });
1545
1268
  const money = (v) => formatCurrency(v, currency);
1546
1269
  const actions = isEdit ? /* @__PURE__ */ jsxs("div", { className: "relative", ref: menuRef, children: [
1547
1270
  /* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", onClick: () => setMenuOpen(!menuOpen), "aria-label": "More actions", children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" }) }),
@@ -1563,113 +1286,148 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1563
1286
  ] }) : null;
1564
1287
  const paidSoFar = savedMovements.reduce((sum, m) => sum + (m.paidAmount ?? 0), 0);
1565
1288
  const hasPayments = paidSoFar > 0;
1566
- const installmentsEditor = /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoiceForm.installments"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1567
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: [1, 2, 3, 6, 12].map((n) => /* @__PURE__ */ jsxs(
1568
- "button",
1569
- {
1570
- type: "button",
1571
- onClick: () => generateInstallments(n),
1572
- disabled: totalAmount <= 0,
1573
- className: `rounded-full px-2.5 py-1 text-[10px] font-medium transition-colors disabled:opacity-30 ${installments.length === n ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/80"}`,
1574
- children: [
1575
- n,
1576
- "x"
1577
- ]
1578
- },
1579
- n
1580
- )) }),
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
1586
- ] }),
1587
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: money(inst.amount) })
1588
- ] }),
1589
- /* @__PURE__ */ jsx(
1590
- DatePicker,
1289
+ const scheduleSection = /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1290
+ hasPayments ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.invoiceForm.scheduleLocked") }) : (
1291
+ // Atalhos de parcelamento — 1x, 2x, 3x, 6x, 12x cobrem quase tudo.
1292
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: [1, 2, 3, 6, 12].map((n) => /* @__PURE__ */ jsxs(
1293
+ "button",
1591
1294
  {
1592
- value: inst.dueDate,
1593
- onChange: (v) => {
1594
- const updated = [...installments];
1595
- updated[i] = { ...inst, dueDate: v };
1596
- setInstallments(updated);
1597
- }
1598
- }
1599
- )
1600
- ] }, inst._id)) })
1601
- ] }) });
1602
- const scheduleSection = isEdit ? /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1603
- hasPayments && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.invoiceForm.scheduleLocked") }),
1604
- !hasPayments && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: [1, 2, 3, 6, 12].map((n) => /* @__PURE__ */ jsxs(
1605
- "button",
1606
- {
1607
- type: "button",
1608
- onClick: () => generateInstallments(n),
1609
- disabled: totalAmount <= 0,
1610
- className: `rounded-full px-2.5 py-1 text-[10px] font-medium transition-colors disabled:opacity-30 ${installments.length === n ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/80"}`,
1611
- children: [
1612
- n,
1613
- "x"
1614
- ]
1615
- },
1616
- n
1617
- )) }),
1295
+ type: "button",
1296
+ onClick: () => generateInstallments(n),
1297
+ disabled: totalAmount <= 0,
1298
+ className: `rounded-full px-2.5 py-1 text-[10px] font-medium transition-colors disabled:opacity-30 ${installments.length === n ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/80"}`,
1299
+ children: [
1300
+ n,
1301
+ "x"
1302
+ ]
1303
+ },
1304
+ n
1305
+ )) })
1306
+ ),
1618
1307
  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: "divide-y rounded border", children: installments.map((inst, i) => {
1308
+ const patch = (next) => {
1309
+ const updated = [...installments];
1310
+ updated[i] = { ...inst, ...next };
1311
+ setInstallments(updated);
1312
+ };
1619
1313
  const saved = savedMovements[i];
1620
1314
  const isPaid = saved?.status === "paid";
1621
1315
  const isPartial = saved?.status === "partial";
1622
1316
  const StatusIcon = isPaid ? CircleCheckBig : CircleDashed;
1623
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 px-3 py-2.5", children: [
1624
- /* @__PURE__ */ jsxs("span", { className: "w-6 shrink-0 text-xs font-medium text-muted-foreground", children: [
1625
- "#",
1626
- i + 1
1627
- ] }),
1628
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums", children: money(inst.amount) }),
1629
- saved && /* @__PURE__ */ jsxs("span", { className: `inline-flex shrink-0 items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${isPaid ? "bg-success-soft text-success-soft-foreground" : isPartial ? "bg-warning-soft text-warning-soft-foreground" : "bg-muted text-muted-foreground"}`, children: [
1630
- /* @__PURE__ */ jsx(StatusIcon, { className: "h-2.5 w-2.5" }),
1631
- t2(isPaid ? "financial.invoice.statusPaid" : isPartial ? "financial.invoice.statusPartial" : "financial.invoice.statusPending")
1317
+ const canAttach = isEdit ? !!saved && !isPaid : true;
1318
+ return /* @__PURE__ */ jsxs("div", { children: [
1319
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 px-3 py-2", children: [
1320
+ /* @__PURE__ */ jsxs("span", { className: "w-6 shrink-0 text-xs font-medium text-muted-foreground", children: [
1321
+ "#",
1322
+ i + 1
1323
+ ] }),
1324
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums", children: money(inst.amount) }),
1325
+ saved && /* @__PURE__ */ jsxs("span", { className: `inline-flex shrink-0 items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${isPaid ? "bg-success-soft text-success-soft-foreground" : isPartial ? "bg-warning-soft text-warning-soft-foreground" : "bg-muted text-muted-foreground"}`, children: [
1326
+ /* @__PURE__ */ jsx(StatusIcon, { className: "h-2.5 w-2.5" }),
1327
+ t2(isPaid ? "financial.invoice.statusPaid" : isPartial ? "financial.invoice.statusPartial" : "financial.invoice.statusPending")
1328
+ ] }),
1329
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [
1330
+ !hasPayments ? /* @__PURE__ */ jsx(
1331
+ DatePicker,
1332
+ {
1333
+ value: inst.dueDate,
1334
+ variant: "chip",
1335
+ onChange: (v) => patch({ dueDate: v })
1336
+ }
1337
+ ) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: inst.dueDate }),
1338
+ canAttach && /* @__PURE__ */ jsx(
1339
+ "button",
1340
+ {
1341
+ type: "button",
1342
+ onClick: () => {
1343
+ setReceiptTarget(i);
1344
+ receiptPicker.open();
1345
+ },
1346
+ title: t2("financial.quickTx.receiptCapture"),
1347
+ "aria-label": t2("financial.quickTx.receiptCapture"),
1348
+ className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-md transition-colors ${inst.receiptUrl ? "text-success hover:bg-success-soft" : "text-muted-foreground hover:bg-muted hover:text-foreground"}`,
1349
+ children: inst.receiptUrl ? /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Camera, { className: "h-3.5 w-3.5" })
1350
+ }
1351
+ ),
1352
+ saved && !isPaid && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setPayingMovement(saved), children: [
1353
+ /* @__PURE__ */ jsx(DollarSign, { className: "mr-1 h-3.5 w-3.5" }),
1354
+ t2("financial.invoice.pay")
1355
+ ] })
1356
+ ] })
1632
1357
  ] }),
1633
- /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
1634
- !hasPayments ? /* @__PURE__ */ jsx(
1635
- DatePicker,
1358
+ !isEdit && inst.receiptUrl && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t bg-muted/20 px-3 py-2", children: [
1359
+ /* @__PURE__ */ jsx(
1360
+ "img",
1361
+ {
1362
+ src: inst.receiptUrl,
1363
+ alt: t2("financial.quickTx.receipt"),
1364
+ className: "h-8 w-8 shrink-0 rounded border object-cover"
1365
+ }
1366
+ ),
1367
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] font-medium text-success", children: t2("financial.invoiceForm.willBePaid") }),
1368
+ /* @__PURE__ */ jsxs(
1369
+ "select",
1370
+ {
1371
+ value: inst.payMethodTypeId ?? "",
1372
+ onChange: (e) => patch({ payMethodTypeId: e.target.value }),
1373
+ className: "min-w-[8rem] flex-1 rounded-input border border-input bg-card px-2 py-1 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
1374
+ children: [
1375
+ /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectMethod") }),
1376
+ paymentMethodTypes.map((tp) => /* @__PURE__ */ jsx("option", { value: tp.id, children: tp.name }, tp.id))
1377
+ ]
1378
+ }
1379
+ ),
1380
+ /* @__PURE__ */ jsxs(
1381
+ "select",
1636
1382
  {
1637
- value: inst.dueDate,
1638
- variant: "chip",
1639
- onChange: (v) => {
1640
- const updated = [...installments];
1641
- updated[i] = { ...inst, dueDate: v };
1642
- setInstallments(updated);
1643
- }
1383
+ value: inst.payAccountId ?? (bankAccounts.length === 1 ? bankAccounts[0].id : ""),
1384
+ onChange: (e) => patch({ payAccountId: e.target.value }),
1385
+ className: "min-w-[8rem] flex-1 rounded-input border border-input bg-card px-2 py-1 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
1386
+ children: [
1387
+ /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectAccount") }),
1388
+ bankAccounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
1389
+ ]
1644
1390
  }
1645
- ) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: inst.dueDate }),
1646
- saved && !isPaid && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setPayingMovement(saved), children: [
1647
- /* @__PURE__ */ jsx(DollarSign, { className: "mr-1 h-3.5 w-3.5" }),
1648
- t2("financial.invoice.pay")
1649
- ] })
1391
+ ),
1392
+ /* @__PURE__ */ jsx(
1393
+ "button",
1394
+ {
1395
+ type: "button",
1396
+ onClick: () => patch({ receiptUrl: void 0, payMethodTypeId: void 0, payAccountId: void 0 }),
1397
+ "aria-label": t2("financial.quickTx.receiptRemove"),
1398
+ className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
1399
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
1400
+ }
1401
+ )
1650
1402
  ] })
1651
1403
  ] }, inst._id);
1652
1404
  }) })
1653
- ] }) }) : null;
1405
+ ] }) });
1654
1406
  const sideSections = /* @__PURE__ */ jsxs(Fragment, { children: [
1655
- /* @__PURE__ */ jsx(OrderFormBlock, { title: direction === "debit" ? t2("financial.invoiceForm.payTo") : t2("financial.invoiceForm.receiveFrom"), children: /* @__PURE__ */ jsx(
1656
- SearchSelect,
1657
- {
1658
- required: true,
1659
- value: contactId ?? "",
1660
- displayValue: contactName,
1661
- onChange: (id, opt) => {
1662
- setContactId(id || void 0);
1663
- setContactName(opt?.label ?? "");
1664
- },
1665
- onSearch: async (q) => {
1666
- if (!config.contactLookup) return [];
1667
- const results = await config.contactLookup.search(q);
1668
- return results.map((r) => ({ id: r.id, label: r.label, subtitle: r.subtitle, group: r.group, data: r }));
1669
- },
1670
- placeholder: t2("financial.invoiceForm.searchType")
1671
- }
1672
- ) }),
1407
+ /* @__PURE__ */ jsxs(OrderFormBlock, { title: direction === "debit" ? t2("financial.invoiceForm.payTo") : t2("financial.invoiceForm.receiveFrom"), children: [
1408
+ /* @__PURE__ */ jsx(
1409
+ SearchSelect,
1410
+ {
1411
+ required: true,
1412
+ value: contactId ?? "",
1413
+ displayValue: contactName,
1414
+ onChange: (id, opt) => {
1415
+ setContactId(id || void 0);
1416
+ setContactName(opt?.label ?? "");
1417
+ },
1418
+ onSearch: async (q) => {
1419
+ if (!config.contactLookup) return [];
1420
+ const results = await config.contactLookup.search(q);
1421
+ return results.map((r) => ({ id: r.id, label: r.label, subtitle: r.subtitle, group: r.group, data: r }));
1422
+ },
1423
+ createLabel: t2("financial.invoiceForm.createContact"),
1424
+ clearLabel: t2("financial.invoiceForm.clear"),
1425
+ onCreate: contactCreate.onCreate,
1426
+ placeholder: t2("financial.invoiceForm.searchType")
1427
+ }
1428
+ ),
1429
+ contactCreate.modal
1430
+ ] }),
1673
1431
  /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.details"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1674
1432
  hasLocations && /* @__PURE__ */ jsx(LocationSelector, { locations, value: unitId, onChange: setUnitId }),
1675
1433
  /* @__PURE__ */ jsxs("div", { children: [
@@ -1690,7 +1448,6 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1690
1448
  )
1691
1449
  ] })
1692
1450
  ] }) }),
1693
- !isEdit && installmentsEditor,
1694
1451
  /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoiceForm.observations"), children: /* @__PURE__ */ jsx(
1695
1452
  "textarea",
1696
1453
  {
@@ -1756,6 +1513,21 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1756
1513
  removeItem(item._id);
1757
1514
  if (expandedItemId === item._id) setExpandedItemId(null);
1758
1515
  },
1516
+ extraFields: postable.length > 0 && /* @__PURE__ */ jsx("div", { className: "fz-fields fz-fields-2", children: /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1517
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium text-muted-foreground", children: t2("financial.invoiceForm.account") }),
1518
+ /* @__PURE__ */ jsxs(
1519
+ "select",
1520
+ {
1521
+ value: item.accountId ?? "",
1522
+ onChange: (e) => updateItem(item._id, { accountId: e.target.value || void 0 }),
1523
+ className: "mt-0.5 w-full rounded-input border border-input bg-card px-2.5 py-1.5 text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
1524
+ children: [
1525
+ /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.accountNone") }),
1526
+ postable.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: accountLabel(a) }, a.id))
1527
+ ]
1528
+ }
1529
+ )
1530
+ ] }) }),
1759
1531
  entityLookups: config.entityLookups,
1760
1532
  showTypeSelector: hasItemLookups,
1761
1533
  allowSurcharge: true,
@@ -1781,13 +1553,19 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1781
1553
  ]
1782
1554
  }
1783
1555
  ),
1556
+ receiptPicker.input,
1784
1557
  payingMovement && /* @__PURE__ */ jsx(
1785
1558
  PaymentModal,
1786
1559
  {
1787
1560
  movement: payingMovement,
1788
- onClose: () => setPayingMovement(null),
1561
+ receiptUrl: pendingReceipt,
1562
+ onClose: () => {
1563
+ setPayingMovement(null);
1564
+ setPendingReceipt(void 0);
1565
+ },
1789
1566
  onPaid: () => {
1790
1567
  setPayingMovement(null);
1568
+ setPendingReceipt(void 0);
1791
1569
  void (async () => {
1792
1570
  if (!editId) return;
1793
1571
  const movements = await provider.getMovements({ direction });
@@ -1826,6 +1604,27 @@ function InvoiceFormView({ direction, editId, onSaved }) {
1826
1604
  ` })
1827
1605
  ] });
1828
1606
  }
1607
+
1608
+ // src/lib/settlements.ts
1609
+ function groupPaymentsByBill(bills, payments) {
1610
+ const byBillId = /* @__PURE__ */ new Map();
1611
+ for (const bill of bills) byBillId.set(bill.id, []);
1612
+ const unattributed = [];
1613
+ for (const payment of payments) {
1614
+ if (payment.settlesMovementId) {
1615
+ const bucket = byBillId.get(payment.settlesMovementId);
1616
+ if (bucket) bucket.push(payment);
1617
+ else unattributed.push(payment);
1618
+ continue;
1619
+ }
1620
+ const derived = bills.find(
1621
+ (bill) => (bill.installmentNumber ?? null) === (payment.installmentNumber ?? null)
1622
+ );
1623
+ if (derived) byBillId.get(derived.id).push(payment);
1624
+ else unattributed.push(payment);
1625
+ }
1626
+ return { byBillId, unattributed };
1627
+ }
1829
1628
  var STATUS_COLORS = {
1830
1629
  open: { bg: "bg-info-soft text-info-soft-foreground", icon: CircleDashed, labelKey: "financial.invoice.statusOpen", tone: "info" },
1831
1630
  pending: { bg: "bg-warning-soft text-warning-soft-foreground", icon: CircleDashed, labelKey: "financial.invoice.statusPending", tone: "warning" },
@@ -1854,6 +1653,13 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1854
1653
  const [payments, setPayments] = useState([]);
1855
1654
  const [loading, setLoading] = useState(true);
1856
1655
  const [payingMovement, setPayingMovement] = useState(null);
1656
+ const [receiptFor, setReceiptFor] = useState(null);
1657
+ const [pendingReceipt, setPendingReceipt] = useState();
1658
+ const receiptPicker = useReceiptPicker((dataUrl) => {
1659
+ setPendingReceipt(dataUrl);
1660
+ setPayingMovement(receiptFor);
1661
+ setReceiptFor(null);
1662
+ });
1857
1663
  const [expandedMovId, setExpandedMovId] = useState(null);
1858
1664
  const [menuOpen, setMenuOpen] = useState(false);
1859
1665
  const [confirmCancel, setConfirmCancel] = useState(false);
@@ -1915,7 +1721,7 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1915
1721
  }
1916
1722
  }
1917
1723
  if (loading) {
1918
- return /* @__PURE__ */ jsx(OrderDetailSkeleton, {});
1724
+ return /* @__PURE__ */ jsx(OrderDetailSkeleton, { parentLabel, onBack });
1919
1725
  }
1920
1726
  if (!invoice) {
1921
1727
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
@@ -1923,6 +1729,7 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1923
1729
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t2("financial.invoice.notFoundDesc") })
1924
1730
  ] });
1925
1731
  }
1732
+ const settlements = groupPaymentsByBill(movements, payments);
1926
1733
  const statusConfig = STATUS_COLORS[invoice.status] ?? STATUS_COLORS.pending;
1927
1734
  statusConfig.icon;
1928
1735
  const totalPaid = movements.reduce((sum, m) => sum + m.paidAmount, 0);
@@ -1997,7 +1804,6 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1997
1804
  {
1998
1805
  title: invoice.fiscalNumber ? `#${invoice.fiscalNumber}` : invoice.contactName ?? invoice.invoiceDate,
1999
1806
  badges: [{ label: t2(statusConfig.labelKey), tone: statusConfig.tone }],
2000
- subtitle: invoice.invoiceDate,
2001
1807
  onBack,
2002
1808
  backLabel: parentLabel,
2003
1809
  actions,
@@ -2016,150 +1822,183 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
2016
1822
  mainSections: (
2017
1823
  // O cronograma no MESMO bloco que os itens e o pagamento — antes o título
2018
1824
  // ficava fora do cartão, e a seção lia como se fosse de outra tela.
2019
- /* @__PURE__ */ jsx(OrderDetailBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsx("div", { className: "-mx-4 -mb-4", children: movements.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-4 text-center text-sm text-muted-foreground", children: t2("financial.invoice.noInstallments") }) : /* @__PURE__ */ jsx("div", { className: "divide-y border-t", children: movements.map((mov) => {
2020
- const remaining = mov.amount - mov.paidAmount;
2021
- const isPaid = mov.status === "paid";
2022
- const isPartial = mov.status === "partial";
2023
- const hasPaidDetails = isPaid || isPartial;
2024
- const isExpanded = expandedMovId === mov.id;
2025
- const movStatus = STATUS_COLORS[mov.status] ?? STATUS_COLORS.pending;
2026
- const MovIcon = movStatus.icon;
2027
- const instPayments = payments.filter((p) => (p.installmentNumber ?? null) === (mov.installmentNumber ?? null));
2028
- return /* @__PURE__ */ jsxs("div", { children: [
2029
- /* @__PURE__ */ jsxs(
2030
- "div",
2031
- {
2032
- className: `flex items-center gap-3 px-4 py-3 ${hasPaidDetails ? "cursor-pointer hover:bg-muted/20 transition-colors" : ""}`,
2033
- onClick: hasPaidDetails ? () => handleExpandMov(mov.id) : void 0,
2034
- children: [
2035
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-muted-foreground w-6 shrink-0", children: [
2036
- "#",
2037
- mov.installmentNumber
2038
- ] }),
2039
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2040
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2041
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium tabular-nums", children: formatCurrency(mov.amount, currency) }),
2042
- /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-0.5 rounded-full px-1.5 py-0.5 text-[9px] font-medium capitalize ${movStatus.bg}`, children: [
2043
- /* @__PURE__ */ jsx(MovIcon, { className: "h-2 w-2" }),
2044
- " ",
2045
- t2(movStatus.labelKey)
2046
- ] })
1825
+ /* @__PURE__ */ jsx(OrderDetailBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "-mx-4 -mb-4", children: [
1826
+ movements.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-4 text-center text-sm text-muted-foreground", children: t2("financial.invoice.noInstallments") }) : /* @__PURE__ */ jsx("div", { className: "divide-y border-t", children: movements.map((mov) => {
1827
+ const remaining = mov.amount - mov.paidAmount;
1828
+ const isPaid = mov.status === "paid";
1829
+ const isPartial = mov.status === "partial";
1830
+ const hasPaidDetails = isPaid || isPartial;
1831
+ const isExpanded = expandedMovId === mov.id;
1832
+ const movStatus = STATUS_COLORS[mov.status] ?? STATUS_COLORS.pending;
1833
+ const MovIcon = movStatus.icon;
1834
+ const instPayments = settlements.byBillId.get(mov.id) ?? [];
1835
+ return /* @__PURE__ */ jsxs("div", { children: [
1836
+ /* @__PURE__ */ jsxs(
1837
+ "div",
1838
+ {
1839
+ className: `flex items-center gap-3 px-4 py-3 ${hasPaidDetails ? "cursor-pointer hover:bg-muted/20 transition-colors" : ""}`,
1840
+ onClick: hasPaidDetails ? () => handleExpandMov(mov.id) : void 0,
1841
+ children: [
1842
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-muted-foreground w-6 shrink-0", children: [
1843
+ "#",
1844
+ mov.installmentNumber
2047
1845
  ] }),
2048
- /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
2049
- t2("financial.invoice.due"),
2050
- " ",
2051
- mov.dueDate,
2052
- mov.paidAmount > 0 && !isPaid && ` \xB7 ${t2("financial.invoice.paidLabel")} ${formatCurrency(mov.paidAmount, currency)}`,
2053
- mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
2054
- ] })
2055
- ] }),
2056
- !isPaid && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
2057
- "button",
2058
- {
2059
- onClick: (e) => {
2060
- e.stopPropagation();
2061
- setPayingMovement(mov);
2062
- },
2063
- className: "inline-flex items-center gap-1 rounded-lg bg-primary border border-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors shrink-0",
2064
- children: [
2065
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
2066
- t2("financial.invoice.pay"),
2067
- " ",
2068
- remaining < mov.amount ? formatCurrency(remaining, currency) : ""
2069
- ]
2070
- }
2071
- ),
2072
- isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
2073
- "button",
2074
- {
2075
- onClick: (e) => {
2076
- e.stopPropagation();
2077
- setPayingMovement(mov);
2078
- },
2079
- className: "inline-flex items-center gap-1 rounded-lg border px-3 py-1.5 text-xs font-medium hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors shrink-0",
2080
- children: [
2081
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
2082
- t2("financial.invoice.pay"),
1846
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1847
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1848
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium tabular-nums", children: formatCurrency(mov.amount, currency) }),
1849
+ /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-0.5 rounded-full px-1.5 py-0.5 text-[9px] font-medium capitalize ${movStatus.bg}`, children: [
1850
+ /* @__PURE__ */ jsx(MovIcon, { className: "h-2 w-2" }),
1851
+ " ",
1852
+ t2(movStatus.labelKey)
1853
+ ] })
1854
+ ] }),
1855
+ /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
1856
+ t2("financial.invoice.due"),
2083
1857
  " ",
2084
- formatCurrency(remaining, currency)
2085
- ]
2086
- }
2087
- ),
2088
- hasPaidDetails && /* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 text-muted-foreground shrink-0 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })
2089
- ]
2090
- }
2091
- ),
2092
- /* @__PURE__ */ jsx(
2093
- "div",
2094
- {
2095
- className: "grid transition-all duration-200 ease-out",
2096
- style: { gridTemplateRows: isExpanded ? "1fr" : "0fr" },
2097
- children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-2.5 pl-14", children: detailsLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
2098
- /* @__PURE__ */ jsx("div", { className: "h-3 w-20 rounded bg-muted/40 animate-pulse" }),
2099
- /* @__PURE__ */ jsx("div", { className: "h-3 w-16 rounded bg-muted/30 animate-pulse" }),
2100
- /* @__PURE__ */ jsx("div", { className: "h-3 w-24 rounded bg-muted/40 animate-pulse" })
2101
- ] }) : instPayments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-[11px] text-muted-foreground", children: t2("financial.invoice.noDetails") }) : /* @__PURE__ */ jsx("div", { className: "space-y-1", children: instPayments.map((p) => {
2102
- const pMethod = paymentMethods.find((m) => m.id === p.paymentMethodId);
2103
- const pType = paymentMethodTypes.find((tp) => tp.id === (pMethod?.paymentMethodTypeId ?? p.paymentMethodTypeId));
2104
- const pAccount = bankAccounts.find((a) => a.id === p.bankAccountId);
2105
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-1 gap-y-0.5 text-[11px] text-muted-foreground", children: [
2106
- /* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
2107
- pType && /* @__PURE__ */ jsxs(Fragment, { children: [
2108
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2109
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: pType.name }),
2110
- pMethod && /* @__PURE__ */ jsxs("span", { children: [
2111
- "(",
2112
- pMethod.name,
2113
- ")"
2114
- ] })
2115
- ] }),
2116
- p.cardBrand && /* @__PURE__ */ jsxs(Fragment, { children: [
2117
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2118
- /* @__PURE__ */ jsx(CreditCard, { className: "h-3 w-3 shrink-0" }),
2119
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: p.cardBrand }),
2120
- p.cardInstallments && /* @__PURE__ */ jsxs("span", { children: [
2121
- p.cardInstallments,
2122
- "x"
1858
+ mov.dueDate,
1859
+ mov.paidAmount > 0 && !isPaid && ` \xB7 ${t2("financial.invoice.paidLabel")} ${formatCurrency(mov.paidAmount, currency)}`,
1860
+ mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
2123
1861
  ] })
2124
1862
  ] }),
2125
- pAccount && /* @__PURE__ */ jsxs(Fragment, { children: [
2126
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2127
- /* @__PURE__ */ jsx(Building2, { className: "h-3 w-3 shrink-0" }),
2128
- /* @__PURE__ */ jsx("span", { children: pAccount.name })
2129
- ] }),
2130
- (p.feeAmount ?? 0) > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2131
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2132
- /* @__PURE__ */ jsxs("span", { className: "text-warning", children: [
2133
- t2("financial.statements.columnFee"),
2134
- " ",
2135
- formatCurrency(p.feeAmount ?? 0, currency)
1863
+ !isPaid && invoice.status !== "cancelled" && /* @__PURE__ */ jsx(
1864
+ "button",
1865
+ {
1866
+ onClick: (e) => {
1867
+ e.stopPropagation();
1868
+ setReceiptFor(mov);
1869
+ receiptPicker.open();
1870
+ },
1871
+ title: t2("financial.quickTx.receiptCapture"),
1872
+ "aria-label": t2("financial.quickTx.receiptCapture"),
1873
+ 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",
1874
+ children: /* @__PURE__ */ jsx(Camera, { className: "h-3 w-3" })
1875
+ }
1876
+ ),
1877
+ !isPaid && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
1878
+ "button",
1879
+ {
1880
+ onClick: (e) => {
1881
+ e.stopPropagation();
1882
+ setPayingMovement(mov);
1883
+ },
1884
+ className: "inline-flex items-center gap-1 rounded-button bg-primary border border-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors shrink-0",
1885
+ children: [
1886
+ /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
1887
+ t2("financial.invoice.pay"),
1888
+ " ",
1889
+ remaining < mov.amount ? formatCurrency(remaining, currency) : ""
1890
+ ]
1891
+ }
1892
+ ),
1893
+ isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
1894
+ "button",
1895
+ {
1896
+ onClick: (e) => {
1897
+ e.stopPropagation();
1898
+ setPayingMovement(mov);
1899
+ },
1900
+ className: "inline-flex items-center gap-1 rounded-lg border px-3 py-1.5 text-xs font-medium hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors shrink-0",
1901
+ children: [
1902
+ /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
1903
+ t2("financial.invoice.pay"),
1904
+ " ",
1905
+ formatCurrency(remaining, currency)
1906
+ ]
1907
+ }
1908
+ ),
1909
+ hasPaidDetails && /* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 text-muted-foreground shrink-0 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })
1910
+ ]
1911
+ }
1912
+ ),
1913
+ /* @__PURE__ */ jsx(
1914
+ "div",
1915
+ {
1916
+ className: "grid transition-all duration-200 ease-out",
1917
+ style: { gridTemplateRows: isExpanded ? "1fr" : "0fr" },
1918
+ children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-2.5 pl-14", children: detailsLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1919
+ /* @__PURE__ */ jsx("div", { className: "h-3 w-20 rounded bg-muted/40 animate-pulse" }),
1920
+ /* @__PURE__ */ jsx("div", { className: "h-3 w-16 rounded bg-muted/30 animate-pulse" }),
1921
+ /* @__PURE__ */ jsx("div", { className: "h-3 w-24 rounded bg-muted/40 animate-pulse" })
1922
+ ] }) : instPayments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-[11px] text-muted-foreground", children: t2("financial.invoice.noDetails") }) : /* @__PURE__ */ jsx("div", { className: "space-y-1", children: instPayments.map((p) => {
1923
+ const pMethod = paymentMethods.find((m) => m.id === p.paymentMethodId);
1924
+ const pType = paymentMethodTypes.find((tp) => tp.id === (pMethod?.paymentMethodTypeId ?? p.paymentMethodTypeId));
1925
+ const pAccount = bankAccounts.find((a) => a.id === p.bankAccountId);
1926
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-1 gap-y-0.5 text-[11px] text-muted-foreground", children: [
1927
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
1928
+ pType && /* @__PURE__ */ jsxs(Fragment, { children: [
1929
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1930
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: pType.name }),
1931
+ pMethod && /* @__PURE__ */ jsxs("span", { children: [
1932
+ "(",
1933
+ pMethod.name,
1934
+ ")"
1935
+ ] })
1936
+ ] }),
1937
+ p.cardBrand && /* @__PURE__ */ jsxs(Fragment, { children: [
1938
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1939
+ /* @__PURE__ */ jsx(CreditCard, { className: "h-3 w-3 shrink-0" }),
1940
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: p.cardBrand }),
1941
+ p.cardInstallments && /* @__PURE__ */ jsxs("span", { children: [
1942
+ p.cardInstallments,
1943
+ "x"
1944
+ ] })
1945
+ ] }),
1946
+ pAccount && /* @__PURE__ */ jsxs(Fragment, { children: [
1947
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1948
+ /* @__PURE__ */ jsx(Building2, { className: "h-3 w-3 shrink-0" }),
1949
+ /* @__PURE__ */ jsx("span", { children: pAccount.name })
1950
+ ] }),
1951
+ (p.feeAmount ?? 0) > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1952
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1953
+ /* @__PURE__ */ jsxs("span", { className: "text-warning", children: [
1954
+ t2("financial.statements.columnFee"),
1955
+ " ",
1956
+ formatCurrency(p.feeAmount ?? 0, currency)
1957
+ ] })
1958
+ ] }),
1959
+ p.paymentDate && /* @__PURE__ */ jsxs(Fragment, { children: [
1960
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1961
+ /* @__PURE__ */ jsx("span", { children: p.paymentDate })
1962
+ ] }),
1963
+ p.notes && /* @__PURE__ */ jsxs(Fragment, { children: [
1964
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1965
+ /* @__PURE__ */ jsx("span", { className: "italic truncate max-w-[200px]", children: p.notes })
2136
1966
  ] })
2137
- ] }),
2138
- p.paymentDate && /* @__PURE__ */ jsxs(Fragment, { children: [
2139
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2140
- /* @__PURE__ */ jsx("span", { children: p.paymentDate })
2141
- ] }),
2142
- p.notes && /* @__PURE__ */ jsxs(Fragment, { children: [
2143
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2144
- /* @__PURE__ */ jsx("span", { className: "italic truncate max-w-[200px]", children: p.notes })
2145
- ] })
2146
- ] }, p.id);
2147
- }) }) }) })
2148
- }
2149
- )
2150
- ] }, mov.id);
2151
- }) }) }) })
1967
+ ] }, p.id);
1968
+ }) }) }) })
1969
+ }
1970
+ )
1971
+ ] }, mov.id);
1972
+ }) }),
1973
+ settlements.unattributed.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t bg-warning-soft/40 px-4 py-2.5", children: [
1974
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-foreground", children: t2("financial.invoice.unlinkedPayments") }),
1975
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] text-muted-foreground", children: t2("financial.invoice.unlinkedPaymentsDesc") }),
1976
+ settlements.unattributed.map((p) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
1977
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
1978
+ p.paymentDate && /* @__PURE__ */ jsxs(Fragment, { children: [
1979
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1980
+ /* @__PURE__ */ jsx("span", { children: p.paymentDate })
1981
+ ] })
1982
+ ] }, p.id))
1983
+ ] })
1984
+ ] }) })
2152
1985
  ),
2153
1986
  extraSections: sideBlocks
2154
1987
  }
2155
1988
  ),
1989
+ receiptPicker.input,
2156
1990
  payingMovement && /* @__PURE__ */ jsx(
2157
1991
  PaymentModal,
2158
1992
  {
2159
1993
  movement: payingMovement,
2160
- onClose: () => setPayingMovement(null),
1994
+ receiptUrl: pendingReceipt,
1995
+ onClose: () => {
1996
+ setPayingMovement(null);
1997
+ setPendingReceipt(void 0);
1998
+ },
2161
1999
  onPaid: () => {
2162
2000
  setPayingMovement(null);
2001
+ setPendingReceipt(void 0);
2163
2002
  loadData();
2164
2003
  }
2165
2004
  }
@@ -2212,7 +2051,7 @@ function referrerLabel(view, t2) {
2212
2051
  function PayablesView({ intent, onNavigate, previousView, back }) {
2213
2052
  const t2 = useTranslation();
2214
2053
  if (intent.mode === "new") {
2215
- return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", onSaved: (id) => onNavigate(id ? `payables-detail:${id}` : "payables-list") });
2054
+ return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", focusReceipt: intent.receipt, onSaved: (id) => onNavigate(id ? `payables-detail:${id}` : "payables-list") });
2216
2055
  }
2217
2056
  if (intent.mode === "edit" && intent.editId) {
2218
2057
  return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", editId: intent.editId, onSaved: () => onNavigate(`payables-detail:${intent.editId}`) });
@@ -2235,6 +2074,7 @@ function PayablesView({ intent, onNavigate, previousView, back }) {
2235
2074
  {
2236
2075
  direction: "debit",
2237
2076
  onNew: () => onNavigate("payables-new"),
2077
+ onNewReceipt: () => onNavigate("payables-new-receipt"),
2238
2078
  onEdit: (id) => onNavigate(`payables-detail:${id}`)
2239
2079
  }
2240
2080
  );
@@ -2242,7 +2082,7 @@ function PayablesView({ intent, onNavigate, previousView, back }) {
2242
2082
  function ReceivablesView({ intent, onNavigate, previousView, back }) {
2243
2083
  const t2 = useTranslation();
2244
2084
  if (intent.mode === "new") {
2245
- return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", onSaved: (id) => onNavigate(id ? `receivables-detail:${id}` : "receivables-list") });
2085
+ return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", focusReceipt: intent.receipt, onSaved: (id) => onNavigate(id ? `receivables-detail:${id}` : "receivables-list") });
2246
2086
  }
2247
2087
  if (intent.mode === "edit" && intent.editId) {
2248
2088
  return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", editId: intent.editId, onSaved: () => onNavigate(`receivables-detail:${intent.editId}`) });
@@ -2265,6 +2105,7 @@ function ReceivablesView({ intent, onNavigate, previousView, back }) {
2265
2105
  {
2266
2106
  direction: "credit",
2267
2107
  onNew: () => onNavigate("receivables-new"),
2108
+ onNewReceipt: () => onNavigate("receivables-new-receipt"),
2268
2109
  onEdit: (id) => onNavigate(`receivables-detail:${id}`)
2269
2110
  }
2270
2111
  );
@@ -2401,7 +2242,7 @@ function CashOpeningPanel({
2401
2242
  {
2402
2243
  onClick: handleConfirm,
2403
2244
  disabled: submitting,
2404
- className: "inline-flex items-center gap-1.5 rounded-lg bg-primary border border-primary px-4 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors disabled:opacity-50",
2245
+ className: "inline-flex items-center gap-1.5 rounded-button bg-primary border border-primary px-4 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors disabled:opacity-50",
2405
2246
  children: [
2406
2247
  /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
2407
2248
  " ",
@@ -2502,7 +2343,7 @@ function CashOpeningPanel({
2502
2343
  {
2503
2344
  onClick: () => setConfirming(true),
2504
2345
  disabled: effectiveBalance <= 0,
2505
- className: "inline-flex items-center gap-1.5 rounded-lg bg-primary border border-primary px-4 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors disabled:opacity-50",
2346
+ className: "inline-flex items-center gap-1.5 rounded-button bg-primary border border-primary px-4 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors disabled:opacity-50",
2506
2347
  children: [
2507
2348
  /* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }),
2508
2349
  " ",
@@ -2645,7 +2486,7 @@ function CashRegistersView() {
2645
2486
  }
2646
2487
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2647
2488
  /* @__PURE__ */ jsx(SubpageHeader, { title: labels.cashRegisters ?? t2("financial.cash.title"), subtitle: t2("financial.cash.subtitle") }),
2648
- /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card shadow-sm", children: [
2489
+ /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm", children: [
2649
2490
  /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-4 py-3 border-b", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2650
2491
  /* @__PURE__ */ jsx(Landmark, { className: "h-4 w-4 text-info" }),
2651
2492
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.openSessions") }),
@@ -2682,7 +2523,7 @@ function CashRegistersView() {
2682
2523
  ] }, session.id)) })
2683
2524
  ] }),
2684
2525
  openSessions.length > 0 && cashAccounts.length > openSessions.length && /* @__PURE__ */ jsx(OpenAnotherCollapsible, { cashAccounts, openSessions, currency, onOpen: handleOpen }),
2685
- /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card shadow-sm", children: [
2526
+ /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm", children: [
2686
2527
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
2687
2528
  /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-muted-foreground" }),
2688
2529
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.sessionHistory") })
@@ -2736,7 +2577,7 @@ function OpenAnotherCollapsible({
2736
2577
  const [expanded, setExpanded] = useState(false);
2737
2578
  const availableAccounts = cashAccounts.filter((a) => !openSessions.some((s) => s.bankAccountId === a.id));
2738
2579
  if (availableAccounts.length === 0) return null;
2739
- return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card shadow-sm", children: [
2580
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm", children: [
2740
2581
  /* @__PURE__ */ jsxs(
2741
2582
  "button",
2742
2583
  {
@@ -2963,7 +2804,7 @@ function StatementsView({ onNavigate }) {
2963
2804
  const e = row.original;
2964
2805
  const isTransfer = e.entryKind !== "movement";
2965
2806
  const memo = e.movement.notes;
2966
- const title = e.invoice?.contactName || (isTransfer ? t2("financial.statements.transfer") : memo || "Transaction");
2807
+ const title = e.invoice?.contactName || (isTransfer ? t2("financial.statements.transfer") : memo || t2("financial.statements.untitledEntry"));
2967
2808
  const memoLine = memo && memo !== title ? memo : void 0;
2968
2809
  const doc = e.invoice?.fiscalNumber;
2969
2810
  const inst = e.movement.installmentNumber;
@@ -3083,7 +2924,7 @@ function StatementsView({ onNavigate }) {
3083
2924
  );
3084
2925
  };
3085
2926
  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: [
2927
+ /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setShowTransfer(true), disabled: bankAccounts.length < 2, children: [
3087
2928
  /* @__PURE__ */ jsx(ArrowRightLeft, { className: "h-3.5 w-3.5" }),
3088
2929
  t2("financial.statements.newTransfer")
3089
2930
  ] }) }),
@@ -3173,7 +3014,12 @@ function CommissionsView() {
3173
3014
  const { labels } = useFinancialConfig();
3174
3015
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3175
3016
  /* @__PURE__ */ jsx(SubpageHeader, { title: labels.commissions, subtitle: t2("financial.commissions.subtitle") }),
3176
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center rounded-lg border-2 border-dashed border-muted p-12", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t2("financial.commissions.comingSoon") }) })
3017
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-muted px-6 py-12 text-center", children: [
3018
+ /* @__PURE__ */ jsx("div", { className: "mb-3 flex h-12 w-12 items-center justify-center rounded-xl bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(Users, { className: "h-6 w-6" }) }),
3019
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t2("financial.commissions.emptyTitle") }),
3020
+ /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-xs text-muted-foreground", children: t2("financial.commissions.emptyDesc") }),
3021
+ /* @__PURE__ */ jsx("p", { className: "mt-3 max-w-sm text-xs text-muted-foreground", children: t2("financial.commissions.emptyRulesHint") })
3022
+ ] })
3177
3023
  ] });
3178
3024
  }
3179
3025
  function cardMath(acc) {
@@ -3183,15 +3029,13 @@ function cardMath(acc) {
3183
3029
  const pct = limit > 0 ? Math.min(100, used / limit * 100) : 0;
3184
3030
  return { limit, used, available, pct };
3185
3031
  }
3186
- function CardsView() {
3032
+ function CardsView({ onNavigate }) {
3187
3033
  const t2 = useTranslation();
3188
3034
  const { currency } = useFinancialConfig();
3189
3035
  const bankAccounts = useFinancialStore((s) => s.bankAccounts);
3190
3036
  const bankAccountsLoading = useFinancialStore((s) => s.bankAccountsLoading);
3191
3037
  const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
3192
3038
  const [tab, setTab] = useState("open");
3193
- const [addOpen, setAddOpen] = useState(false);
3194
- const [addCardId, setAddCardId] = useState();
3195
3039
  useEffect(() => {
3196
3040
  fetchBankAccounts();
3197
3041
  }, []);
@@ -3212,13 +3056,12 @@ function CardsView() {
3212
3056
  }
3213
3057
  return { totalAvailable, totalBills };
3214
3058
  }, [openCards]);
3215
- function openAddExpense(cardId) {
3216
- setAddCardId(cardId);
3217
- setAddOpen(true);
3059
+ function openAddExpense(_cardId) {
3060
+ onNavigate?.("payables-new");
3218
3061
  }
3219
3062
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3220
3063
  /* @__PURE__ */ jsx(SubpageHeader, { title: t2("financial.cards.title"), subtitle: t2("financial.cards.subtitle") }),
3221
- bankAccountsLoading && cards.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-sm text-muted-foreground", children: "Loading..." }) : cards.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center rounded-2xl border-2 border-dashed border-muted", children: [
3064
+ bankAccountsLoading && cards.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-sm text-muted-foreground", children: t2("financial.cards.loading") }) : cards.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center rounded-2xl border-2 border-dashed border-muted", children: [
3222
3065
  /* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-amber-300 to-yellow-600 mb-3 text-white shadow-sm", children: /* @__PURE__ */ jsx(CreditCard, { className: "h-6 w-6" }) }),
3223
3066
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t2("financial.cards.noCards") }),
3224
3067
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 max-w-xs", children: t2("financial.cards.noCardsHint") })
@@ -3301,16 +3144,7 @@ function CardsView() {
3301
3144
  ] })
3302
3145
  ] }, card.id);
3303
3146
  }) })
3304
- ] }),
3305
- /* @__PURE__ */ jsx(
3306
- QuickTransactionForm,
3307
- {
3308
- open: addOpen,
3309
- onOpenChange: setAddOpen,
3310
- defaultType: "expense",
3311
- defaultAccountId: addCardId
3312
- }
3313
- )
3147
+ ] })
3314
3148
  ] });
3315
3149
  }
3316
3150
  function ReconciliationView() {
@@ -3389,12 +3223,12 @@ function ReconciliationView() {
3389
3223
  ] })
3390
3224
  ] })
3391
3225
  ] }),
3392
- lines.length === 0 && !loading ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card shadow-sm py-16 text-center", children: [
3226
+ lines.length === 0 && !loading ? /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm py-16 text-center", children: [
3393
3227
  /* @__PURE__ */ jsx(Check, { className: "h-8 w-8 text-success/40 mx-auto mb-2" }),
3394
3228
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t2("financial.reconciliation.empty") }),
3395
3229
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: t2("financial.reconciliation.emptyHint") })
3396
3230
  ] }) : /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
3397
- /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card shadow-sm overflow-hidden", children: [
3231
+ /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm overflow-hidden", children: [
3398
3232
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
3399
3233
  /* @__PURE__ */ jsx(Landmark, { className: "h-4 w-4 text-muted-foreground" }),
3400
3234
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.reconciliation.bankLines") })
@@ -3425,7 +3259,7 @@ function ReconciliationView() {
3425
3259
  );
3426
3260
  }) })
3427
3261
  ] }),
3428
- /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card shadow-sm overflow-hidden", children: [
3262
+ /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm overflow-hidden", children: [
3429
3263
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
3430
3264
  /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 text-muted-foreground" }),
3431
3265
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.reconciliation.candidates") })
@@ -3692,8 +3526,12 @@ function FinancialOnboarding({ onComplete }) {
3692
3526
  }
3693
3527
  function parseIntent(activeView) {
3694
3528
  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 };
3529
+ const m = base.match(/^(payables|receivables)-(new-receipt|new|list|detail|edit)$/);
3530
+ if (m) {
3531
+ const raw = m[2];
3532
+ const mode = raw === "new-receipt" ? "new" : raw;
3533
+ return { view: m[1], mode, editId: id, receipt: raw === "new-receipt" };
3534
+ }
3697
3535
  return { view: activeView };
3698
3536
  }
3699
3537
  function buildNav(config, activeView, navigate, t2) {
@@ -3713,7 +3551,7 @@ function buildNav(config, activeView, navigate, t2) {
3713
3551
  icon: "ArrowUpCircle",
3714
3552
  active: activeView.startsWith("payables"),
3715
3553
  children: [
3716
- { id: "payables-new", label: t2("financial.nav.new"), active: activeView === "payables-new", onClick: () => navigate("payables-new") },
3554
+ { id: "payables-new", label: t2("financial.nav.new"), active: activeView.startsWith("payables-new"), onClick: () => navigate("payables-new") },
3717
3555
  { id: "payables-list", label: t2("financial.nav.list"), active: activeView === "payables-list", onClick: () => navigate("payables-list") },
3718
3556
  { id: "payables-recurring", label: t2("financial.nav.recurringExpenses"), active: activeView === "payables-recurring", onClick: () => navigate("payables-recurring") }
3719
3557
  ]
@@ -3726,7 +3564,7 @@ function buildNav(config, activeView, navigate, t2) {
3726
3564
  icon: "ArrowDownCircle",
3727
3565
  active: activeView.startsWith("receivables"),
3728
3566
  children: [
3729
- { id: "receivables-new", label: t2("financial.nav.new"), active: activeView === "receivables-new", onClick: () => navigate("receivables-new") },
3567
+ { id: "receivables-new", label: t2("financial.nav.new"), active: activeView.startsWith("receivables-new"), onClick: () => navigate("receivables-new") },
3730
3568
  { id: "receivables-list", label: t2("financial.nav.list"), active: activeView === "receivables-list", onClick: () => navigate("receivables-list") }
3731
3569
  ]
3732
3570
  });
@@ -3770,10 +3608,8 @@ function buildNav(config, activeView, navigate, t2) {
3770
3608
  id: "commissions",
3771
3609
  label: t2("financial.nav.commissions"),
3772
3610
  icon: "Users",
3773
- children: [
3774
- { id: "commissions-overview", label: t2("financial.nav.overview"), active: activeView === "commissions", onClick: () => navigate("commissions") },
3775
- { id: "commissions-rules", label: t2("financial.nav.rules"), active: activeView === "commissions", onClick: () => navigate("commissions") }
3776
- ]
3611
+ active: activeView === "commissions",
3612
+ onClick: () => navigate("commissions")
3777
3613
  });
3778
3614
  }
3779
3615
  if (config.modules.cards) {
@@ -3781,10 +3617,8 @@ function buildNav(config, activeView, navigate, t2) {
3781
3617
  id: "cards",
3782
3618
  label: t2("financial.nav.cards"),
3783
3619
  icon: "CreditCard",
3784
- children: [
3785
- { id: "cards-overview", label: t2("financial.nav.overview"), active: activeView === "cards", onClick: () => navigate("cards") },
3786
- { id: "cards-reconciliation", label: t2("financial.nav.reconciliation"), active: activeView === "cards", onClick: () => navigate("cards") }
3787
- ]
3620
+ active: activeView === "cards",
3621
+ onClick: () => navigate("cards")
3788
3622
  });
3789
3623
  }
3790
3624
  for (const item of items) {
@@ -3793,7 +3627,7 @@ function buildNav(config, activeView, navigate, t2) {
3793
3627
  }
3794
3628
  return items;
3795
3629
  }
3796
- function FinancialPage({ config, provider, store: store2, registries }) {
3630
+ function FinancialPage({ config, provider, store: store6, registries }) {
3797
3631
  const { view, direction, navigate, previousView, back } = useModuleNavigation("/financial", {
3798
3632
  summary: 0,
3799
3633
  "payables-list": 0,
@@ -3832,26 +3666,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3832
3666
  const intent = parseIntent(view);
3833
3667
  const isSettings = view === "settings";
3834
3668
  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
3669
  const quickActions = useMemo(() => {
3854
- if (config.quickAdd) return [];
3855
3670
  if (!canCreate) return [];
3856
3671
  const actions = [];
3857
3672
  if (config.modules.payables) {
@@ -3882,7 +3697,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3882
3697
  });
3883
3698
  }
3884
3699
  return actions;
3885
- }, [config.modules, config.quickAdd, canCreate, navigate, t2]);
3700
+ }, [config.modules, canCreate, navigate, t2]);
3886
3701
  function handleOnboardingComplete() {
3887
3702
  setOnboardingComplete(true);
3888
3703
  try {
@@ -3891,14 +3706,13 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3891
3706
  }
3892
3707
  }
3893
3708
  if (!onboardingComplete) {
3894
- return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store2, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete: handleOnboardingComplete }) });
3709
+ return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete: handleOnboardingComplete }) });
3895
3710
  }
3896
3711
  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(
3712
+ 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
3713
  PluginSettingsPanel,
3899
3714
  {
3900
3715
  title: t2("financial.settingsPage.title"),
3901
- subtitle: t2("financial.settingsPage.subtitle"),
3902
3716
  generalSettings: /* @__PURE__ */ jsx(FinancialGeneralSettings, {}),
3903
3717
  registries,
3904
3718
  hostPluginId: "financial",
@@ -3923,7 +3737,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3923
3737
  case "commissions":
3924
3738
  return /* @__PURE__ */ jsx(CommissionsView, {});
3925
3739
  case "cards":
3926
- return /* @__PURE__ */ jsx(CardsView, {});
3740
+ return /* @__PURE__ */ jsx(CardsView, { onNavigate: navigate });
3927
3741
  case "notas-fiscais":
3928
3742
  return /* @__PURE__ */ jsx(NotasFiscaisView, {});
3929
3743
  default:
@@ -3935,7 +3749,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3935
3749
  if (!feature) return renderView();
3936
3750
  return /* @__PURE__ */ jsx(EntitlementGate, { feature, fallback: /* @__PURE__ */ jsx(UpgradePrompt, { feature }), children: renderView() });
3937
3751
  }
3938
- return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store2, children: /* @__PURE__ */ jsxs(
3752
+ return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(
3939
3753
  ModulePage,
3940
3754
  {
3941
3755
  title: config.labels.pageTitle,
@@ -3945,45 +3759,15 @@ function FinancialPage({ config, provider, store: store2, registries }) {
3945
3759
  showHeader: intent.view === "summary" || view === "summary",
3946
3760
  viewKey: view,
3947
3761
  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
- ]
3762
+ headerAction: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
3763
+ ModuleActionBar,
3764
+ {
3765
+ quickActions,
3766
+ settingsPath: registries && registries.length > 0 ? "/settings/financial" : void 0,
3767
+ settingsLabel: t2("financial.settingsPage.title")
3768
+ }
3769
+ ) }),
3770
+ children: renderGatedView()
3987
3771
  }
3988
3772
  ) });
3989
3773
  }
@@ -4149,7 +3933,7 @@ function BalanceHero() {
4149
3933
  const summary = useFinancialStore((s) => s.summary);
4150
3934
  const navigate = useDashboardNavigate();
4151
3935
  useEnsureSummary();
4152
- const [hidden, setHidden] = React18__default.useState(false);
3936
+ const [hidden, setHidden] = React17.useState(false);
4153
3937
  const money = (v) => hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022" : formatCurrency(v ?? 0, currency);
4154
3938
  const inflow = summary?.monthlyInflow ?? 0;
4155
3939
  const outflow = summary?.monthlyOutflow ?? 0;
@@ -4222,7 +4006,7 @@ function categorizeSpend(notes) {
4222
4006
  function SpendByCategoryChart() {
4223
4007
  const t2 = useTranslation();
4224
4008
  const provider = useFinancialProvider();
4225
- const [rows, setRows] = React18__default.useState([]);
4009
+ const [rows, setRows] = React17.useState([]);
4226
4010
  useEffect(() => {
4227
4011
  let alive = true;
4228
4012
  void (async () => {
@@ -4265,14 +4049,14 @@ function UpcomingBills() {
4265
4049
  const { currency } = useFinancialConfig();
4266
4050
  const provider = useFinancialProvider();
4267
4051
  const dueLabel = useDueLabel();
4268
- const [bills, setBills] = React18__default.useState([]);
4052
+ const [bills, setBills] = React17.useState([]);
4269
4053
  useEffect(() => {
4270
4054
  let alive = true;
4271
4055
  void (async () => {
4272
- const pending2 = await provider.getMovements({ status: "pending" });
4056
+ const pending = await provider.getMovements({ status: "pending" });
4273
4057
  if (!alive) return;
4274
4058
  setBills(
4275
- pending2.data.filter((m) => m.direction === "debit" && m.movementKind === "bill").sort((a, b) => a.dueDate.localeCompare(b.dueDate))
4059
+ pending.data.filter((m) => m.direction === "debit" && m.movementKind === "bill").sort((a, b) => a.dueDate.localeCompare(b.dueDate))
4276
4060
  );
4277
4061
  })();
4278
4062
  return () => {
@@ -4445,7 +4229,7 @@ function createStore(options) {
4445
4229
  };
4446
4230
  }
4447
4231
  function createMockFinancialProvider(options) {
4448
- const store2 = createStore(options);
4232
+ const store6 = createStore(options);
4449
4233
  const tenantId = "mock-tenant";
4450
4234
  function recalcInvoiceStatus(invoice) {
4451
4235
  if (invoice.paidAmount >= invoice.totalAmount) {
@@ -4453,7 +4237,7 @@ function createMockFinancialProvider(options) {
4453
4237
  } else if (invoice.paidAmount > 0) {
4454
4238
  invoice.status = "partial";
4455
4239
  } else {
4456
- const movements = store2.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4240
+ const movements = store6.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4457
4241
  const hasOverdue = movements.some((m) => m.status === "overdue" || m.status === "pending" && m.dueDate < today());
4458
4242
  invoice.status = hasOverdue ? "overdue" : "open";
4459
4243
  }
@@ -4462,16 +4246,16 @@ function createMockFinancialProvider(options) {
4462
4246
  const provider = {
4463
4247
  // --- Invoices ---
4464
4248
  async getInvoices(query) {
4465
- let results = [...store2.invoices];
4249
+ let results = [...store6.invoices];
4466
4250
  if (query.direction) results = results.filter((i) => i.direction === query.direction);
4467
4251
  if (query.status) results = results.filter((i) => matchesStatus(i.status, query.status));
4468
4252
  if (query.contactId) results = results.filter((i) => i.contactId === query.contactId);
4469
4253
  if (query.accountId) {
4470
- const invoiceIds = new Set(store2.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
4254
+ const invoiceIds = new Set(store6.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
4471
4255
  results = results.filter((invoice) => invoiceIds.has(invoice.id));
4472
4256
  }
4473
4257
  if (query.costCenterId) {
4474
- const invoiceIds = new Set(store2.invoiceItems.filter((item) => item.costCenterId === query.costCenterId).map((item) => item.invoiceId));
4258
+ const invoiceIds = new Set(store6.invoiceItems.filter((item) => item.costCenterId === query.costCenterId).map((item) => item.invoiceId));
4475
4259
  results = results.filter((invoice) => invoiceIds.has(invoice.id));
4476
4260
  }
4477
4261
  if (query.dateRange) results = results.filter((i) => matchesDateRange(i.invoiceDate, query.dateRange));
@@ -4485,10 +4269,10 @@ function createMockFinancialProvider(options) {
4485
4269
  return paginate(results, query.page, query.pageSize);
4486
4270
  },
4487
4271
  async getInvoiceById(id) {
4488
- return store2.invoices.find((i) => i.id === id) ?? null;
4272
+ return store6.invoices.find((i) => i.id === id) ?? null;
4489
4273
  },
4490
4274
  async getInvoiceItems(invoiceId) {
4491
- return store2.invoiceItems.filter((i) => i.invoiceId === invoiceId);
4275
+ return store6.invoiceItems.filter((i) => i.invoiceId === invoiceId);
4492
4276
  },
4493
4277
  async createInvoice(input) {
4494
4278
  const invoiceId = uid();
@@ -4517,7 +4301,7 @@ function createMockFinancialProvider(options) {
4517
4301
  createdAt: now(),
4518
4302
  updatedAt: now()
4519
4303
  };
4520
- store2.invoices.push(invoice);
4304
+ store6.invoices.push(invoice);
4521
4305
  for (const itemInput of input.items) {
4522
4306
  const itemTotal = itemInput.quantity * itemInput.unitPrice - (itemInput.discount ?? 0) + (itemInput.surcharge ?? 0);
4523
4307
  const item = {
@@ -4536,7 +4320,7 @@ function createMockFinancialProvider(options) {
4536
4320
  metadata: itemInput.metadata,
4537
4321
  createdAt: now()
4538
4322
  };
4539
- store2.invoiceItems.push(item);
4323
+ store6.invoiceItems.push(item);
4540
4324
  }
4541
4325
  for (const movInput of input.installments) {
4542
4326
  const movement = {
@@ -4557,29 +4341,29 @@ function createMockFinancialProvider(options) {
4557
4341
  createdAt: now(),
4558
4342
  updatedAt: now()
4559
4343
  };
4560
- store2.movements.push(movement);
4344
+ store6.movements.push(movement);
4561
4345
  }
4562
4346
  return invoice;
4563
4347
  },
4564
4348
  async updateInvoice(id, data) {
4565
- const invoice = store2.invoices.find((i) => i.id === id);
4349
+ const invoice = store6.invoices.find((i) => i.id === id);
4566
4350
  if (!invoice) throw new Error(`Invoice ${id} not found`);
4567
4351
  Object.assign(invoice, data, { updatedAt: now() });
4568
4352
  return invoice;
4569
4353
  },
4570
4354
  async cancelInvoice(id) {
4571
- const invoice = store2.invoices.find((i) => i.id === id);
4355
+ const invoice = store6.invoices.find((i) => i.id === id);
4572
4356
  if (!invoice) throw new Error(`Invoice ${id} not found`);
4573
4357
  invoice.status = "cancelled";
4574
4358
  invoice.updatedAt = now();
4575
- store2.movements.filter((m) => m.invoiceId === id && m.status === "pending").forEach((m) => {
4359
+ store6.movements.filter((m) => m.invoiceId === id && m.status === "pending").forEach((m) => {
4576
4360
  m.status = "cancelled";
4577
4361
  m.updatedAt = now();
4578
4362
  });
4579
4363
  },
4580
4364
  // --- Movements ---
4581
4365
  async getMovements(query) {
4582
- let results = [...store2.movements];
4366
+ let results = [...store6.movements];
4583
4367
  if (query.direction) results = results.filter((m) => m.direction === query.direction);
4584
4368
  if (query.status) results = results.filter((m) => matchesStatus(m.status, query.status));
4585
4369
  if (query.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
@@ -4589,9 +4373,9 @@ function createMockFinancialProvider(options) {
4589
4373
  return paginate(results, query.page, query.pageSize);
4590
4374
  },
4591
4375
  async payMovement(input) {
4592
- const bill = store2.movements.find((m) => m.id === input.movementId);
4376
+ const bill = store6.movements.find((m) => m.id === input.movementId);
4593
4377
  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;
4378
+ const method = input.paymentMethodId ? store6.paymentMethods.find((pm) => pm.id === input.paymentMethodId) : void 0;
4595
4379
  const feePct = bill.direction === "credit" ? method?.discountValue ?? 0 : 0;
4596
4380
  const thisFee = round2(input.amount * (feePct / 100));
4597
4381
  const payment = {
@@ -4603,6 +4387,7 @@ function createMockFinancialProvider(options) {
4603
4387
  paidAmount: input.amount,
4604
4388
  feeAmount: thisFee,
4605
4389
  status: "paid",
4390
+ settlesMovementId: bill.id,
4606
4391
  dueDate: bill.dueDate,
4607
4392
  paymentDate: input.paymentDate,
4608
4393
  installmentNumber: bill.installmentNumber,
@@ -4613,18 +4398,19 @@ function createMockFinancialProvider(options) {
4613
4398
  cardBrand: input.cardBrand,
4614
4399
  cardInstallments: input.cardInstallments,
4615
4400
  notes: input.notes,
4401
+ metadata: input.metadata,
4616
4402
  tenantId,
4617
4403
  createdAt: now(),
4618
4404
  updatedAt: now()
4619
4405
  };
4620
- store2.movements.push(payment);
4406
+ store6.movements.push(payment);
4621
4407
  bill.paidAmount = round2(bill.paidAmount + input.amount);
4622
4408
  bill.status = bill.paidAmount >= bill.amount ? "paid" : "partial";
4623
4409
  bill.paymentDate = void 0;
4624
4410
  bill.bankAccountId = void 0;
4625
4411
  bill.updatedAt = now();
4626
4412
  if (input.bankAccountId) {
4627
- const account = store2.bankAccounts.find((a) => a.id === input.bankAccountId);
4413
+ const account = store6.bankAccounts.find((a) => a.id === input.bankAccountId);
4628
4414
  if (account) {
4629
4415
  if (bill.direction === "credit") {
4630
4416
  account.currentBalance = round2(account.currentBalance + (input.amount - thisFee));
@@ -4635,9 +4421,9 @@ function createMockFinancialProvider(options) {
4635
4421
  }
4636
4422
  }
4637
4423
  if (bill.invoiceId) {
4638
- const invoice = store2.invoices.find((i) => i.id === bill.invoiceId);
4424
+ const invoice = store6.invoices.find((i) => i.id === bill.invoiceId);
4639
4425
  if (invoice) {
4640
- const bills = store2.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4426
+ const bills = store6.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4641
4427
  invoice.paidAmount = bills.reduce((sum, m) => sum + m.paidAmount, 0);
4642
4428
  recalcInvoiceStatus(invoice);
4643
4429
  }
@@ -4645,7 +4431,7 @@ function createMockFinancialProvider(options) {
4645
4431
  return payment;
4646
4432
  },
4647
4433
  async cancelMovement(id) {
4648
- const movement = store2.movements.find((m) => m.id === id);
4434
+ const movement = store6.movements.find((m) => m.id === id);
4649
4435
  if (!movement) throw new Error(`Movement ${id} not found`);
4650
4436
  movement.status = "cancelled";
4651
4437
  movement.updatedAt = now();
@@ -4672,22 +4458,22 @@ function createMockFinancialProvider(options) {
4672
4458
  createdAt: now(),
4673
4459
  updatedAt: now()
4674
4460
  };
4675
- store2.movements.push({
4461
+ store6.movements.push({
4676
4462
  ...base,
4677
4463
  id: debitMovementId,
4678
4464
  direction: "debit",
4679
4465
  bankAccountId: input.fromAccountId,
4680
4466
  metadata: { transferId, transferRole: "out", counterAccountId: input.toAccountId }
4681
4467
  });
4682
- store2.movements.push({
4468
+ store6.movements.push({
4683
4469
  ...base,
4684
4470
  id: creditMovementId,
4685
4471
  direction: "credit",
4686
4472
  bankAccountId: input.toAccountId,
4687
4473
  metadata: { transferId, transferRole: "in", counterAccountId: input.fromAccountId }
4688
4474
  });
4689
- const from = store2.bankAccounts.find((a) => a.id === input.fromAccountId);
4690
- const to = store2.bankAccounts.find((a) => a.id === input.toAccountId);
4475
+ const from = store6.bankAccounts.find((a) => a.id === input.fromAccountId);
4476
+ const to = store6.bankAccounts.find((a) => a.id === input.toAccountId);
4691
4477
  if (from) {
4692
4478
  from.currentBalance = round2(from.currentBalance - input.amount);
4693
4479
  from.updatedAt = now();
@@ -4700,7 +4486,7 @@ function createMockFinancialProvider(options) {
4700
4486
  },
4701
4487
  // --- Bank Accounts ---
4702
4488
  async getBankAccounts() {
4703
- return store2.bankAccounts.filter((a) => a.isActive);
4489
+ return store6.bankAccounts.filter((a) => a.isActive);
4704
4490
  },
4705
4491
  async createBankAccount(data) {
4706
4492
  const account = {
@@ -4721,26 +4507,26 @@ function createMockFinancialProvider(options) {
4721
4507
  createdAt: now(),
4722
4508
  updatedAt: now()
4723
4509
  };
4724
- store2.bankAccounts.push(account);
4510
+ store6.bankAccounts.push(account);
4725
4511
  return account;
4726
4512
  },
4727
4513
  async updateBankAccount(id, data) {
4728
- const account = store2.bankAccounts.find((a) => a.id === id);
4514
+ const account = store6.bankAccounts.find((a) => a.id === id);
4729
4515
  if (!account) throw new Error(`Bank account ${id} not found`);
4730
4516
  Object.assign(account, data, { updatedAt: now() });
4731
4517
  return account;
4732
4518
  },
4733
4519
  // --- Cash Sessions ---
4734
4520
  async getCashSessions(bankAccountId) {
4735
- let results = [...store2.cashSessions];
4521
+ let results = [...store6.cashSessions];
4736
4522
  if (bankAccountId) results = results.filter((s) => s.bankAccountId === bankAccountId);
4737
4523
  return results.sort((a, b) => b.openedAt.localeCompare(a.openedAt));
4738
4524
  },
4739
4525
  async getOpenSession(bankAccountId) {
4740
- return store2.cashSessions.find((s) => s.bankAccountId === bankAccountId && s.status === "open") ?? null;
4526
+ return store6.cashSessions.find((s) => s.bankAccountId === bankAccountId && s.status === "open") ?? null;
4741
4527
  },
4742
4528
  async openCashSession(input) {
4743
- const account = store2.bankAccounts.find((a) => a.id === input.bankAccountId);
4529
+ const account = store6.bankAccounts.find((a) => a.id === input.bankAccountId);
4744
4530
  const session = {
4745
4531
  id: uid(),
4746
4532
  bankAccountId: input.bankAccountId,
@@ -4754,13 +4540,13 @@ function createMockFinancialProvider(options) {
4754
4540
  tenantId,
4755
4541
  createdAt: now()
4756
4542
  };
4757
- store2.cashSessions.push(session);
4543
+ store6.cashSessions.push(session);
4758
4544
  return session;
4759
4545
  },
4760
4546
  async closeCashSession(input) {
4761
- const session = store2.cashSessions.find((s) => s.id === input.sessionId);
4547
+ const session = store6.cashSessions.find((s) => s.id === input.sessionId);
4762
4548
  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");
4549
+ const sessionMovements = store6.movements.filter((m) => m.cashSessionId === session.id && m.status === "paid");
4764
4550
  const inflow = sessionMovements.filter((m) => m.direction === "credit").reduce((s, m) => s + m.paidAmount, 0);
4765
4551
  const outflow = sessionMovements.filter((m) => m.direction === "debit").reduce((s, m) => s + m.paidAmount, 0);
4766
4552
  const expectedBalance = session.openingBalance + inflow - outflow;
@@ -4775,9 +4561,9 @@ function createMockFinancialProvider(options) {
4775
4561
  return session;
4776
4562
  },
4777
4563
  async getCashSessionSummary(sessionId) {
4778
- const session = store2.cashSessions.find((s) => s.id === sessionId);
4564
+ const session = store6.cashSessions.find((s) => s.id === sessionId);
4779
4565
  if (!session) throw new Error(`Cash session ${sessionId} not found`);
4780
- const sessionMovements = store2.movements.filter((m) => m.cashSessionId === sessionId && m.status === "paid");
4566
+ const sessionMovements = store6.movements.filter((m) => m.cashSessionId === sessionId && m.status === "paid");
4781
4567
  return {
4782
4568
  session,
4783
4569
  movementCount: sessionMovements.length,
@@ -4787,21 +4573,21 @@ function createMockFinancialProvider(options) {
4787
4573
  },
4788
4574
  // --- Payment Methods ---
4789
4575
  async getPaymentMethods() {
4790
- return store2.paymentMethods.filter((pm) => pm.isActive);
4576
+ return store6.paymentMethods.filter((pm) => pm.isActive);
4791
4577
  },
4792
4578
  async getPaymentMethodTypes() {
4793
- return store2.paymentMethodTypes.filter((t2) => t2.isActive);
4579
+ return store6.paymentMethodTypes.filter((t2) => t2.isActive);
4794
4580
  },
4795
4581
  // --- Chart of Accounts ---
4796
4582
  async getChartOfAccounts() {
4797
- return store2.chartOfAccounts.filter((n) => n.isActive);
4583
+ return store6.chartOfAccounts.filter((n) => n.isActive);
4798
4584
  },
4799
4585
  async getCostCenters() {
4800
- return store2.costCenters.filter((c) => c.isActive);
4586
+ return store6.costCenters.filter((c) => c.isActive);
4801
4587
  },
4802
4588
  // --- Card Transactions ---
4803
4589
  async getCardTransactions(dateRange) {
4804
- let results = [...store2.cardTransactions];
4590
+ let results = [...store6.cardTransactions];
4805
4591
  if (dateRange) results = results.filter((t2) => matchesDateRange(t2.expectedDate, dateRange));
4806
4592
  return results.sort((a, b) => a.expectedDate.localeCompare(b.expectedDate));
4807
4593
  },
@@ -4810,15 +4596,15 @@ function createMockFinancialProvider(options) {
4810
4596
  const todayStr = today();
4811
4597
  const isRealized = (m) => m.status !== "cancelled" && !!m.paymentDate;
4812
4598
  const consolidated = !query.bankAccountId;
4813
- const scopeAccounts = consolidated ? store2.bankAccounts.filter((a) => a.isActive) : store2.bankAccounts.filter((a) => a.id === query.bankAccountId);
4599
+ const scopeAccounts = consolidated ? store6.bankAccounts.filter((a) => a.isActive) : store6.bankAccounts.filter((a) => a.id === query.bankAccountId);
4814
4600
  const accountIds = new Set(scopeAccounts.map((a) => a.id));
4815
- const accountNameById = new Map(store2.bankAccounts.map((a) => [a.id, a.name]));
4601
+ const accountNameById = new Map(store6.bankAccounts.map((a) => [a.id, a.name]));
4816
4602
  const initialSum = scopeAccounts.reduce((s, a) => s + a.initialBalance, 0);
4817
4603
  if (!consolidated && accountIds.size === 0) {
4818
4604
  return { entries: [], openingBalance: 0, closingBalance: 0, totalCredits: 0, totalDebits: 0, totalFees: 0, net: 0, accountId: query.bankAccountId };
4819
4605
  }
4820
4606
  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)));
4607
+ const inScope = store6.movements.filter((m) => isRealized(m) && (consolidated || m.bankAccountId && accountIds.has(m.bankAccountId)));
4822
4608
  const openingBalance = round2(initialSum + inScope.filter((m) => (m.paymentDate ?? "") < query.dateRange.from).reduce((s, m) => s + netOf(m), 0));
4823
4609
  const rangeMovs = inScope.filter((m) => matchesDateRange(m.paymentDate, query.dateRange)).sort((a, b) => (a.paymentDate ?? "").localeCompare(b.paymentDate ?? "") || a.createdAt.localeCompare(b.createdAt));
4824
4610
  let balance = openingBalance;
@@ -4840,7 +4626,7 @@ function createMockFinancialProvider(options) {
4840
4626
  }
4841
4627
  return {
4842
4628
  movement,
4843
- invoice: movement.invoiceId ? store2.invoices.find((i) => i.id === movement.invoiceId) : void 0,
4629
+ invoice: movement.invoiceId ? store6.invoices.find((i) => i.id === movement.invoiceId) : void 0,
4844
4630
  runningBalance: balance,
4845
4631
  gross,
4846
4632
  fee,
@@ -4852,9 +4638,9 @@ function createMockFinancialProvider(options) {
4852
4638
  });
4853
4639
  for (const e of entries) {
4854
4640
  const mid = e.movement.matchedMovementId;
4855
- const mm = mid ? store2.movements.find((m) => m.id === mid) : void 0;
4641
+ const mm = mid ? store6.movements.find((m) => m.id === mid) : void 0;
4856
4642
  if (!mm) continue;
4857
- const inv = mm.invoiceId ? store2.invoices.find((i) => i.id === mm.invoiceId) : void 0;
4643
+ const inv = mm.invoiceId ? store6.invoices.find((i) => i.id === mm.invoiceId) : void 0;
4858
4644
  e.reconciledWith = {
4859
4645
  movementId: mm.id,
4860
4646
  description: mm.notes ?? void 0,
@@ -4878,11 +4664,11 @@ function createMockFinancialProvider(options) {
4878
4664
  },
4879
4665
  // --- Summary ---
4880
4666
  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(
4667
+ const totalBalance = store6.bankAccounts.filter((a) => a.isActive).reduce((sum, a) => sum + a.currentBalance, 0);
4668
+ const pendingReceivable = store6.movements.filter(
4883
4669
  (m) => m.direction === "credit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
4884
4670
  );
4885
- const pendingPayable = store2.movements.filter(
4671
+ const pendingPayable = store6.movements.filter(
4886
4672
  (m) => m.direction === "debit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
4887
4673
  );
4888
4674
  const todayStr = today();
@@ -4890,7 +4676,7 @@ function createMockFinancialProvider(options) {
4890
4676
  monthAgo.setDate(monthAgo.getDate() - 30);
4891
4677
  const monthStart = monthAgo.toISOString().slice(0, 10);
4892
4678
  const monthEnd = todayStr;
4893
- const monthMovements = store2.movements.filter(
4679
+ const monthMovements = store6.movements.filter(
4894
4680
  (m) => m.movementKind === "payment" && matchesDateRange(m.paymentDate, dateRange ?? { from: monthStart, to: monthEnd })
4895
4681
  );
4896
4682
  const overdueReceivable = pendingReceivable.filter((m) => m.dueDate < todayStr);
@@ -4911,12 +4697,12 @@ function createMockFinancialProvider(options) {
4911
4697
  async importBankTransactions(input) {
4912
4698
  let imported = 0, duplicates = 0;
4913
4699
  for (const line of input.lines) {
4914
- const dup = store2.movements.some((m) => m.externalSource === line.externalSource && m.externalId === line.externalId);
4700
+ const dup = store6.movements.some((m) => m.externalSource === line.externalSource && m.externalId === line.externalId);
4915
4701
  if (dup) {
4916
4702
  duplicates++;
4917
4703
  continue;
4918
4704
  }
4919
- store2.movements.push({
4705
+ store6.movements.push({
4920
4706
  id: uid(),
4921
4707
  direction: line.direction,
4922
4708
  movementKind: "payment",
@@ -4938,38 +4724,38 @@ function createMockFinancialProvider(options) {
4938
4724
  return { imported, duplicates };
4939
4725
  },
4940
4726
  async getUnreconciled(query) {
4941
- let results = store2.movements.filter((m) => m.externalSource && !m.reconciledAt);
4727
+ let results = store6.movements.filter((m) => m.externalSource && !m.reconciledAt);
4942
4728
  if (query?.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
4943
4729
  if (query?.dateRange) results = results.filter((m) => matchesDateRange(m.paymentDate, query.dateRange));
4944
4730
  return results.sort((a, b) => (b.paymentDate ?? "").localeCompare(a.paymentDate ?? ""));
4945
4731
  },
4946
4732
  async suggestReconciliation(bankMovementId) {
4947
- const line = store2.movements.find((m) => m.id === bankMovementId);
4733
+ const line = store6.movements.find((m) => m.id === bankMovementId);
4948
4734
  if (!line) return [];
4949
4735
  const amount = line.amount;
4950
4736
  const tol = Math.max(0.01, round2(amount * 0.02));
4951
4737
  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) => {
4738
+ 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
4739
  const amtScore = amount > 0 ? Math.max(0, 1 - Math.abs(m.amount - amount) / amount) : 0;
4954
4740
  const refDate = m.dueDate ?? m.paymentDate;
4955
4741
  const days = lineDate && refDate ? Math.abs((new Date(lineDate).getTime() - new Date(refDate).getTime()) / 864e5) : 30;
4956
4742
  const dateScore = Math.max(0, 1 - days / 30);
4957
4743
  return {
4958
4744
  movement: m,
4959
- invoice: m.invoiceId ? store2.invoices.find((i) => i.id === m.invoiceId) : void 0,
4745
+ invoice: m.invoiceId ? store6.invoices.find((i) => i.id === m.invoiceId) : void 0,
4960
4746
  score: round2(0.7 * amtScore + 0.3 * dateScore)
4961
4747
  };
4962
4748
  }).sort((a, b) => b.score - a.score).slice(0, 25);
4963
4749
  },
4964
4750
  async reconcileMovement(input) {
4965
- const line = store2.movements.find((m) => m.id === input.bankMovementId);
4751
+ const line = store6.movements.find((m) => m.id === input.bankMovementId);
4966
4752
  if (line) {
4967
4753
  line.reconciledAt = now();
4968
4754
  line.matchedMovementId = input.matchedMovementId;
4969
4755
  line.updatedAt = now();
4970
4756
  }
4971
4757
  if (input.matchedMovementId) {
4972
- const internal = store2.movements.find((m) => m.id === input.matchedMovementId);
4758
+ const internal = store6.movements.find((m) => m.id === input.matchedMovementId);
4973
4759
  if (internal) {
4974
4760
  internal.reconciledAt = now();
4975
4761
  internal.updatedAt = now();
@@ -4977,14 +4763,14 @@ function createMockFinancialProvider(options) {
4977
4763
  }
4978
4764
  },
4979
4765
  async unreconcileMovement(bankMovementId) {
4980
- const line = store2.movements.find((m) => m.id === bankMovementId);
4766
+ const line = store6.movements.find((m) => m.id === bankMovementId);
4981
4767
  if (!line) return;
4982
4768
  const matchedId = line.matchedMovementId;
4983
4769
  line.reconciledAt = void 0;
4984
4770
  line.matchedMovementId = void 0;
4985
4771
  line.updatedAt = now();
4986
4772
  if (matchedId) {
4987
- const internal = store2.movements.find((m) => m.id === matchedId);
4773
+ const internal = store6.movements.find((m) => m.id === matchedId);
4988
4774
  if (internal) {
4989
4775
  internal.reconciledAt = void 0;
4990
4776
  internal.updatedAt = now();
@@ -4993,7 +4779,7 @@ function createMockFinancialProvider(options) {
4993
4779
  },
4994
4780
  // --- Notas fiscais (NF-e / NFC-e / NFS-e) ---
4995
4781
  async getNotas(query) {
4996
- let list = [...store2.notas].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
4782
+ let list = [...store6.notas].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
4997
4783
  if (query.status) list = list.filter((n) => matchesStatus(n.status, query.status));
4998
4784
  if (query.model) list = list.filter((n) => n.model === query.model);
4999
4785
  if (query.orderId) list = list.filter((n) => n.orderId === query.orderId);
@@ -5004,7 +4790,7 @@ function createMockFinancialProvider(options) {
5004
4790
  return paginate(list, query.page, query.pageSize);
5005
4791
  },
5006
4792
  async getNotaById(id) {
5007
- return store2.notas.find((n) => n.id === id) ?? null;
4793
+ return store6.notas.find((n) => n.id === id) ?? null;
5008
4794
  },
5009
4795
  async createNota(input) {
5010
4796
  const nota = {
@@ -5022,19 +4808,19 @@ function createMockFinancialProvider(options) {
5022
4808
  createdAt: now(),
5023
4809
  updatedAt: now()
5024
4810
  };
5025
- store2.notas.unshift(nota);
4811
+ store6.notas.unshift(nota);
5026
4812
  return nota;
5027
4813
  },
5028
4814
  async updateNota(id, patch) {
5029
- const nota = store2.notas.find((n) => n.id === id);
4815
+ const nota = store6.notas.find((n) => n.id === id);
5030
4816
  if (!nota) throw new Error("Nota fiscal not found");
5031
4817
  Object.assign(nota, patch, { updatedAt: now() });
5032
4818
  return nota;
5033
4819
  },
5034
4820
  async issueNota(id) {
5035
- const nota = store2.notas.find((n) => n.id === id);
4821
+ const nota = store6.notas.find((n) => n.id === id);
5036
4822
  if (!nota) throw new Error("Nota fiscal not found");
5037
- const issuedCount = store2.notas.filter((n) => n.status === "issued").length;
4823
+ const issuedCount = store6.notas.filter((n) => n.status === "issued").length;
5038
4824
  nota.status = "issued";
5039
4825
  nota.number = String(issuedCount + 1).padStart(6, "0");
5040
4826
  nota.series = nota.series ?? "1";
@@ -5044,7 +4830,7 @@ function createMockFinancialProvider(options) {
5044
4830
  return nota;
5045
4831
  },
5046
4832
  async cancelNota(id, reason) {
5047
- const nota = store2.notas.find((n) => n.id === id);
4833
+ const nota = store6.notas.find((n) => n.id === id);
5048
4834
  if (!nota) throw new Error("Nota fiscal not found");
5049
4835
  nota.status = "cancelled";
5050
4836
  nota.errorMessage = reason;
@@ -5299,6 +5085,11 @@ function createSupabaseFinancialProvider() {
5299
5085
  party_id: input.contactId,
5300
5086
  notes: input.observations,
5301
5087
  currency: "BRL",
5088
+ // The form has an invoice-date picker and this used to ignore it, so
5089
+ // every invoice was dated the day it was typed. `invoiceDate` is read
5090
+ // back off created_at (see mapping below) and is what the date filters
5091
+ // and the payables history sort by, so it has to be written here.
5092
+ ...input.invoiceDate ? { created_at: (/* @__PURE__ */ new Date(`${input.invoiceDate}T12:00:00Z`)).toISOString() } : {},
5302
5093
  metadata: { itemsSummary, contactName: input.contactName, installmentCount: input.installments.length || 1 }
5303
5094
  }).select().single();
5304
5095
  if (order && input.items.length > 0) {
@@ -5331,14 +5122,17 @@ function createSupabaseFinancialProvider() {
5331
5122
  paid_amount: 0,
5332
5123
  status: "pending",
5333
5124
  due_date: inst.dueDate,
5334
- installment_number: inst.installmentNumber ?? i + 1
5125
+ installment_number: inst.installmentNumber ?? i + 1,
5126
+ // O metadata da parcela vinha sendo descartado aqui: o comprovante
5127
+ // anexado no cadastro sobrevivia no mock e sumia no banco.
5128
+ metadata: inst.metadata ?? null
5335
5129
  }))
5336
5130
  );
5337
5131
  }
5338
5132
  return {
5339
5133
  id: order.id,
5340
5134
  direction: input.direction,
5341
- invoiceDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
5135
+ invoiceDate: input.invoiceDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
5342
5136
  fiscalNumber: referenceNumber,
5343
5137
  totalAmount,
5344
5138
  paidAmount: 0,
@@ -5409,11 +5203,18 @@ function createSupabaseFinancialProvider() {
5409
5203
  cash_session_id: input.cashSessionId ?? null,
5410
5204
  card_brand: input.cardBrand ?? null,
5411
5205
  card_installments: input.cardInstallments ?? null,
5412
- notes: input.notes ?? null
5206
+ notes: input.notes ?? null,
5207
+ metadata: input.metadata ?? null,
5208
+ // Which bill this settles. Without it the pairing has to be re-derived
5209
+ // from (invoice_id, installment_number), which is ambiguous the moment
5210
+ // one installment takes two payments. See migration 014.
5211
+ settles_movement_id: mov.id
5413
5212
  };
5414
5213
  let { data, error } = await pub.from(T.movements).insert(paymentRow).select().single();
5415
- if (error && /fee_amount/i.test(error.message ?? "")) {
5416
- const { fee_amount, ...rest } = paymentRow;
5214
+ if (error && /fee_amount|settles_movement_id/i.test(error.message ?? "")) {
5215
+ const rest = { ...paymentRow };
5216
+ if (/fee_amount/i.test(error.message ?? "")) delete rest.fee_amount;
5217
+ if (/settles_movement_id/i.test(error.message ?? "")) delete rest.settles_movement_id;
5417
5218
  ({ data, error } = await pub.from(T.movements).insert(rest).select().single());
5418
5219
  }
5419
5220
  if (error) throw error;
@@ -6097,82 +5898,6 @@ function createFinancialStore(provider) {
6097
5898
  throw err;
6098
5899
  }
6099
5900
  },
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
5901
  async payMovement(input) {
6177
5902
  try {
6178
5903
  await provider.payMovement(input);
@@ -6282,7 +6007,17 @@ var chartOfAccountsEntity = {
6282
6007
  fields: [
6283
6008
  { key: "code", label: "Code", type: "text", required: true, showInTable: true },
6284
6009
  { key: "name", label: "Name", type: "text", required: true, showInTable: true },
6285
- { key: "nodeType", label: "Type", type: "select", options: ["group", "leaf"], showInTable: true, required: true, defaultValue: "leaf" },
6010
+ { key: "nodeType", label: "Structure", type: "select", options: ["group", "leaf"], showInTable: true, required: true, defaultValue: "leaf" },
6011
+ // What the account IS, as opposed to where it sits in the tree (nodeType).
6012
+ // Migration 013 leaves it unconstrained; the picker offers the five the
6013
+ // module reasons about.
6014
+ { key: "accountType", label: "Nature", type: "select", options: [
6015
+ { label: "Revenue", value: "revenue" },
6016
+ { label: "Expense", value: "expense" },
6017
+ { label: "Asset", value: "asset" },
6018
+ { label: "Liability", value: "liability" },
6019
+ { label: "Equity", value: "equity" }
6020
+ ], showInTable: true },
6286
6021
  { key: "parentId", label: "Parent", type: "text", showInTable: false },
6287
6022
  { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
6288
6023
  ],
@@ -6404,20 +6139,20 @@ var financialRegistries = [
6404
6139
  description: "Hierarchical chart of accounts for categorizing transactions",
6405
6140
  display: "tree",
6406
6141
  seedData: [
6407
- { id: "coa-1", code: "1", name: "Revenue", nodeType: "group", isActive: true },
6408
- { id: "coa-1.1", code: "1.1", name: "Sales Revenue", nodeType: "group", parentId: "coa-1", isActive: true },
6409
- { id: "coa-1.1.01", code: "1.1.01", name: "Services", nodeType: "leaf", parentId: "coa-1.1", isActive: true },
6410
- { id: "coa-1.1.02", code: "1.1.02", name: "Products", nodeType: "leaf", parentId: "coa-1.1", isActive: true },
6411
- { id: "coa-2", code: "2", name: "Other Revenue", nodeType: "group", isActive: true },
6412
- { id: "coa-2.1", code: "2.1", name: "Financial Income", nodeType: "leaf", parentId: "coa-2", isActive: true },
6413
- { id: "coa-3", code: "3", name: "Operational Costs", nodeType: "group", isActive: true },
6414
- { id: "coa-3.1", code: "3.1", name: "Materials & Supplies", nodeType: "group", parentId: "coa-3", isActive: true },
6415
- { id: "coa-3.1.01", code: "3.1.01", name: "Raw Materials", nodeType: "leaf", parentId: "coa-3.1", isActive: true },
6416
- { id: "coa-3.1.02", code: "3.1.02", name: "Consumables", nodeType: "leaf", parentId: "coa-3.1", isActive: true },
6417
- { id: "coa-4", code: "4", name: "Expenses", nodeType: "group", isActive: true },
6418
- { id: "coa-4.1", code: "4.1", name: "Rent", nodeType: "leaf", parentId: "coa-4", isActive: true },
6419
- { id: "coa-4.2", code: "4.2", name: "Utilities", nodeType: "leaf", parentId: "coa-4", isActive: true },
6420
- { id: "coa-4.3", code: "4.3", name: "Payroll", nodeType: "leaf", parentId: "coa-4", isActive: true }
6142
+ { id: "coa-1", code: "1", name: "Revenue", nodeType: "group", accountType: "revenue", isActive: true },
6143
+ { id: "coa-1.1", code: "1.1", name: "Sales Revenue", nodeType: "group", accountType: "revenue", parentId: "coa-1", isActive: true },
6144
+ { id: "coa-1.1.01", code: "1.1.01", name: "Services", nodeType: "leaf", accountType: "revenue", parentId: "coa-1.1", isActive: true },
6145
+ { id: "coa-1.1.02", code: "1.1.02", name: "Products", nodeType: "leaf", accountType: "revenue", parentId: "coa-1.1", isActive: true },
6146
+ { id: "coa-2", code: "2", name: "Other Revenue", nodeType: "group", accountType: "revenue", isActive: true },
6147
+ { id: "coa-2.1", code: "2.1", name: "Financial Income", nodeType: "leaf", accountType: "revenue", parentId: "coa-2", isActive: true },
6148
+ { id: "coa-3", code: "3", name: "Operational Costs", nodeType: "group", accountType: "expense", isActive: true },
6149
+ { id: "coa-3.1", code: "3.1", name: "Materials & Supplies", nodeType: "group", accountType: "expense", parentId: "coa-3", isActive: true },
6150
+ { id: "coa-3.1.01", code: "3.1.01", name: "Raw Materials", nodeType: "leaf", accountType: "expense", parentId: "coa-3.1", isActive: true },
6151
+ { id: "coa-3.1.02", code: "3.1.02", name: "Consumables", nodeType: "leaf", accountType: "expense", parentId: "coa-3.1", isActive: true },
6152
+ { id: "coa-4", code: "4", name: "Expenses", nodeType: "group", accountType: "expense", isActive: true },
6153
+ { id: "coa-4.1", code: "4.1", name: "Rent", nodeType: "leaf", accountType: "expense", parentId: "coa-4", isActive: true },
6154
+ { id: "coa-4.2", code: "4.2", name: "Utilities", nodeType: "leaf", accountType: "expense", parentId: "coa-4", isActive: true },
6155
+ { id: "coa-4.3", code: "4.3", name: "Payroll", nodeType: "leaf", accountType: "expense", parentId: "coa-4", isActive: true }
6421
6156
  ]
6422
6157
  },
6423
6158
  {
@@ -6505,6 +6240,7 @@ var en = {
6505
6240
  "financial.cards.currentBill": "Current bill",
6506
6241
  "financial.cards.dueOn": "Due on the {{day}}",
6507
6242
  "financial.cards.installment": "Installment {{current}}/{{total}}",
6243
+ "financial.cards.loading": "Loading...",
6508
6244
  "financial.cards.noCards": "No cards yet",
6509
6245
  "financial.cards.noCardsHint": "Add a credit card in settings to track your bills here",
6510
6246
  "financial.cards.noTransactions": "No card transactions",
@@ -6546,10 +6282,17 @@ var en = {
6546
6282
  "financial.cashRegisters.register": "Register",
6547
6283
  "financial.cashRegisters.sessionHistory": "Session History",
6548
6284
  "financial.cashRegisters.subtitle": "Open, count and close cash sessions",
6549
- "financial.commissions.comingSoon": "Coming soon",
6285
+ "financial.commissions.emptyTitle": "No commissions calculated",
6286
+ "financial.commissions.emptyDesc": "This module tracks commission rules, but nothing here calculates a commission from your sales yet.",
6287
+ "financial.commissions.emptyRulesHint": "Commission rules are managed under Settings \u203A Financial \u203A Properties.",
6550
6288
  "financial.commissions.subtitle": "Team commissions",
6551
6289
  "financial.invoice.accountsPayable": "Accounts Payable",
6552
6290
  "financial.invoice.accountsReceivable": "Accounts Receivable",
6291
+ "financial.invoice.bulkCancel": "Cancel",
6292
+ "financial.invoice.bulkCancelDone": "{{count}} invoice(s) cancelled",
6293
+ "financial.invoice.bulkCancelFailed": "{{count}} invoice(s) could not be cancelled",
6294
+ "financial.invoice.bulkCancelNone": "None of the selected invoices can be cancelled.",
6295
+ "financial.invoice.bulkCancelTitle": "Cancel {{count}} invoice(s)?",
6553
6296
  "financial.invoice.cancelConfirm": "This action cannot be undone.",
6554
6297
  "financial.invoice.cancelDesc": "The invoice will be marked as cancelled. All pending installments will also be cancelled. This invoice will no longer count towards totals.",
6555
6298
  "financial.invoice.cancelInvoice": "Cancel Invoice",
@@ -6567,7 +6310,20 @@ var en = {
6567
6310
  "financial.invoice.items": "Items",
6568
6311
  "financial.invoice.keepInvoice": "Keep Invoice",
6569
6312
  "financial.invoice.new": "New",
6313
+ "financial.invoice.newManual": "Fill it in by hand",
6314
+ "financial.invoiceForm.createContact": "Create",
6315
+ "financial.invoiceForm.alreadyPaid": "Already paid",
6316
+ "financial.invoiceForm.paidWith": "Payment method",
6317
+ "financial.invoiceForm.paidFrom": "Paid from",
6318
+ "financial.invoiceForm.selectMethod": "Select a method",
6319
+ "financial.invoiceForm.selectAccount": "Select an account",
6320
+ "financial.invoiceForm.paidNeedsMethod": "Tell us the payment method and the account",
6321
+ "financial.invoiceForm.willBePaid": "This installment will be settled",
6322
+ "financial.invoiceForm.clear": "Clear",
6323
+ "financial.invoice.newManualHint": "Open the full invoice form",
6570
6324
  "financial.invoice.noDetails": "No additional details recorded",
6325
+ "financial.invoice.unlinkedPayments": "Payments not linked to an installment",
6326
+ "financial.invoice.unlinkedPaymentsDesc": "Money recorded against this invoice that does not name one of the installments above.",
6571
6327
  "financial.invoice.noInstallments": "No installments",
6572
6328
  "financial.invoice.noInvoices": "No invoices yet",
6573
6329
  "financial.invoice.summaryTotal": "Total",
@@ -6872,6 +6628,8 @@ var en = {
6872
6628
  "financial.invoiceForm.cancelling": "Cancelling...",
6873
6629
  "financial.invoiceForm.removeAdjustments": "Remove adjustments",
6874
6630
  "financial.invoiceForm.discount": "Discount",
6631
+ "financial.invoiceForm.account": "Chart account",
6632
+ "financial.invoiceForm.accountNone": "Not assigned",
6875
6633
  "financial.invoiceForm.surcharge": "Surcharge",
6876
6634
  "financial.invoiceForm.total": "Total:",
6877
6635
  "financial.invoiceForm.itemCount": "{{count}} item{{plural}}",
@@ -6945,6 +6703,7 @@ var en = {
6945
6703
  "financial.statements.totalFees": "Fees",
6946
6704
  "financial.statements.net": "Net",
6947
6705
  "financial.statements.transfer": "Transfer",
6706
+ "financial.statements.untitledEntry": "Transaction",
6948
6707
  "financial.statements.reconciled": "Reconciled with the bank statement",
6949
6708
  "financial.statements.imported": "Imported from the bank \xB7 not reconciled",
6950
6709
  "financial.statements.reconciledWith": "Reconciled with {{ref}}",
@@ -7006,6 +6765,7 @@ var ptBR = {
7006
6765
  "financial.cards.currentBill": "Fatura atual",
7007
6766
  "financial.cards.dueOn": "Vence dia {{day}}",
7008
6767
  "financial.cards.installment": "Parcela {{current}}/{{total}}",
6768
+ "financial.cards.loading": "Carregando...",
7009
6769
  "financial.cards.noCards": "Nenhum cart\xE3o cadastrado",
7010
6770
  "financial.cards.noCardsHint": "Adicione um cart\xE3o de cr\xE9dito nas configura\xE7\xF5es para acompanhar suas faturas aqui",
7011
6771
  "financial.cards.noTransactions": "Nenhuma transa\xE7\xE3o com cart\xE3o",
@@ -7047,10 +6807,17 @@ var ptBR = {
7047
6807
  "financial.cashRegisters.register": "Caixa",
7048
6808
  "financial.cashRegisters.sessionHistory": "Hist\xF3rico de Sess\xF5es",
7049
6809
  "financial.cashRegisters.subtitle": "Abrir, contar e fechar sess\xF5es de caixa",
7050
- "financial.commissions.comingSoon": "Em breve",
6810
+ "financial.commissions.emptyTitle": "Nenhuma comiss\xE3o calculada",
6811
+ "financial.commissions.emptyDesc": "Este m\xF3dulo guarda as regras de comiss\xE3o, mas ainda n\xE3o h\xE1 nada que calcule comiss\xE3o a partir das suas vendas.",
6812
+ "financial.commissions.emptyRulesHint": "As regras de comiss\xE3o ficam em Configura\xE7\xF5es \u203A Financeiro \u203A Propriedades.",
7051
6813
  "financial.commissions.subtitle": "Comiss\xF5es da equipe",
7052
6814
  "financial.invoice.accountsPayable": "Contas a Pagar",
7053
6815
  "financial.invoice.accountsReceivable": "Contas a Receber",
6816
+ "financial.invoice.bulkCancel": "Cancelar",
6817
+ "financial.invoice.bulkCancelDone": "{{count}} fatura(s) cancelada(s)",
6818
+ "financial.invoice.bulkCancelFailed": "{{count}} fatura(s) n\xE3o puderam ser canceladas",
6819
+ "financial.invoice.bulkCancelNone": "Nenhuma das faturas selecionadas pode ser cancelada.",
6820
+ "financial.invoice.bulkCancelTitle": "Cancelar {{count}} fatura(s)?",
7054
6821
  "financial.invoice.cancelConfirm": "Esta a\xE7\xE3o n\xE3o pode ser desfeita.",
7055
6822
  "financial.invoice.cancelDesc": "A fatura ser\xE1 marcada como cancelada. Todas as parcelas pendentes tamb\xE9m ser\xE3o canceladas. Esta fatura n\xE3o contar\xE1 mais nos totais.",
7056
6823
  "financial.invoice.cancelInvoice": "Cancelar Fatura",
@@ -7068,7 +6835,20 @@ var ptBR = {
7068
6835
  "financial.invoice.items": "Itens",
7069
6836
  "financial.invoice.keepInvoice": "Manter Fatura",
7070
6837
  "financial.invoice.new": "Nova",
6838
+ "financial.invoice.newManual": "Cadastrar manualmente",
6839
+ "financial.invoiceForm.createContact": "Cadastrar",
6840
+ "financial.invoiceForm.alreadyPaid": "J\xE1 foi pago",
6841
+ "financial.invoiceForm.paidWith": "Forma de pagamento",
6842
+ "financial.invoiceForm.paidFrom": "Saiu de",
6843
+ "financial.invoiceForm.selectMethod": "Selecione a forma",
6844
+ "financial.invoiceForm.selectAccount": "Selecione a conta",
6845
+ "financial.invoiceForm.paidNeedsMethod": "Diga a forma de pagamento e a conta",
6846
+ "financial.invoiceForm.willBePaid": "Esta parcela entra como paga",
6847
+ "financial.invoiceForm.clear": "Limpar",
6848
+ "financial.invoice.newManualHint": "Preencher os campos da conta",
7071
6849
  "financial.invoice.noDetails": "Nenhum detalhe adicional registrado",
6850
+ "financial.invoice.unlinkedPayments": "Pagamentos sem parcela vinculada",
6851
+ "financial.invoice.unlinkedPaymentsDesc": "Dinheiro registrado nesta fatura que n\xE3o aponta para nenhuma das parcelas acima.",
7072
6852
  "financial.invoice.noInstallments": "Sem parcelas",
7073
6853
  "financial.invoice.noInvoices": "Nenhuma fatura ainda",
7074
6854
  "financial.invoice.summaryTotal": "Total",
@@ -7376,6 +7156,8 @@ var ptBR = {
7376
7156
  "financial.invoiceForm.cancelling": "Cancelando...",
7377
7157
  "financial.invoiceForm.removeAdjustments": "Remover ajustes",
7378
7158
  "financial.invoiceForm.discount": "Desconto",
7159
+ "financial.invoiceForm.account": "Conta cont\xE1bil",
7160
+ "financial.invoiceForm.accountNone": "N\xE3o atribu\xEDda",
7379
7161
  "financial.invoiceForm.surcharge": "Acr\xE9scimo",
7380
7162
  "financial.invoiceForm.total": "Total:",
7381
7163
  "financial.invoiceForm.itemCount": "{{count}} item{{plural}}",
@@ -7449,6 +7231,7 @@ var ptBR = {
7449
7231
  "financial.statements.totalFees": "Taxas",
7450
7232
  "financial.statements.net": "L\xEDquido",
7451
7233
  "financial.statements.transfer": "Transfer\xEAncia",
7234
+ "financial.statements.untitledEntry": "Lan\xE7amento",
7452
7235
  "financial.statements.reconciled": "Conciliado com o extrato banc\xE1rio",
7453
7236
  "financial.statements.imported": "Importado do banco \xB7 n\xE3o conciliado",
7454
7237
  "financial.statements.reconciledWith": "Conciliado com {{ref}}",
@@ -7657,6 +7440,306 @@ var stripeConnectorDef = {
7657
7440
  await store.disconnect();
7658
7441
  }
7659
7442
  };
7443
+
7444
+ // src/integrations/acquirers/shared.ts
7445
+ function receivablePermissions(provider) {
7446
+ return [
7447
+ {
7448
+ kind: "data",
7449
+ resource: "payment",
7450
+ actions: ["write"],
7451
+ 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.`,
7452
+ sensitive: true
7453
+ },
7454
+ {
7455
+ kind: "data",
7456
+ resource: "invoice",
7457
+ actions: ["read", "write"],
7458
+ 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.`,
7459
+ sensitive: true
7460
+ }
7461
+ ];
7462
+ }
7463
+ var SETTLE_ON_SETTING = {
7464
+ key: "settleOn",
7465
+ label: "Quando dar a baixa",
7466
+ description: "A venda no cart\xE3o entra hoje e o dinheiro cai depois. Escolha o que conta como recebido.",
7467
+ type: "select",
7468
+ options: [
7469
+ { value: "payout", label: "Quando o dinheiro cai na conta (repasse)" },
7470
+ { value: "sale", label: "Na hora da venda" }
7471
+ ],
7472
+ defaultValue: "payout"
7473
+ };
7474
+ function methodsSetting(options) {
7475
+ return {
7476
+ key: "methods",
7477
+ label: "Quais vendas trazer",
7478
+ description: "S\xF3 o que estiver marcado aqui vira lan\xE7amento no Financeiro.",
7479
+ type: "multi-select",
7480
+ options,
7481
+ // Nothing marked is a feed with nothing to bring: the connection is real and
7482
+ // reads as "conectado, falta configurar".
7483
+ requiredToOperate: true
7484
+ };
7485
+ }
7486
+ function methodsDetail2(labels, value) {
7487
+ const list = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
7488
+ const named = list.map((v) => labels[String(v).trim()] ?? String(v).trim()).filter(Boolean);
7489
+ return named.length > 0 ? named.join(", ") : void 0;
7490
+ }
7491
+
7492
+ // src/integrations/mercadopago/connectorDef.tsx
7493
+ var MERCADOPAGO_CONNECTOR_ID = "mercadopago";
7494
+ var METHOD_LABELS2 = {
7495
+ credit_card: "Cr\xE9dito",
7496
+ debit_card: "D\xE9bito",
7497
+ pix: "Pix",
7498
+ ticket: "Boleto",
7499
+ account_money: "Saldo em conta"
7500
+ };
7501
+ var MERCADOPAGO_SETTINGS = [
7502
+ methodsSetting([
7503
+ { value: "credit_card", label: METHOD_LABELS2.credit_card },
7504
+ { value: "debit_card", label: METHOD_LABELS2.debit_card },
7505
+ { value: "pix", label: METHOD_LABELS2.pix },
7506
+ { value: "ticket", label: METHOD_LABELS2.ticket },
7507
+ { value: "account_money", label: METHOD_LABELS2.account_money }
7508
+ ]),
7509
+ SETTLE_ON_SETTING
7510
+ ];
7511
+ var store2 = createConnectionStore(MERCADOPAGO_CONNECTOR_ID, {
7512
+ settings: MERCADOPAGO_SETTINGS,
7513
+ settingsKeys: ["collectorId"]
7514
+ });
7515
+ var mercadoPagoConnectorDef = {
7516
+ id: MERCADOPAGO_CONNECTOR_ID,
7517
+ hostPluginId: "financial",
7518
+ name: "Mercado Pago",
7519
+ 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.",
7520
+ icon: "CreditCard",
7521
+ category: "payments",
7522
+ authKind: "oauth",
7523
+ // We reach Mercado Pago for what it received; nothing here charges anyone.
7524
+ direction: "inbound",
7525
+ planes: ["sync"],
7526
+ docsUrl: "https://www.mercadopago.com.br/developers/pt/docs/checkout-api/additional-content/security/oauth/creation",
7527
+ permissions: receivablePermissions("Mercado Pago"),
7528
+ fields: [],
7529
+ settings: MERCADOPAGO_SETTINGS,
7530
+ howItSyncs: [
7531
+ "De tempos em tempos, o que o Mercado Pago recebeu vira lan\xE7amento no Financeiro.",
7532
+ "Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem no Mercado Pago.",
7533
+ "A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
7534
+ ],
7535
+ ...connectionStatus(
7536
+ MERCADOPAGO_CONNECTOR_ID,
7537
+ (connection) => ({
7538
+ connected: !!connection?.active,
7539
+ detail: methodsDetail2(METHOD_LABELS2, connection?.settings?.methods),
7540
+ plane: "sync",
7541
+ ...settingsStatus(MERCADOPAGO_SETTINGS, connection)
7542
+ }),
7543
+ { store: () => store2 }
7544
+ ),
7545
+ ...connectionSettings(store2),
7546
+ ...connectionRuns(store2),
7547
+ startOAuth: fayzConnectorOAuth(MERCADOPAGO_CONNECTOR_ID),
7548
+ saveConnection: async () => {
7549
+ await store2.save({ active: true });
7550
+ },
7551
+ disconnect: () => store2.disconnect(),
7552
+ disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
7553
+ };
7554
+ var PAGBANK_CONNECTOR_ID = "pagbank";
7555
+ var METHOD_LABELS3 = {
7556
+ CREDIT_CARD: "Cr\xE9dito",
7557
+ DEBIT_CARD: "D\xE9bito",
7558
+ PIX: "Pix",
7559
+ BOLETO: "Boleto"
7560
+ };
7561
+ var PAGBANK_SETTINGS = [
7562
+ methodsSetting([
7563
+ { value: "CREDIT_CARD", label: METHOD_LABELS3.CREDIT_CARD },
7564
+ { value: "DEBIT_CARD", label: METHOD_LABELS3.DEBIT_CARD },
7565
+ { value: "PIX", label: METHOD_LABELS3.PIX },
7566
+ { value: "BOLETO", label: METHOD_LABELS3.BOLETO }
7567
+ ]),
7568
+ SETTLE_ON_SETTING
7569
+ ];
7570
+ var store3 = createConnectionStore(PAGBANK_CONNECTOR_ID, {
7571
+ settings: PAGBANK_SETTINGS,
7572
+ settingsKeys: ["accountId"]
7573
+ });
7574
+ var pagBankConnectorDef = {
7575
+ id: PAGBANK_CONNECTOR_ID,
7576
+ hostPluginId: "financial",
7577
+ name: "PagBank",
7578
+ 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.",
7579
+ icon: "CreditCard",
7580
+ // The catalogue carries no `pagbank` row yet, so the mark is declared here
7581
+ // rather than borrowed. Without it the row renders as a grey plug.
7582
+ logoUrl: "https://assets.fayz.ai/connectors/pagbank_pagseguro.png",
7583
+ category: "payments",
7584
+ authKind: "oauth",
7585
+ direction: "inbound",
7586
+ planes: ["sync"],
7587
+ docsUrl: "https://developer.pagbank.com.br/docs/connect-authorization",
7588
+ permissions: receivablePermissions("PagBank"),
7589
+ fields: [],
7590
+ settings: PAGBANK_SETTINGS,
7591
+ howItSyncs: [
7592
+ "De tempos em tempos, o que o PagBank recebeu vira lan\xE7amento no Financeiro.",
7593
+ "Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem no PagBank.",
7594
+ "A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
7595
+ ],
7596
+ ...connectionStatus(
7597
+ PAGBANK_CONNECTOR_ID,
7598
+ (connection) => ({
7599
+ connected: !!connection?.active,
7600
+ detail: methodsDetail2(METHOD_LABELS3, connection?.settings?.methods),
7601
+ plane: "sync",
7602
+ ...settingsStatus(PAGBANK_SETTINGS, connection)
7603
+ }),
7604
+ { store: () => store3 }
7605
+ ),
7606
+ ...connectionSettings(store3),
7607
+ ...connectionRuns(store3),
7608
+ startOAuth: fayzConnectorOAuth(PAGBANK_CONNECTOR_ID),
7609
+ saveConnection: async () => {
7610
+ await store3.save({ active: true });
7611
+ },
7612
+ disconnect: () => store3.disconnect(),
7613
+ disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
7614
+ };
7615
+ var CIELO_CONNECTOR_ID = "cielo";
7616
+ var CIELO_FIELDS = [
7617
+ { key: "merchantId", label: "Merchant ID", type: "text", secret: true, placeholder: "Cielo \u2192 Gest\xE3o API \u2192 Credencial API" },
7618
+ { key: "merchantKey", label: "Merchant Key", type: "password", placeholder: "Mostrada uma \xFAnica vez, ao gerar" }
7619
+ ];
7620
+ var METHOD_LABELS4 = {
7621
+ CreditCard: "Cr\xE9dito",
7622
+ DebitCard: "D\xE9bito",
7623
+ Pix: "Pix"
7624
+ };
7625
+ var CIELO_SETTINGS = [
7626
+ methodsSetting([
7627
+ { value: "CreditCard", label: METHOD_LABELS4.CreditCard },
7628
+ { value: "DebitCard", label: METHOD_LABELS4.DebitCard },
7629
+ { value: "Pix", label: METHOD_LABELS4.Pix }
7630
+ ]),
7631
+ SETTLE_ON_SETTING
7632
+ ];
7633
+ var store4 = createConnectionStore(CIELO_CONNECTOR_ID, {
7634
+ fields: CIELO_FIELDS,
7635
+ settings: CIELO_SETTINGS
7636
+ });
7637
+ var cieloConnectorDef = {
7638
+ id: CIELO_CONNECTOR_ID,
7639
+ hostPluginId: "financial",
7640
+ name: "Cielo",
7641
+ 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.",
7642
+ icon: "CreditCard",
7643
+ // The catalogue carries no `cielo` row yet, so the mark is declared here.
7644
+ logoUrl: "https://assets.fayz.ai/connectors/cielo.jpg",
7645
+ category: "payments",
7646
+ authKind: "api-key",
7647
+ direction: "inbound",
7648
+ planes: ["sync"],
7649
+ docsUrl: "https://developercielo.github.io/manual/cielo-ecommerce",
7650
+ providerInstall: { url: "https://www.cielo.com.br/" },
7651
+ permissions: receivablePermissions("Cielo"),
7652
+ fields: CIELO_FIELDS,
7653
+ settings: CIELO_SETTINGS,
7654
+ howItSyncs: [
7655
+ "De tempos em tempos, o que a Cielo recebeu vira lan\xE7amento no Financeiro.",
7656
+ "Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem na Cielo.",
7657
+ "A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
7658
+ ],
7659
+ ...connectionStatus(
7660
+ CIELO_CONNECTOR_ID,
7661
+ (connection) => ({
7662
+ connected: !!connection?.active,
7663
+ detail: methodsDetail2(METHOD_LABELS4, connection?.settings?.methods),
7664
+ plane: "sync",
7665
+ ...settingsStatus(CIELO_SETTINGS, connection)
7666
+ }),
7667
+ { store: () => store4 }
7668
+ ),
7669
+ ...connectionSettings(store4),
7670
+ ...connectionRuns(store4),
7671
+ // Reached only after the vault accepted the pair; neither half arrives here.
7672
+ saveConnection: async () => {
7673
+ await store4.save({ active: true });
7674
+ },
7675
+ disconnect: () => store4.disconnect(),
7676
+ disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
7677
+ };
7678
+ var STONE_CONNECTOR_ID = "stone";
7679
+ var STONE_FIELDS = [
7680
+ { key: "stoneCode", label: "Stone Code", type: "text", placeholder: "N\xFAmero de afilia\xE7\xE3o da sua loja" },
7681
+ { key: "secretKey", label: "Secret Key", type: "password", placeholder: "Stone \u2192 painel \u2192 chaves de API" }
7682
+ ];
7683
+ var METHOD_LABELS5 = {
7684
+ credit: "Cr\xE9dito",
7685
+ debit: "D\xE9bito",
7686
+ pix: "Pix",
7687
+ voucher: "Vale-refei\xE7\xE3o"
7688
+ };
7689
+ var STONE_SETTINGS = [
7690
+ methodsSetting([
7691
+ { value: "credit", label: METHOD_LABELS5.credit },
7692
+ { value: "debit", label: METHOD_LABELS5.debit },
7693
+ { value: "pix", label: METHOD_LABELS5.pix },
7694
+ { value: "voucher", label: METHOD_LABELS5.voucher }
7695
+ ]),
7696
+ SETTLE_ON_SETTING
7697
+ ];
7698
+ var store5 = createConnectionStore(STONE_CONNECTOR_ID, {
7699
+ fields: STONE_FIELDS,
7700
+ settings: STONE_SETTINGS
7701
+ });
7702
+ var stoneConnectorDef = {
7703
+ id: STONE_CONNECTOR_ID,
7704
+ hostPluginId: "financial",
7705
+ name: "Stone",
7706
+ 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.",
7707
+ icon: "CreditCard",
7708
+ category: "payments",
7709
+ authKind: "api-key",
7710
+ direction: "inbound",
7711
+ planes: ["sync"],
7712
+ docsUrl: "https://docs.stone.com.br",
7713
+ permissions: receivablePermissions("Stone"),
7714
+ fields: STONE_FIELDS,
7715
+ settings: STONE_SETTINGS,
7716
+ howItSyncs: [
7717
+ "De tempos em tempos, o que a Stone recebeu vira lan\xE7amento no Financeiro.",
7718
+ "Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem na Stone.",
7719
+ "A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
7720
+ ],
7721
+ ...connectionStatus(
7722
+ STONE_CONNECTOR_ID,
7723
+ (connection) => ({
7724
+ connected: !!connection?.active,
7725
+ detail: methodsDetail2(METHOD_LABELS5, connection?.settings?.methods),
7726
+ plane: "sync",
7727
+ ...settingsStatus(STONE_SETTINGS, connection)
7728
+ }),
7729
+ { store: () => store5 }
7730
+ ),
7731
+ ...connectionSettings(store5),
7732
+ ...connectionRuns(store5),
7733
+ saveConnection: async (values) => {
7734
+ await store5.save({
7735
+ active: true,
7736
+ providerAccountKey: values.stoneCode ?? null,
7737
+ settings: { stoneCode: values.stoneCode }
7738
+ });
7739
+ },
7740
+ disconnect: () => store5.disconnect(),
7741
+ disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
7742
+ };
7660
7743
  var STATUS_STYLE = {
7661
7744
  paid: "bg-success/15 text-success",
7662
7745
  partial: "bg-warning/15 text-warning",
@@ -7804,15 +7887,16 @@ function resolveConfig(options) {
7804
7887
  receivables: options?.modules?.receivables !== false,
7805
7888
  cashRegisters: options?.modules?.cashRegisters !== false,
7806
7889
  statements: options?.modules?.statements !== false,
7807
- commissions: options?.modules?.commissions !== false,
7890
+ // Opt-in: commission RULES are a settings registry, but nothing computes a
7891
+ // commission. Defaulting it on advertised a nav item that could only ever
7892
+ // land on an empty screen.
7893
+ commissions: options?.modules?.commissions === true,
7808
7894
  cards: options?.modules?.cards !== false,
7809
7895
  // Opt-in: only shown when an app explicitly enables reconciliation (bank connector present).
7810
7896
  reconciliation: options?.modules?.reconciliation === true,
7811
7897
  // Opt-in: only shown when an app explicitly enables fiscal-invoice tracking.
7812
7898
  notasFiscais: options?.modules?.notasFiscais === true
7813
7899
  },
7814
- // Opt-in: only render the plugin's own quick-add buttons/FAB when the host asks.
7815
- quickAdd: options?.quickAdd === true,
7816
7900
  labels: { ...DEFAULT_LABELS, ...options?.labels },
7817
7901
  currency: { ...DEFAULT_CURRENCY, ...options?.currency },
7818
7902
  moduleNav: options?.moduleNav,
@@ -7834,14 +7918,15 @@ function createFinancialPlugin(options) {
7834
7918
  registerTranslations(financialLocales);
7835
7919
  registerMessageDefaults(buildFinancialMessageDefaults());
7836
7920
  const provider = options?.dataProvider ?? createSafeFinancialProvider();
7837
- const store2 = createFinancialStore(provider);
7838
- const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store: store2 });
7921
+ const store6 = createFinancialStore(provider);
7922
+ const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store: store6 });
7839
7923
  const registries = [...financialRegistries, ...options?.settingsRegistries ?? []];
7840
- const PersonStatementBound = (props) => React18__default.createElement(PersonStatementWidget, { person: props.item, provider, config });
7924
+ const PersonStatementBound = (props) => React17.createElement(PersonStatementWidget, { person: props.item, provider, config });
7841
7925
  PersonStatementBound.displayName = "FinancialPersonStatement";
7842
- const PageComponent = () => React18__default.createElement(FinancialPage, { config, provider, store: store2, registries });
7926
+ const PageComponent = () => React17.createElement(FinancialPage, { config, provider, store: store6, registries });
7843
7927
  return {
7844
7928
  id: "financial",
7929
+ defaultAgentRole: "finance",
7845
7930
  name: config.labels.pageTitle,
7846
7931
  icon: "DollarSign",
7847
7932
  version: "1.0.0",
@@ -7968,28 +8053,15 @@ function createFinancialPlugin(options) {
7968
8053
  { label: "Compare this week's revenue to last week" }
7969
8054
  ]
7970
8055
  },
7971
- {
7972
- id: "financial.create-invoice",
7973
- name: "createInvoice",
7974
- description: "Creates a new invoice/payable for a contact.",
7975
- icon: "FileText",
7976
- mode: "persist",
7977
- limitKey: "movements_month",
7978
- category: "Finance",
7979
- parameters: {
7980
- type: "object",
7981
- properties: {
7982
- contact: { type: "string", description: "Contact/customer name" },
7983
- amount: { type: "number", description: "Invoice amount" },
7984
- description: { type: "string", description: "Invoice description" }
7985
- },
7986
- required: ["contact", "amount"]
7987
- },
7988
- suggestions: [
7989
- { label: "Create an invoice for a new client" }
7990
- ],
7991
- permission: { feature: "financial", action: "create" }
7992
- },
8056
+ // (removed) `createInvoice`. It declared mode:'persist' with no handler, no
8057
+ // dataTools entry and no RPC, so buildAgentToolset filtered it out — a
8058
+ // capability advertised to nobody. It was not given an RPC because its
8059
+ // three parameters (contact, amount, description) cannot express what
8060
+ // CreateInvoiceInput requires: `direction` (payable vs receivable),
8061
+ // `invoiceDate`, and `installments[]` with their due dates. An RPC would
8062
+ // have to invent all three, and a bill with a guessed due date is worse
8063
+ // than no bill. Creating an invoice stays the form's job — /financial
8064
+ // #payables-new, which the agent can navigate to.
7993
8065
  {
7994
8066
  id: "financial.mark-payment-received",
7995
8067
  name: "markPaymentReceived",
@@ -8045,10 +8117,9 @@ function createFinancialPlugin(options) {
8045
8117
  label: "Financial",
8046
8118
  icon: "DollarSign",
8047
8119
  component: (() => {
8048
- const FinancialSettingsTab = () => React18__default.createElement(PluginSettingsPanel, {
8120
+ const FinancialSettingsTab = () => React17.createElement(PluginSettingsPanel, {
8049
8121
  title: "Financial Settings",
8050
- subtitle: "Payment methods, accounts, and configurations",
8051
- generalSettings: React18__default.createElement(FinancialGeneralSettings),
8122
+ generalSettings: React17.createElement(FinancialGeneralSettings),
8052
8123
  registries,
8053
8124
  hostPluginId: "financial",
8054
8125
  routeBase: "/settings/financial"
@@ -8063,7 +8134,18 @@ function createFinancialPlugin(options) {
8063
8134
  // Stripe is the first connector this module implements rather than merely
8064
8135
  // shelves — it stays in `integrations` too, because the union is what the
8065
8136
  // catalogue row joins on.
8066
- connectors: [stripeConnectorDef],
8137
+ // Stripe emits the receivable; the four below bring back what was actually
8138
+ // received, which is the other half of a conciliação. Mercado Pago and
8139
+ // PagBank broker the merchant's consent, so they ask for nothing; Cielo and
8140
+ // Stone issue their credential TO the merchant and have no consent flow to
8141
+ // broker — see src/integrations/acquirers/ACQUIRERS.md.
8142
+ connectors: [
8143
+ stripeConnectorDef,
8144
+ mercadoPagoConnectorDef,
8145
+ pagBankConnectorDef,
8146
+ cieloConnectorDef,
8147
+ stoneConnectorDef
8148
+ ],
8067
8149
  // TecnoSpeed is live already — the openbanking addon's `plugbank`
8068
8150
  // connector IS this slug. AbacatePay and Pagar.me were named as what
8069
8151
  // having Financeiro should put on the shelf; Mercado Pago, Bling and Stone
@@ -8073,6 +8155,6 @@ function createFinancialPlugin(options) {
8073
8155
  };
8074
8156
  }
8075
8157
 
8076
- export { FINANCIAL_NAV_FEATURES, buildFinancialDomainEvents, buildFinancialMessageDefaults, createFinancialPlugin, createMockFinancialProvider, createSafeFinancialProvider, openQuickAdd };
8158
+ export { FINANCIAL_NAV_FEATURES, buildFinancialDomainEvents, buildFinancialMessageDefaults, createFinancialPlugin, createMockFinancialProvider, createSafeFinancialProvider };
8077
8159
  //# sourceMappingURL=index.js.map
8078
8160
  //# sourceMappingURL=index.js.map