@fayz-ai/plugin-financial 0.10.0 → 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.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import React17, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
2
2
  import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, createSafeDataProvider, registerTranslations, registerMessageDefaults, useTranslation, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
3
- import { toast, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, DataTable, PageTransition, ModulePage, Button, DashboardCanvas, SubpageHeader, Skeleton, PageHeaderActions, Card, FilterChip, DatePicker, Input, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, CurrencyInput, useSaveBar, SearchSelect, ListView, KpiCard, ChartWidget, CardHeader, CardTitle, CardContent, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
4
- import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, PermissionGate, PersonLink, useLimitGuard, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, OrderDetailSkeleton, OrderDetailBlock, OrderDetailView, QuickActionsButton, invalidateLimit } from '@fayz-ai/admin';
5
- import { Wallet, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, Plus, FileText, FileCheck2, ExternalLink, Send, Ban, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, ChevronDown, Clock, Paperclip, CheckCheck, X, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, CircleAlert, CircleEllipsis, Pencil, CalendarDays, LayoutList, Layers, Camera, Building2, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
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';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
  import { createPortal } from 'react-dom';
8
8
  import { createStore as createStore$1 } from 'zustand/vanilla';
@@ -104,22 +104,22 @@ function MobileSummary() {
104
104
  }, [statement]);
105
105
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
106
106
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
107
- /* @__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: [
108
108
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.summary.totalBalance") }),
109
109
  /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-2xl font-semibold", children: fmt(summary?.totalBalance ?? 0) })
110
110
  ] }),
111
- /* @__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: [
112
112
  /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.summary.income") }),
113
113
  /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-base font-semibold text-success", children: fmt(inflow) })
114
114
  ] }),
115
- /* @__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: [
116
116
  /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.summary.expenses") }),
117
117
  /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-base font-semibold text-destructive", children: fmt(outflow) })
118
118
  ] })
119
119
  ] }),
120
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: [
121
121
  /* @__PURE__ */ jsx("p", { className: "px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: dateLabel(date) }),
122
- /* @__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) => {
123
123
  const isCredit = e.movement.direction === "credit";
124
124
  const title = e.invoice?.contactName || e.movement.notes || t2("financial.summary.transactionsTitle");
125
125
  const Icon = categoryIcon(title);
@@ -159,6 +159,15 @@ function MobileSummary() {
159
159
  ] }, date)) })
160
160
  ] });
161
161
  }
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));
167
+ }
168
+ function accountLabel(account) {
169
+ return `${account.code} - ${account.name}`;
170
+ }
162
171
  var STATUS_OPTIONS = [
163
172
  { value: "open", labelKey: "financial.invoice.statusOpen", color: "bg-info-soft text-info-soft-foreground", icon: CircleDashed },
164
173
  { value: "partial", labelKey: "financial.invoice.statusPartial", color: "bg-warning-soft text-warning-soft-foreground", icon: CircleEllipsis },
@@ -248,6 +257,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
248
257
  const t2 = useTranslation();
249
258
  const can = usePermissionOptional();
250
259
  const canCreate = can("financial", "create");
260
+ const canEdit = can("financial", "edit");
251
261
  const onNewGated = canCreate ? onNew : void 0;
252
262
  const onNewReceiptGated = canCreate ? onNewReceipt : void 0;
253
263
  const { currency } = useFinancialConfig();
@@ -256,11 +266,13 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
256
266
  useFinancialStore((s) => s.invoicesTotal);
257
267
  const invoicesLoading = useFinancialStore((s) => s.invoicesLoading);
258
268
  const fetchInvoices = useFinancialStore((s) => s.fetchInvoices);
269
+ const fetchSummary = useFinancialStore((s) => s.fetchSummary);
259
270
  const [search, setSearch] = useState("");
260
271
  const [statusFilter, setStatusFilter] = useState([]);
261
272
  const [accountId, setAccountId] = useState("");
262
273
  const [costCenterId, setCostCenterId] = useState("");
263
274
  const [accounts, setAccounts] = useState([]);
275
+ const postable = useMemo(() => postableAccounts(accounts), [accounts]);
264
276
  const [costCenters, setCostCenters] = useState([]);
265
277
  const [datePreset, setDatePreset] = useState("all");
266
278
  const [dateFrom, setDateFrom] = useState("");
@@ -296,16 +308,17 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
296
308
  active = false;
297
309
  };
298
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]);
299
319
  useEffect(() => {
300
- fetchInvoices({
301
- direction,
302
- status: statusFilter.length > 0 ? statusFilter : void 0,
303
- search: search || void 0,
304
- accountId: accountId || void 0,
305
- costCenterId: costCenterId || void 0,
306
- dateRange
307
- });
308
- }, [direction, statusFilter, search, accountId, costCenterId, dateRange]);
320
+ fetchInvoices(query);
321
+ }, [query]);
309
322
  const filtered = invoices.filter((inv) => inv.direction === direction);
310
323
  const columns = useInvoiceColumns(currency);
311
324
  const tags = useMemo(
@@ -324,6 +337,46 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
324
337
  openAmount = totalAmount - paidAmount;
325
338
  return { totalAmount, paidAmount, openAmount };
326
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]);
327
380
  const receivedLabel = direction === "debit" ? t2("financial.invoice.summaryPaid") : t2("financial.invoice.summaryReceived");
328
381
  const pendingLabel = direction === "debit" ? t2("financial.invoice.summaryPayable") : t2("financial.invoice.summaryReceivable");
329
382
  const exportRows = () => {
@@ -375,7 +428,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
375
428
  children: t2("financial.invoice.filterClear")
376
429
  }
377
430
  ),
378
- accounts.length > 0 && /* @__PURE__ */ jsx(
431
+ postable.length > 0 && /* @__PURE__ */ jsx(
379
432
  FilterChip,
380
433
  {
381
434
  icon: Landmark,
@@ -383,9 +436,9 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
383
436
  allLabel: t2("financial.invoice.filterAllAccounts"),
384
437
  value: accountId || void 0,
385
438
  onChange: (next) => setAccountId(next ?? ""),
386
- options: accounts.map((account) => ({
439
+ options: postable.map((account) => ({
387
440
  value: account.id,
388
- label: `${account.code} - ${account.name}`
441
+ label: accountLabel(account)
389
442
  }))
390
443
  }
391
444
  ),
@@ -446,6 +499,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
446
499
  onExport: filtered.length > 0 ? exportRows : void 0,
447
500
  exportLabel: t2("crud.export.action"),
448
501
  headerActions: newActions,
502
+ bulkActions,
449
503
  onRowClick: (row) => onEdit?.(row.id),
450
504
  emptyIcon: FileText,
451
505
  emptyMessage: t2("financial.invoice.noInvoices"),
@@ -468,7 +522,21 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
468
522
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: pendingLabel }),
469
523
  /* @__PURE__ */ jsx("span", { className: "font-semibold text-warning", children: formatCurrency(summary.openAmount, currency) })
470
524
  ] })
471
- ] })
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
+ )
472
540
  ] });
473
541
  }
474
542
  function useReceiptPicker(onPick) {
@@ -501,96 +569,6 @@ function useReceiptPicker(onPick) {
501
569
  );
502
570
  return { open: () => ref.current?.click(), input };
503
571
  }
504
- function ReceiptField({
505
- value,
506
- onChange,
507
- autoOpen = false
508
- }) {
509
- const t2 = useTranslation();
510
- const fileInputRef = useRef(null);
511
- useEffect(() => {
512
- if (!autoOpen) return;
513
- const id = window.setTimeout(() => fileInputRef.current?.click(), 250);
514
- return () => window.clearTimeout(id);
515
- }, [autoOpen]);
516
- function onPick(e) {
517
- const file = e.target.files?.[0];
518
- e.target.value = "";
519
- if (!file) return;
520
- if (!file.type.startsWith("image/")) {
521
- toast.error(t2("financial.quickTx.receiptInvalid"));
522
- return;
523
- }
524
- const reader = new FileReader();
525
- reader.onload = () => {
526
- onChange(typeof reader.result === "string" ? reader.result : void 0);
527
- };
528
- reader.onerror = () => toast.error(t2("financial.quickTx.receiptInvalid"));
529
- reader.readAsDataURL(file);
530
- }
531
- return /* @__PURE__ */ jsxs("div", { children: [
532
- /* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [
533
- /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }),
534
- " ",
535
- t2("financial.quickTx.receipt")
536
- ] }),
537
- /* @__PURE__ */ jsx(
538
- "input",
539
- {
540
- ref: fileInputRef,
541
- type: "file",
542
- accept: "image/*",
543
- capture: "environment",
544
- className: "hidden",
545
- onChange: onPick
546
- }
547
- ),
548
- value ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-input border border-input bg-card p-2 shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)]", children: [
549
- /* @__PURE__ */ jsx(
550
- "img",
551
- {
552
- src: value,
553
- alt: t2("financial.quickTx.receipt"),
554
- className: "h-16 w-16 shrink-0 rounded-md border object-cover"
555
- }
556
- ),
557
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
558
- /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: t2("financial.quickTx.receiptAttached") }),
559
- /* @__PURE__ */ jsx(
560
- "button",
561
- {
562
- type: "button",
563
- onClick: () => fileInputRef.current?.click(),
564
- className: "text-xs text-primary hover:underline",
565
- children: t2("financial.quickTx.receiptReplace")
566
- }
567
- )
568
- ] }),
569
- /* @__PURE__ */ jsx(
570
- "button",
571
- {
572
- type: "button",
573
- onClick: () => onChange(void 0),
574
- "aria-label": t2("financial.quickTx.receiptRemove"),
575
- className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted",
576
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
577
- }
578
- )
579
- ] }) : /* @__PURE__ */ jsxs(
580
- "button",
581
- {
582
- type: "button",
583
- onClick: () => fileInputRef.current?.click(),
584
- className: `flex w-full items-center justify-center gap-2 rounded-input border border-dashed px-3 py-3 text-sm font-medium transition-colors hover:bg-muted/30 ${autoOpen ? "border-primary text-primary ring-1 ring-primary/30" : "border-input text-muted-foreground"}`,
585
- children: [
586
- /* @__PURE__ */ jsx(Camera, { className: "h-4 w-4" }),
587
- " ",
588
- t2("financial.quickTx.receiptCapture")
589
- ]
590
- }
591
- )
592
- ] });
593
- }
594
572
  var ACCOUNT_TYPES_FOR_TX = {
595
573
  cash: ["cash_register"],
596
574
  credit_card: ["credit_card", "bank_account"],
@@ -756,7 +734,7 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
756
734
  "button",
757
735
  {
758
736
  onClick: () => setExpandMethodPicker(true),
759
- 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] ?? ""}`,
760
738
  "data-active": "true",
761
739
  children: [
762
740
  SelectedIcon && /* @__PURE__ */ jsx(SelectedIcon, { className: `h-4 w-4 ${ICON_COLORS[txType] ?? ""}` }),
@@ -787,7 +765,7 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
787
765
  {
788
766
  "data-active": isActive,
789
767
  onClick: () => handleSelectType(type.id),
790
- 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}`,
791
769
  children: [
792
770
  /* @__PURE__ */ jsx(Icon, { className: `h-5 w-5 ${isActive ? ICON_COLORS[tt] ?? "" : "text-muted-foreground"}` }),
793
771
  /* @__PURE__ */ jsx("span", { className: `text-[11px] font-medium leading-tight ${isActive ? "text-foreground" : "text-muted-foreground"}`, children: type.name })
@@ -995,6 +973,16 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
995
973
  void fetchBankAccounts();
996
974
  void fetchPaymentMethods();
997
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]);
998
986
  const [loaded, setLoaded] = useState(!editId);
999
987
  const [loading, setLoading] = useState(!!editId);
1000
988
  const [contactName, setContactName] = useState("");
@@ -1004,11 +992,32 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1004
992
  const [fiscalNumber, setFiscalNumber] = useState("");
1005
993
  const [observations, setObservations] = useState("");
1006
994
  const [items, setItems] = useState([]);
995
+ const [accounts, setAccounts] = useState([]);
996
+ const postable = useMemo(() => postableAccounts(accounts), [accounts]);
1007
997
  const [expandedItemId, setExpandedItemId] = useState(null);
1008
998
  const [addingItem, setAddingItem] = useState(false);
1009
999
  const [installments, setInstallments] = useState([]);
1010
1000
  const [savedMovements, setSavedMovements] = useState([]);
1011
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
+ });
1012
1021
  const [saving, setSaving] = useState(false);
1013
1022
  const [menuOpen, setMenuOpen] = useState(false);
1014
1023
  const [confirmCancel, setConfirmCancel] = useState(false);
@@ -1055,7 +1064,9 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1055
1064
  quantity: it.quantity,
1056
1065
  unitPrice: it.unitPrice,
1057
1066
  discount: it.discount,
1058
- surcharge: it.surcharge
1067
+ surcharge: it.surcharge,
1068
+ accountId: it.accountId,
1069
+ costCenterId: it.costCenterId
1059
1070
  })));
1060
1071
  const movements = await provider.getMovements({ direction: invoice.direction });
1061
1072
  const invoiceMovements = movements.data.filter((m) => m.invoiceId === editId && m.movementKind === "bill").sort((a, b) => (a.installmentNumber ?? 0) - (b.installmentNumber ?? 0));
@@ -1119,6 +1130,17 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1119
1130
  if (isEdit || totalAmount <= 0 || installments.length > 0) return;
1120
1131
  generateInstallments(1);
1121
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]);
1122
1144
  function generateInstallments(count) {
1123
1145
  if (count < 1 || totalAmount <= 0) return;
1124
1146
  const perInstallment = Math.floor(totalAmount * 100 / count) / 100;
@@ -1242,7 +1264,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1242
1264
  });
1243
1265
  const title = isEdit ? t2("financial.invoiceForm.editTitle") : t2("financial.invoiceForm.newTitle");
1244
1266
  const subtitle = isEdit ? t2("financial.invoiceForm.editing") : t2("financial.invoiceForm.creating");
1245
- 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 });
1246
1268
  const money = (v) => formatCurrency(v, currency);
1247
1269
  const actions = isEdit ? /* @__PURE__ */ jsxs("div", { className: "relative", ref: menuRef, children: [
1248
1270
  /* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", onClick: () => setMenuOpen(!menuOpen), "aria-label": "More actions", children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" }) }),
@@ -1264,58 +1286,91 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1264
1286
  ] }) : null;
1265
1287
  const paidSoFar = savedMovements.reduce((sum, m) => sum + (m.paidAmount ?? 0), 0);
1266
1288
  const hasPayments = paidSoFar > 0;
1267
- const installmentsEditor = /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoiceForm.installments"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1268
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: [1, 2, 3, 6, 12].map((n) => /* @__PURE__ */ jsxs(
1269
- "button",
1270
- {
1271
- type: "button",
1272
- onClick: () => generateInstallments(n),
1273
- disabled: totalAmount <= 0,
1274
- 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"}`,
1275
- children: [
1276
- n,
1277
- "x"
1278
- ]
1279
- },
1280
- n
1281
- )) }),
1282
- installments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: totalAmount <= 0 ? t2("financial.invoiceForm.addItemsFirst") : t2("financial.invoiceForm.selectInstallments") }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: installments.map((inst, i) => {
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",
1294
+ {
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
+ ),
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) => {
1283
1308
  const patch = (next) => {
1284
1309
  const updated = [...installments];
1285
1310
  updated[i] = { ...inst, ...next };
1286
1311
  setInstallments(updated);
1287
1312
  };
1288
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 rounded border p-2.5", children: [
1289
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1290
- /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-muted-foreground", children: [
1313
+ const saved = savedMovements[i];
1314
+ const isPaid = saved?.status === "paid";
1315
+ const isPartial = saved?.status === "partial";
1316
+ const StatusIcon = isPaid ? CircleCheckBig : CircleDashed;
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: [
1291
1321
  "#",
1292
1322
  i + 1
1293
1323
  ] }),
1294
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: money(inst.amount) })
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
+ ] })
1295
1357
  ] }),
1296
- /* @__PURE__ */ jsx(
1297
- DatePicker,
1298
- {
1299
- value: inst.dueDate,
1300
- onChange: (v) => patch({ dueDate: v })
1301
- }
1302
- ),
1303
- /* @__PURE__ */ jsx(
1304
- ReceiptField,
1305
- {
1306
- value: inst.receiptUrl,
1307
- onChange: (url) => patch({ receiptUrl: url }),
1308
- autoOpen: focusReceipt && i === 0 && !inst.receiptUrl
1309
- }
1310
- ),
1311
- inst.receiptUrl && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 border-t pt-1.5", children: [
1312
- /* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium text-success", children: t2("financial.invoiceForm.willBePaid") }),
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") }),
1313
1368
  /* @__PURE__ */ jsxs(
1314
1369
  "select",
1315
1370
  {
1316
1371
  value: inst.payMethodTypeId ?? "",
1317
1372
  onChange: (e) => patch({ payMethodTypeId: e.target.value }),
1318
- className: "w-full rounded-input border border-input bg-card px-2 py-1.5 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
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",
1319
1374
  children: [
1320
1375
  /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectMethod") }),
1321
1376
  paymentMethodTypes.map((tp) => /* @__PURE__ */ jsx("option", { value: tp.id, children: tp.name }, tp.id))
@@ -1327,95 +1382,52 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1327
1382
  {
1328
1383
  value: inst.payAccountId ?? (bankAccounts.length === 1 ? bankAccounts[0].id : ""),
1329
1384
  onChange: (e) => patch({ payAccountId: e.target.value }),
1330
- className: "w-full rounded-input border border-input bg-card px-2 py-1.5 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
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",
1331
1386
  children: [
1332
1387
  /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectAccount") }),
1333
1388
  bankAccounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
1334
1389
  ]
1335
1390
  }
1336
- )
1337
- ] })
1338
- ] }, inst._id);
1339
- }) })
1340
- ] }) });
1341
- const scheduleSection = isEdit ? /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1342
- hasPayments && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.invoiceForm.scheduleLocked") }),
1343
- !hasPayments && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: [1, 2, 3, 6, 12].map((n) => /* @__PURE__ */ jsxs(
1344
- "button",
1345
- {
1346
- type: "button",
1347
- onClick: () => generateInstallments(n),
1348
- disabled: totalAmount <= 0,
1349
- 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"}`,
1350
- children: [
1351
- n,
1352
- "x"
1353
- ]
1354
- },
1355
- n
1356
- )) }),
1357
- 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) => {
1358
- const saved = savedMovements[i];
1359
- const isPaid = saved?.status === "paid";
1360
- const isPartial = saved?.status === "partial";
1361
- const StatusIcon = isPaid ? CircleCheckBig : CircleDashed;
1362
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 px-3 py-2.5", children: [
1363
- /* @__PURE__ */ jsxs("span", { className: "w-6 shrink-0 text-xs font-medium text-muted-foreground", children: [
1364
- "#",
1365
- i + 1
1366
- ] }),
1367
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums", children: money(inst.amount) }),
1368
- 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: [
1369
- /* @__PURE__ */ jsx(StatusIcon, { className: "h-2.5 w-2.5" }),
1370
- t2(isPaid ? "financial.invoice.statusPaid" : isPartial ? "financial.invoice.statusPartial" : "financial.invoice.statusPending")
1371
- ] }),
1372
- /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
1373
- !hasPayments ? /* @__PURE__ */ jsx(
1374
- DatePicker,
1391
+ ),
1392
+ /* @__PURE__ */ jsx(
1393
+ "button",
1375
1394
  {
1376
- value: inst.dueDate,
1377
- variant: "chip",
1378
- onChange: (v) => {
1379
- const updated = [...installments];
1380
- updated[i] = { ...inst, dueDate: v };
1381
- setInstallments(updated);
1382
- }
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" })
1383
1400
  }
1384
- ) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: inst.dueDate }),
1385
- saved && !isPaid && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setPayingMovement(saved), children: [
1386
- /* @__PURE__ */ jsx(DollarSign, { className: "mr-1 h-3.5 w-3.5" }),
1387
- t2("financial.invoice.pay")
1388
- ] })
1401
+ )
1389
1402
  ] })
1390
1403
  ] }, inst._id);
1391
1404
  }) })
1392
- ] }) }) : null;
1405
+ ] }) });
1393
1406
  const sideSections = /* @__PURE__ */ jsxs(Fragment, { children: [
1394
- /* @__PURE__ */ jsx(OrderFormBlock, { title: direction === "debit" ? t2("financial.invoiceForm.payTo") : t2("financial.invoiceForm.receiveFrom"), children: /* @__PURE__ */ jsx(
1395
- SearchSelect,
1396
- {
1397
- required: true,
1398
- value: contactId ?? "",
1399
- displayValue: contactName,
1400
- onChange: (id, opt) => {
1401
- setContactId(id || void 0);
1402
- setContactName(opt?.label ?? "");
1403
- },
1404
- onSearch: async (q) => {
1405
- if (!config.contactLookup) return [];
1406
- const results = await config.contactLookup.search(q);
1407
- return results.map((r) => ({ id: r.id, label: r.label, subtitle: r.subtitle, group: r.group, data: r }));
1408
- },
1409
- createLabel: t2("financial.invoiceForm.createContact"),
1410
- clearLabel: t2("financial.invoiceForm.clear"),
1411
- onCreate: config.contactLookup?.create ? async (name) => {
1412
- const created = await config.contactLookup.create(name);
1413
- setContactId(created.id);
1414
- setContactName(created.label);
1415
- } : void 0,
1416
- placeholder: t2("financial.invoiceForm.searchType")
1417
- }
1418
- ) }),
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
+ ] }),
1419
1431
  /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.details"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1420
1432
  hasLocations && /* @__PURE__ */ jsx(LocationSelector, { locations, value: unitId, onChange: setUnitId }),
1421
1433
  /* @__PURE__ */ jsxs("div", { children: [
@@ -1436,7 +1448,6 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1436
1448
  )
1437
1449
  ] })
1438
1450
  ] }) }),
1439
- !isEdit && installmentsEditor,
1440
1451
  /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoiceForm.observations"), children: /* @__PURE__ */ jsx(
1441
1452
  "textarea",
1442
1453
  {
@@ -1502,6 +1513,21 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1502
1513
  removeItem(item._id);
1503
1514
  if (expandedItemId === item._id) setExpandedItemId(null);
1504
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
+ ] }) }),
1505
1531
  entityLookups: config.entityLookups,
1506
1532
  showTypeSelector: hasItemLookups,
1507
1533
  allowSurcharge: true,
@@ -1527,13 +1553,19 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1527
1553
  ]
1528
1554
  }
1529
1555
  ),
1556
+ receiptPicker.input,
1530
1557
  payingMovement && /* @__PURE__ */ jsx(
1531
1558
  PaymentModal,
1532
1559
  {
1533
1560
  movement: payingMovement,
1534
- onClose: () => setPayingMovement(null),
1561
+ receiptUrl: pendingReceipt,
1562
+ onClose: () => {
1563
+ setPayingMovement(null);
1564
+ setPendingReceipt(void 0);
1565
+ },
1535
1566
  onPaid: () => {
1536
1567
  setPayingMovement(null);
1568
+ setPendingReceipt(void 0);
1537
1569
  void (async () => {
1538
1570
  if (!editId) return;
1539
1571
  const movements = await provider.getMovements({ direction });
@@ -1572,6 +1604,27 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
1572
1604
  ` })
1573
1605
  ] });
1574
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
+ }
1575
1628
  var STATUS_COLORS = {
1576
1629
  open: { bg: "bg-info-soft text-info-soft-foreground", icon: CircleDashed, labelKey: "financial.invoice.statusOpen", tone: "info" },
1577
1630
  pending: { bg: "bg-warning-soft text-warning-soft-foreground", icon: CircleDashed, labelKey: "financial.invoice.statusPending", tone: "warning" },
@@ -1668,7 +1721,7 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1668
1721
  }
1669
1722
  }
1670
1723
  if (loading) {
1671
- return /* @__PURE__ */ jsx(OrderDetailSkeleton, {});
1724
+ return /* @__PURE__ */ jsx(OrderDetailSkeleton, { parentLabel, onBack });
1672
1725
  }
1673
1726
  if (!invoice) {
1674
1727
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
@@ -1676,6 +1729,7 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1676
1729
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t2("financial.invoice.notFoundDesc") })
1677
1730
  ] });
1678
1731
  }
1732
+ const settlements = groupPaymentsByBill(movements, payments);
1679
1733
  const statusConfig = STATUS_COLORS[invoice.status] ?? STATUS_COLORS.pending;
1680
1734
  statusConfig.icon;
1681
1735
  const totalPaid = movements.reduce((sum, m) => sum + m.paidAmount, 0);
@@ -1750,7 +1804,6 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1750
1804
  {
1751
1805
  title: invoice.fiscalNumber ? `#${invoice.fiscalNumber}` : invoice.contactName ?? invoice.invoiceDate,
1752
1806
  badges: [{ label: t2(statusConfig.labelKey), tone: statusConfig.tone }],
1753
- subtitle: invoice.invoiceDate,
1754
1807
  onBack,
1755
1808
  backLabel: parentLabel,
1756
1809
  actions,
@@ -1769,153 +1822,166 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
1769
1822
  mainSections: (
1770
1823
  // O cronograma no MESMO bloco que os itens e o pagamento — antes o título
1771
1824
  // ficava fora do cartão, e a seção lia como se fosse de outra tela.
1772
- /* @__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) => {
1773
- const remaining = mov.amount - mov.paidAmount;
1774
- const isPaid = mov.status === "paid";
1775
- const isPartial = mov.status === "partial";
1776
- const hasPaidDetails = isPaid || isPartial;
1777
- const isExpanded = expandedMovId === mov.id;
1778
- const movStatus = STATUS_COLORS[mov.status] ?? STATUS_COLORS.pending;
1779
- const MovIcon = movStatus.icon;
1780
- const instPayments = payments.filter((p) => (p.installmentNumber ?? null) === (mov.installmentNumber ?? null));
1781
- return /* @__PURE__ */ jsxs("div", { children: [
1782
- /* @__PURE__ */ jsxs(
1783
- "div",
1784
- {
1785
- className: `flex items-center gap-3 px-4 py-3 ${hasPaidDetails ? "cursor-pointer hover:bg-muted/20 transition-colors" : ""}`,
1786
- onClick: hasPaidDetails ? () => handleExpandMov(mov.id) : void 0,
1787
- children: [
1788
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-muted-foreground w-6 shrink-0", children: [
1789
- "#",
1790
- mov.installmentNumber
1791
- ] }),
1792
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1793
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1794
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium tabular-nums", children: formatCurrency(mov.amount, currency) }),
1795
- /* @__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: [
1796
- /* @__PURE__ */ jsx(MovIcon, { className: "h-2 w-2" }),
1797
- " ",
1798
- t2(movStatus.labelKey)
1799
- ] })
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
1800
1845
  ] }),
1801
- /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
1802
- t2("financial.invoice.due"),
1803
- " ",
1804
- mov.dueDate,
1805
- mov.paidAmount > 0 && !isPaid && ` \xB7 ${t2("financial.invoice.paidLabel")} ${formatCurrency(mov.paidAmount, currency)}`,
1806
- mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
1807
- ] })
1808
- ] }),
1809
- !isPaid && invoice.status !== "cancelled" && /* @__PURE__ */ jsx(
1810
- "button",
1811
- {
1812
- onClick: (e) => {
1813
- e.stopPropagation();
1814
- setReceiptFor(mov);
1815
- receiptPicker.open();
1816
- },
1817
- title: t2("financial.quickTx.receiptCapture"),
1818
- "aria-label": t2("financial.quickTx.receiptCapture"),
1819
- className: "inline-flex shrink-0 items-center gap-1 rounded-lg border bg-card px-2.5 py-1.5 text-xs font-medium shadow-button transition-colors hover:bg-muted active:shadow-button-inset",
1820
- children: /* @__PURE__ */ jsx(Camera, { className: "h-3 w-3" })
1821
- }
1822
- ),
1823
- !isPaid && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
1824
- "button",
1825
- {
1826
- onClick: (e) => {
1827
- e.stopPropagation();
1828
- setPayingMovement(mov);
1829
- },
1830
- 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",
1831
- children: [
1832
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
1833
- t2("financial.invoice.pay"),
1834
- " ",
1835
- remaining < mov.amount ? formatCurrency(remaining, currency) : ""
1836
- ]
1837
- }
1838
- ),
1839
- isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
1840
- "button",
1841
- {
1842
- onClick: (e) => {
1843
- e.stopPropagation();
1844
- setPayingMovement(mov);
1845
- },
1846
- 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",
1847
- children: [
1848
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
1849
- 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"),
1850
1857
  " ",
1851
- formatCurrency(remaining, currency)
1852
- ]
1853
- }
1854
- ),
1855
- hasPaidDetails && /* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 text-muted-foreground shrink-0 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })
1856
- ]
1857
- }
1858
- ),
1859
- /* @__PURE__ */ jsx(
1860
- "div",
1861
- {
1862
- className: "grid transition-all duration-200 ease-out",
1863
- style: { gridTemplateRows: isExpanded ? "1fr" : "0fr" },
1864
- 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: [
1865
- /* @__PURE__ */ jsx("div", { className: "h-3 w-20 rounded bg-muted/40 animate-pulse" }),
1866
- /* @__PURE__ */ jsx("div", { className: "h-3 w-16 rounded bg-muted/30 animate-pulse" }),
1867
- /* @__PURE__ */ jsx("div", { className: "h-3 w-24 rounded bg-muted/40 animate-pulse" })
1868
- ] }) : 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) => {
1869
- const pMethod = paymentMethods.find((m) => m.id === p.paymentMethodId);
1870
- const pType = paymentMethodTypes.find((tp) => tp.id === (pMethod?.paymentMethodTypeId ?? p.paymentMethodTypeId));
1871
- const pAccount = bankAccounts.find((a) => a.id === p.bankAccountId);
1872
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-1 gap-y-0.5 text-[11px] text-muted-foreground", children: [
1873
- /* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
1874
- pType && /* @__PURE__ */ jsxs(Fragment, { children: [
1875
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1876
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: pType.name }),
1877
- pMethod && /* @__PURE__ */ jsxs("span", { children: [
1878
- "(",
1879
- pMethod.name,
1880
- ")"
1881
- ] })
1882
- ] }),
1883
- p.cardBrand && /* @__PURE__ */ jsxs(Fragment, { children: [
1884
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1885
- /* @__PURE__ */ jsx(CreditCard, { className: "h-3 w-3 shrink-0" }),
1886
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: p.cardBrand }),
1887
- p.cardInstallments && /* @__PURE__ */ jsxs("span", { children: [
1888
- p.cardInstallments,
1889
- "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}`
1890
1861
  ] })
1891
1862
  ] }),
1892
- pAccount && /* @__PURE__ */ jsxs(Fragment, { children: [
1893
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1894
- /* @__PURE__ */ jsx(Building2, { className: "h-3 w-3 shrink-0" }),
1895
- /* @__PURE__ */ jsx("span", { children: pAccount.name })
1896
- ] }),
1897
- (p.feeAmount ?? 0) > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1898
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1899
- /* @__PURE__ */ jsxs("span", { className: "text-warning", children: [
1900
- t2("financial.statements.columnFee"),
1901
- " ",
1902
- 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 })
1903
1966
  ] })
1904
- ] }),
1905
- p.paymentDate && /* @__PURE__ */ jsxs(Fragment, { children: [
1906
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1907
- /* @__PURE__ */ jsx("span", { children: p.paymentDate })
1908
- ] }),
1909
- p.notes && /* @__PURE__ */ jsxs(Fragment, { children: [
1910
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
1911
- /* @__PURE__ */ jsx("span", { className: "italic truncate max-w-[200px]", children: p.notes })
1912
- ] })
1913
- ] }, p.id);
1914
- }) }) }) })
1915
- }
1916
- )
1917
- ] }, mov.id);
1918
- }) }) }) })
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
+ ] }) })
1919
1985
  ),
1920
1986
  extraSections: sideBlocks
1921
1987
  }
@@ -2176,7 +2242,7 @@ function CashOpeningPanel({
2176
2242
  {
2177
2243
  onClick: handleConfirm,
2178
2244
  disabled: submitting,
2179
- 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",
2180
2246
  children: [
2181
2247
  /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
2182
2248
  " ",
@@ -2277,7 +2343,7 @@ function CashOpeningPanel({
2277
2343
  {
2278
2344
  onClick: () => setConfirming(true),
2279
2345
  disabled: effectiveBalance <= 0,
2280
- 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",
2281
2347
  children: [
2282
2348
  /* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }),
2283
2349
  " ",
@@ -2420,7 +2486,7 @@ function CashRegistersView() {
2420
2486
  }
2421
2487
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2422
2488
  /* @__PURE__ */ jsx(SubpageHeader, { title: labels.cashRegisters ?? t2("financial.cash.title"), subtitle: t2("financial.cash.subtitle") }),
2423
- /* @__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: [
2424
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: [
2425
2491
  /* @__PURE__ */ jsx(Landmark, { className: "h-4 w-4 text-info" }),
2426
2492
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.openSessions") }),
@@ -2457,7 +2523,7 @@ function CashRegistersView() {
2457
2523
  ] }, session.id)) })
2458
2524
  ] }),
2459
2525
  openSessions.length > 0 && cashAccounts.length > openSessions.length && /* @__PURE__ */ jsx(OpenAnotherCollapsible, { cashAccounts, openSessions, currency, onOpen: handleOpen }),
2460
- /* @__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: [
2461
2527
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
2462
2528
  /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-muted-foreground" }),
2463
2529
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.sessionHistory") })
@@ -2511,7 +2577,7 @@ function OpenAnotherCollapsible({
2511
2577
  const [expanded, setExpanded] = useState(false);
2512
2578
  const availableAccounts = cashAccounts.filter((a) => !openSessions.some((s) => s.bankAccountId === a.id));
2513
2579
  if (availableAccounts.length === 0) return null;
2514
- 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: [
2515
2581
  /* @__PURE__ */ jsxs(
2516
2582
  "button",
2517
2583
  {
@@ -2738,7 +2804,7 @@ function StatementsView({ onNavigate }) {
2738
2804
  const e = row.original;
2739
2805
  const isTransfer = e.entryKind !== "movement";
2740
2806
  const memo = e.movement.notes;
2741
- 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"));
2742
2808
  const memoLine = memo && memo !== title ? memo : void 0;
2743
2809
  const doc = e.invoice?.fiscalNumber;
2744
2810
  const inst = e.movement.installmentNumber;
@@ -2948,7 +3014,12 @@ function CommissionsView() {
2948
3014
  const { labels } = useFinancialConfig();
2949
3015
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2950
3016
  /* @__PURE__ */ jsx(SubpageHeader, { title: labels.commissions, subtitle: t2("financial.commissions.subtitle") }),
2951
- /* @__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
+ ] })
2952
3023
  ] });
2953
3024
  }
2954
3025
  function cardMath(acc) {
@@ -2990,7 +3061,7 @@ function CardsView({ onNavigate }) {
2990
3061
  }
2991
3062
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2992
3063
  /* @__PURE__ */ jsx(SubpageHeader, { title: t2("financial.cards.title"), subtitle: t2("financial.cards.subtitle") }),
2993
- 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: [
2994
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" }) }),
2995
3066
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t2("financial.cards.noCards") }),
2996
3067
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 max-w-xs", children: t2("financial.cards.noCardsHint") })
@@ -3152,12 +3223,12 @@ function ReconciliationView() {
3152
3223
  ] })
3153
3224
  ] })
3154
3225
  ] }),
3155
- 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: [
3156
3227
  /* @__PURE__ */ jsx(Check, { className: "h-8 w-8 text-success/40 mx-auto mb-2" }),
3157
3228
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t2("financial.reconciliation.empty") }),
3158
3229
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: t2("financial.reconciliation.emptyHint") })
3159
3230
  ] }) : /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
3160
- /* @__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: [
3161
3232
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
3162
3233
  /* @__PURE__ */ jsx(Landmark, { className: "h-4 w-4 text-muted-foreground" }),
3163
3234
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.reconciliation.bankLines") })
@@ -3188,7 +3259,7 @@ function ReconciliationView() {
3188
3259
  );
3189
3260
  }) })
3190
3261
  ] }),
3191
- /* @__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: [
3192
3263
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
3193
3264
  /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 text-muted-foreground" }),
3194
3265
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.reconciliation.candidates") })
@@ -3537,10 +3608,8 @@ function buildNav(config, activeView, navigate, t2) {
3537
3608
  id: "commissions",
3538
3609
  label: t2("financial.nav.commissions"),
3539
3610
  icon: "Users",
3540
- children: [
3541
- { id: "commissions-overview", label: t2("financial.nav.overview"), active: activeView === "commissions", onClick: () => navigate("commissions") },
3542
- { id: "commissions-rules", label: t2("financial.nav.rules"), active: activeView === "commissions", onClick: () => navigate("commissions") }
3543
- ]
3611
+ active: activeView === "commissions",
3612
+ onClick: () => navigate("commissions")
3544
3613
  });
3545
3614
  }
3546
3615
  if (config.modules.cards) {
@@ -3548,10 +3617,8 @@ function buildNav(config, activeView, navigate, t2) {
3548
3617
  id: "cards",
3549
3618
  label: t2("financial.nav.cards"),
3550
3619
  icon: "CreditCard",
3551
- children: [
3552
- { id: "cards-overview", label: t2("financial.nav.overview"), active: activeView === "cards", onClick: () => navigate("cards") },
3553
- { id: "cards-reconciliation", label: t2("financial.nav.reconciliation"), active: activeView === "cards", onClick: () => navigate("cards") }
3554
- ]
3620
+ active: activeView === "cards",
3621
+ onClick: () => navigate("cards")
3555
3622
  });
3556
3623
  }
3557
3624
  for (const item of items) {
@@ -3646,7 +3713,6 @@ function FinancialPage({ config, provider, store: store6, registries }) {
3646
3713
  PluginSettingsPanel,
3647
3714
  {
3648
3715
  title: t2("financial.settingsPage.title"),
3649
- subtitle: t2("financial.settingsPage.subtitle"),
3650
3716
  generalSettings: /* @__PURE__ */ jsx(FinancialGeneralSettings, {}),
3651
3717
  registries,
3652
3718
  hostPluginId: "financial",
@@ -4321,6 +4387,7 @@ function createMockFinancialProvider(options) {
4321
4387
  paidAmount: input.amount,
4322
4388
  feeAmount: thisFee,
4323
4389
  status: "paid",
4390
+ settlesMovementId: bill.id,
4324
4391
  dueDate: bill.dueDate,
4325
4392
  paymentDate: input.paymentDate,
4326
4393
  installmentNumber: bill.installmentNumber,
@@ -5018,6 +5085,11 @@ function createSupabaseFinancialProvider() {
5018
5085
  party_id: input.contactId,
5019
5086
  notes: input.observations,
5020
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() } : {},
5021
5093
  metadata: { itemsSummary, contactName: input.contactName, installmentCount: input.installments.length || 1 }
5022
5094
  }).select().single();
5023
5095
  if (order && input.items.length > 0) {
@@ -5060,7 +5132,7 @@ function createSupabaseFinancialProvider() {
5060
5132
  return {
5061
5133
  id: order.id,
5062
5134
  direction: input.direction,
5063
- invoiceDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
5135
+ invoiceDate: input.invoiceDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
5064
5136
  fiscalNumber: referenceNumber,
5065
5137
  totalAmount,
5066
5138
  paidAmount: 0,
@@ -5132,11 +5204,17 @@ function createSupabaseFinancialProvider() {
5132
5204
  card_brand: input.cardBrand ?? null,
5133
5205
  card_installments: input.cardInstallments ?? null,
5134
5206
  notes: input.notes ?? null,
5135
- metadata: input.metadata ?? 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
5136
5212
  };
5137
5213
  let { data, error } = await pub.from(T.movements).insert(paymentRow).select().single();
5138
- if (error && /fee_amount/i.test(error.message ?? "")) {
5139
- 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;
5140
5218
  ({ data, error } = await pub.from(T.movements).insert(rest).select().single());
5141
5219
  }
5142
5220
  if (error) throw error;
@@ -5929,7 +6007,17 @@ var chartOfAccountsEntity = {
5929
6007
  fields: [
5930
6008
  { key: "code", label: "Code", type: "text", required: true, showInTable: true },
5931
6009
  { key: "name", label: "Name", type: "text", required: true, showInTable: true },
5932
- { 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 },
5933
6021
  { key: "parentId", label: "Parent", type: "text", showInTable: false },
5934
6022
  { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
5935
6023
  ],
@@ -6051,20 +6139,20 @@ var financialRegistries = [
6051
6139
  description: "Hierarchical chart of accounts for categorizing transactions",
6052
6140
  display: "tree",
6053
6141
  seedData: [
6054
- { id: "coa-1", code: "1", name: "Revenue", nodeType: "group", isActive: true },
6055
- { id: "coa-1.1", code: "1.1", name: "Sales Revenue", nodeType: "group", parentId: "coa-1", isActive: true },
6056
- { id: "coa-1.1.01", code: "1.1.01", name: "Services", nodeType: "leaf", parentId: "coa-1.1", isActive: true },
6057
- { id: "coa-1.1.02", code: "1.1.02", name: "Products", nodeType: "leaf", parentId: "coa-1.1", isActive: true },
6058
- { id: "coa-2", code: "2", name: "Other Revenue", nodeType: "group", isActive: true },
6059
- { id: "coa-2.1", code: "2.1", name: "Financial Income", nodeType: "leaf", parentId: "coa-2", isActive: true },
6060
- { id: "coa-3", code: "3", name: "Operational Costs", nodeType: "group", isActive: true },
6061
- { id: "coa-3.1", code: "3.1", name: "Materials & Supplies", nodeType: "group", parentId: "coa-3", isActive: true },
6062
- { id: "coa-3.1.01", code: "3.1.01", name: "Raw Materials", nodeType: "leaf", parentId: "coa-3.1", isActive: true },
6063
- { id: "coa-3.1.02", code: "3.1.02", name: "Consumables", nodeType: "leaf", parentId: "coa-3.1", isActive: true },
6064
- { id: "coa-4", code: "4", name: "Expenses", nodeType: "group", isActive: true },
6065
- { id: "coa-4.1", code: "4.1", name: "Rent", nodeType: "leaf", parentId: "coa-4", isActive: true },
6066
- { id: "coa-4.2", code: "4.2", name: "Utilities", nodeType: "leaf", parentId: "coa-4", isActive: true },
6067
- { 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 }
6068
6156
  ]
6069
6157
  },
6070
6158
  {
@@ -6152,6 +6240,7 @@ var en = {
6152
6240
  "financial.cards.currentBill": "Current bill",
6153
6241
  "financial.cards.dueOn": "Due on the {{day}}",
6154
6242
  "financial.cards.installment": "Installment {{current}}/{{total}}",
6243
+ "financial.cards.loading": "Loading...",
6155
6244
  "financial.cards.noCards": "No cards yet",
6156
6245
  "financial.cards.noCardsHint": "Add a credit card in settings to track your bills here",
6157
6246
  "financial.cards.noTransactions": "No card transactions",
@@ -6193,10 +6282,17 @@ var en = {
6193
6282
  "financial.cashRegisters.register": "Register",
6194
6283
  "financial.cashRegisters.sessionHistory": "Session History",
6195
6284
  "financial.cashRegisters.subtitle": "Open, count and close cash sessions",
6196
- "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.",
6197
6288
  "financial.commissions.subtitle": "Team commissions",
6198
6289
  "financial.invoice.accountsPayable": "Accounts Payable",
6199
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)?",
6200
6296
  "financial.invoice.cancelConfirm": "This action cannot be undone.",
6201
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.",
6202
6298
  "financial.invoice.cancelInvoice": "Cancel Invoice",
@@ -6226,6 +6322,8 @@ var en = {
6226
6322
  "financial.invoiceForm.clear": "Clear",
6227
6323
  "financial.invoice.newManualHint": "Open the full invoice form",
6228
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.",
6229
6327
  "financial.invoice.noInstallments": "No installments",
6230
6328
  "financial.invoice.noInvoices": "No invoices yet",
6231
6329
  "financial.invoice.summaryTotal": "Total",
@@ -6530,6 +6628,8 @@ var en = {
6530
6628
  "financial.invoiceForm.cancelling": "Cancelling...",
6531
6629
  "financial.invoiceForm.removeAdjustments": "Remove adjustments",
6532
6630
  "financial.invoiceForm.discount": "Discount",
6631
+ "financial.invoiceForm.account": "Chart account",
6632
+ "financial.invoiceForm.accountNone": "Not assigned",
6533
6633
  "financial.invoiceForm.surcharge": "Surcharge",
6534
6634
  "financial.invoiceForm.total": "Total:",
6535
6635
  "financial.invoiceForm.itemCount": "{{count}} item{{plural}}",
@@ -6603,6 +6703,7 @@ var en = {
6603
6703
  "financial.statements.totalFees": "Fees",
6604
6704
  "financial.statements.net": "Net",
6605
6705
  "financial.statements.transfer": "Transfer",
6706
+ "financial.statements.untitledEntry": "Transaction",
6606
6707
  "financial.statements.reconciled": "Reconciled with the bank statement",
6607
6708
  "financial.statements.imported": "Imported from the bank \xB7 not reconciled",
6608
6709
  "financial.statements.reconciledWith": "Reconciled with {{ref}}",
@@ -6664,6 +6765,7 @@ var ptBR = {
6664
6765
  "financial.cards.currentBill": "Fatura atual",
6665
6766
  "financial.cards.dueOn": "Vence dia {{day}}",
6666
6767
  "financial.cards.installment": "Parcela {{current}}/{{total}}",
6768
+ "financial.cards.loading": "Carregando...",
6667
6769
  "financial.cards.noCards": "Nenhum cart\xE3o cadastrado",
6668
6770
  "financial.cards.noCardsHint": "Adicione um cart\xE3o de cr\xE9dito nas configura\xE7\xF5es para acompanhar suas faturas aqui",
6669
6771
  "financial.cards.noTransactions": "Nenhuma transa\xE7\xE3o com cart\xE3o",
@@ -6705,10 +6807,17 @@ var ptBR = {
6705
6807
  "financial.cashRegisters.register": "Caixa",
6706
6808
  "financial.cashRegisters.sessionHistory": "Hist\xF3rico de Sess\xF5es",
6707
6809
  "financial.cashRegisters.subtitle": "Abrir, contar e fechar sess\xF5es de caixa",
6708
- "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.",
6709
6813
  "financial.commissions.subtitle": "Comiss\xF5es da equipe",
6710
6814
  "financial.invoice.accountsPayable": "Contas a Pagar",
6711
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)?",
6712
6821
  "financial.invoice.cancelConfirm": "Esta a\xE7\xE3o n\xE3o pode ser desfeita.",
6713
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.",
6714
6823
  "financial.invoice.cancelInvoice": "Cancelar Fatura",
@@ -6738,6 +6847,8 @@ var ptBR = {
6738
6847
  "financial.invoiceForm.clear": "Limpar",
6739
6848
  "financial.invoice.newManualHint": "Preencher os campos da conta",
6740
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.",
6741
6852
  "financial.invoice.noInstallments": "Sem parcelas",
6742
6853
  "financial.invoice.noInvoices": "Nenhuma fatura ainda",
6743
6854
  "financial.invoice.summaryTotal": "Total",
@@ -7045,6 +7156,8 @@ var ptBR = {
7045
7156
  "financial.invoiceForm.cancelling": "Cancelando...",
7046
7157
  "financial.invoiceForm.removeAdjustments": "Remover ajustes",
7047
7158
  "financial.invoiceForm.discount": "Desconto",
7159
+ "financial.invoiceForm.account": "Conta cont\xE1bil",
7160
+ "financial.invoiceForm.accountNone": "N\xE3o atribu\xEDda",
7048
7161
  "financial.invoiceForm.surcharge": "Acr\xE9scimo",
7049
7162
  "financial.invoiceForm.total": "Total:",
7050
7163
  "financial.invoiceForm.itemCount": "{{count}} item{{plural}}",
@@ -7118,6 +7231,7 @@ var ptBR = {
7118
7231
  "financial.statements.totalFees": "Taxas",
7119
7232
  "financial.statements.net": "L\xEDquido",
7120
7233
  "financial.statements.transfer": "Transfer\xEAncia",
7234
+ "financial.statements.untitledEntry": "Lan\xE7amento",
7121
7235
  "financial.statements.reconciled": "Conciliado com o extrato banc\xE1rio",
7122
7236
  "financial.statements.imported": "Importado do banco \xB7 n\xE3o conciliado",
7123
7237
  "financial.statements.reconciledWith": "Conciliado com {{ref}}",
@@ -7773,7 +7887,10 @@ function resolveConfig(options) {
7773
7887
  receivables: options?.modules?.receivables !== false,
7774
7888
  cashRegisters: options?.modules?.cashRegisters !== false,
7775
7889
  statements: options?.modules?.statements !== false,
7776
- 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,
7777
7894
  cards: options?.modules?.cards !== false,
7778
7895
  // Opt-in: only shown when an app explicitly enables reconciliation (bank connector present).
7779
7896
  reconciliation: options?.modules?.reconciliation === true,
@@ -7809,6 +7926,7 @@ function createFinancialPlugin(options) {
7809
7926
  const PageComponent = () => React17.createElement(FinancialPage, { config, provider, store: store6, registries });
7810
7927
  return {
7811
7928
  id: "financial",
7929
+ defaultAgentRole: "finance",
7812
7930
  name: config.labels.pageTitle,
7813
7931
  icon: "DollarSign",
7814
7932
  version: "1.0.0",
@@ -7935,28 +8053,15 @@ function createFinancialPlugin(options) {
7935
8053
  { label: "Compare this week's revenue to last week" }
7936
8054
  ]
7937
8055
  },
7938
- {
7939
- id: "financial.create-invoice",
7940
- name: "createInvoice",
7941
- description: "Creates a new invoice/payable for a contact.",
7942
- icon: "FileText",
7943
- mode: "persist",
7944
- limitKey: "movements_month",
7945
- category: "Finance",
7946
- parameters: {
7947
- type: "object",
7948
- properties: {
7949
- contact: { type: "string", description: "Contact/customer name" },
7950
- amount: { type: "number", description: "Invoice amount" },
7951
- description: { type: "string", description: "Invoice description" }
7952
- },
7953
- required: ["contact", "amount"]
7954
- },
7955
- suggestions: [
7956
- { label: "Create an invoice for a new client" }
7957
- ],
7958
- permission: { feature: "financial", action: "create" }
7959
- },
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.
7960
8065
  {
7961
8066
  id: "financial.mark-payment-received",
7962
8067
  name: "markPaymentReceived",
@@ -8014,7 +8119,6 @@ function createFinancialPlugin(options) {
8014
8119
  component: (() => {
8015
8120
  const FinancialSettingsTab = () => React17.createElement(PluginSettingsPanel, {
8016
8121
  title: "Financial Settings",
8017
- subtitle: "Payment methods, accounts, and configurations",
8018
8122
  generalSettings: React17.createElement(FinancialGeneralSettings),
8019
8123
  registries,
8020
8124
  hostPluginId: "financial",