@fayz-ai/plugin-financial 0.18.1 → 0.18.3

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,10 +1,10 @@
1
- import * as React22 from 'react';
2
- import React22__default, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
1
+ import * as React23 from 'react';
2
+ import React23__default, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
3
3
  import { recordRailFields, RECORD_GROUP, activeFacet, createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, registerTranslations, getSupabaseClientOptional, createSafeDataProvider, registerMessageDefaults, reportDataError, useTranslation, createPendingCommand, useUnitMode, useDataChanged, countByTenant, tl, resolveDataProvider, getActiveTenantId, emitDataChanged, exportCSV, useActiveUnitId } from '@fayz-ai/core';
4
- import { ModalTask, toast, Button, useInModalTaskHost, SubpageHeader, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, DataTable, Card, CardContent, Skeleton, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MultiSelect, PageTransition, ModulePage, Badge, TooltipProvider, ConfirmDialog, CurrencyInput, DatePicker, Input, PageHeaderActions, Hint, ModalFooter, Tooltip, TooltipTrigger, TooltipContent, Checkbox, SearchSelect, DashboardCanvas, ComingSoon, PageTabs, StatTiles, ListView, DateRangeControl, FilterPanel, FilterPanelField, SegmentedControl, RequiredFilter, FilterMenuRow, ModalFlow, useSaveBar, Switch, tourAnchor, WizardSteps, KpiCard, ChartWidget, CardHeader, CardTitle, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
4
+ import { ModalTask, toast, Button, useInModalTaskHost, SubpageHeader, DatePicker, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, DataTable, Card, CardContent, Skeleton, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MultiSelect, PageTransition, ModulePage, Badge, TooltipProvider, ConfirmDialog, CurrencyInput, Input, PageHeaderActions, Hint, ModalFooter, PageTabs, Tooltip, TooltipTrigger, TooltipContent, Checkbox, SearchSelect, DashboardCanvas, ComingSoon, StatTiles, ListView, DateRangeControl, FilterPanel, FilterPanelField, SegmentedControl, RequiredFilter, FilterMenuRow, ModalFlow, useSaveBar, Switch, tourAnchor, WizardSteps, KpiCard, ChartWidget, CardHeader, CardTitle, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
- import { createPluginContext, fayzConnectorOAuth, formatCurrency, RightRailPage, createCrudPage, PluginSettingsPanel, dedup, useUnitStore, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailView, PersonLink, useTenantPluginSettings, useModuleNavigation, usePluginOnboardingGate, usePermissionOptional, useModuleNavAccess, PluginOnboardingGateSkeleton, ModuleActionBar, SettingsGroup, ToggleRow, useAgentSurface, parseViewId, EntitlementGate, UpgradePrompt, createArchetypeLookup, useChatStore, adminNavigateTo, revealPage, GetStarted, useAuth, resolveEntityRoute, PermissionGate, useUnitFilter, resolveFayzAgentConnection, getFayzAgentClient, useLookupCreate, OrderFormSkeleton, OrderFormView, OrderFormBlock, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, useLimitGuard, shrinkForVision, toInlineFile, invalidateLimit, askAI, requestChatUpload } from '@fayz-ai/admin';
7
- import { CircleDollarSign, FileText, TriangleAlert, Loader2, Check, X, Ban, CircleDashed, CircleAlert, CircleCheckBig, CircleEllipsis, Pencil, Receipt, Gift, Briefcase, Package, CalendarDays, ExternalLink, DollarSign, LayoutList, Layers, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Paperclip, Camera, Undo2, ChevronRight, Wallet, Plus, AlertTriangle, Filter, TrendingUp, Copy, Trash2, Search, ChevronsUpDown, ChevronsDownUp, Calendar, Building2, User, Unlink, Landmark, ChevronDown, Clock, BookOpen, Info, FolderOpen, Folder, RotateCcw, Calculator, StickyNote, Play, Square, ChevronUp, ClipboardCheck, CalendarClock, BadgeCheck, Target, Settings, ArrowRight, Users, RefreshCw, Download, Link2, Sparkles, FileSearch, Eye, CheckCheck, Pause, MoreVertical, Printer, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, ArrowLeft, ArrowUpRight, ArrowDownRight, EyeOff } from 'lucide-react';
6
+ import { createPluginContext, fayzConnectorOAuth, formatCurrency, RightRailPage, createCrudPage, PluginSettingsPanel, dedup, useUnitStore, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailView, PersonLink, useTenantPluginSettings, useModuleNavigation, usePluginOnboardingGate, usePermissionOptional, useModuleNavAccess, PluginOnboardingGateSkeleton, ModuleActionBar, SettingsGroup, ToggleRow, useAgentSurface, parseViewId, EntitlementGate, UpgradePrompt, createArchetypeLookup, useChatStore, adminNavigateTo, revealPage, useAuth, GetStarted, resolveEntityRoute, PermissionGate, useUnitFilter, resolveFayzAgentConnection, getFayzAgentClient, useLookupCreate, OrderFormSkeleton, OrderFormView, OrderFormBlock, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, useLimitGuard, shrinkForVision, toInlineFile, invalidateLimit, askAI, requestChatUpload } from '@fayz-ai/admin';
7
+ import { CircleDollarSign, FileText, TriangleAlert, Loader2, Check, X, Ban, CircleDashed, CircleAlert, CircleCheckBig, CircleEllipsis, Pencil, Receipt, Gift, Briefcase, Package, CalendarDays, ExternalLink, DollarSign, LayoutList, Layers, Paperclip, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Camera, Undo2, ChevronRight, Wallet, Plus, AlertTriangle, Filter, TrendingUp, Copy, Trash2, Search, ChevronsUpDown, ChevronsDownUp, Calendar, Building2, User, Unlink, ChevronDown, Eye, Inbox, Landmark, Clock, ClipboardCheck, BookOpen, Info, FolderOpen, Folder, RotateCcw, Calculator, Square, Play, ChevronUp, Printer, CalendarClock, BadgeCheck, Target, Settings, ArrowRight, Users, RefreshCw, Download, Sparkles, Link2, FileSearch, CheckCheck, Pause, MoreVertical, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, ArrowLeft, ArrowUpRight, ArrowDownRight, EyeOff } from 'lucide-react';
8
8
  import { createPortal } from 'react-dom';
9
9
  import { createStore as createStore$1 } from 'zustand/vanilla';
10
10
  import { useStore } from 'zustand';
@@ -838,9 +838,9 @@ function startReceiptCapture(labels) {
838
838
  });
839
839
  }
840
840
  function ReceiptAgentSurface({ onOpenDraft }) {
841
- const live = React22.useRef(onOpenDraft);
841
+ const live = React23.useRef(onOpenDraft);
842
842
  live.current = onOpenDraft;
843
- const contribution = React22.useMemo(() => ({
843
+ const contribution = React23.useMemo(() => ({
844
844
  id: "financial.payables.receipt",
845
845
  pluginId: "financial",
846
846
  role: "finance",
@@ -895,11 +895,11 @@ function missingFields(draft) {
895
895
  }
896
896
  function CnabRemittanceModal({ invoices, bankAccounts, provider, onClose }) {
897
897
  const { currency } = useFinancialConfig();
898
- const [step, setStep] = React22__default.useState(0);
899
- const [bankAccountId, setBankAccountId] = React22__default.useState("");
900
- const [draft, setDraft] = React22__default.useState(null);
901
- const [saving, setSaving] = React22__default.useState(false);
902
- const [saveError, setSaveError] = React22__default.useState("");
898
+ const [step, setStep] = React23__default.useState(0);
899
+ const [bankAccountId, setBankAccountId] = React23__default.useState("");
900
+ const [draft, setDraft] = React23__default.useState(null);
901
+ const [saving, setSaving] = React23__default.useState(false);
902
+ const [saveError, setSaveError] = React23__default.useState("");
903
903
  const eligibleAccounts2 = bankAccounts.filter((account2) => account2.accountType === "bank" && account2.isActive);
904
904
  const total = invoices.reduce((sum, invoice) => sum + Math.max(0, invoice.totalAmount - invoice.paidAmount), 0);
905
905
  const account = eligibleAccounts2.find((candidate) => candidate.id === bankAccountId);
@@ -1093,7 +1093,7 @@ function FinancialCounterpartySearchSelect({
1093
1093
  onEdit,
1094
1094
  disabled
1095
1095
  }) {
1096
- const search = React22.useCallback(async (query) => {
1096
+ const search = React23.useCallback(async (query) => {
1097
1097
  const results = await lookup.search(query);
1098
1098
  return results.map((result) => ({
1099
1099
  id: result.id,
@@ -2903,9 +2903,9 @@ var SUBMIT_DRAFT = {
2903
2903
  parameters: { type: "object", properties: {} }
2904
2904
  };
2905
2905
  function PayableDraftSurface({ config, draft }) {
2906
- const live = React22.useRef(draft);
2906
+ const live = React23.useRef(draft);
2907
2907
  live.current = draft;
2908
- const lookup = React22.useMemo(
2908
+ const lookup = React23.useMemo(
2909
2909
  () => config.contactLookup ?? createArchetypeLookup({
2910
2910
  archetype: "person",
2911
2911
  kind: "supplier",
@@ -2914,7 +2914,7 @@ function PayableDraftSurface({ config, draft }) {
2914
2914
  }),
2915
2915
  [config.contactLookup]
2916
2916
  );
2917
- React22.useEffect(() => {
2917
+ React23.useEffect(() => {
2918
2918
  liveDraft = {
2919
2919
  describe: () => live.current.describe(),
2920
2920
  setSupplier: (name, id) => live.current.setSupplier(name, id),
@@ -2925,7 +2925,7 @@ function PayableDraftSurface({ config, draft }) {
2925
2925
  liveDraft = null;
2926
2926
  };
2927
2927
  }, []);
2928
- const contribution = React22.useMemo(() => ({
2928
+ const contribution = React23.useMemo(() => ({
2929
2929
  id: "financial.payables.draft",
2930
2930
  pluginId: "financial",
2931
2931
  role: "finance",
@@ -3000,7 +3000,7 @@ function PayableDraftSurface({ config, draft }) {
3000
3000
  }
3001
3001
  function createFinancialAgentMount(deps) {
3002
3002
  const FinancialAgentMount = () => {
3003
- const contribution = React22.useMemo(() => ({
3003
+ const contribution = React23.useMemo(() => ({
3004
3004
  id: "financial.payables.global",
3005
3005
  pluginId: "financial",
3006
3006
  role: "finance",
@@ -3166,6 +3166,17 @@ var ICON_COLORS = {
3166
3166
  bank_transfer: "text-warning",
3167
3167
  check: "text-muted-foreground"
3168
3168
  };
3169
+ function eligibleAccountsFor(bankAccounts, direction, type) {
3170
+ const txType = type?.transactionType ?? "";
3171
+ const active = bankAccounts.filter((a) => a.isActive !== false);
3172
+ if (direction === "debit") {
3173
+ if (txType === "credit_card") return active.filter((a) => a.accountType === "card_payable");
3174
+ return active.filter((a) => !["card_payable", "card_receivable_credit", "card_receivable_debit"].includes(a.accountType));
3175
+ }
3176
+ const allowed = type?.allowedAccountTypes && type.allowedAccountTypes.length ? type.allowedAccountTypes : ACCOUNT_TYPES_FOR_TX2[txType];
3177
+ if (!allowed || !allowed.length) return bankAccounts;
3178
+ return bankAccounts.filter((a) => allowed.includes(a.accountType));
3179
+ }
3169
3180
  function usePaymentFields(options) {
3170
3181
  const t2 = useTranslation();
3171
3182
  const { currency } = useFinancialConfig();
@@ -3175,7 +3186,10 @@ function usePaymentFields(options) {
3175
3186
  initialDate,
3176
3187
  lockAmount = false,
3177
3188
  disabled = false,
3178
- onSubmit
3189
+ onSubmit,
3190
+ initialMethodTypeId,
3191
+ initialMethodId,
3192
+ initialAccountId
3179
3193
  } = options;
3180
3194
  const cashSessions = useFinancialStore((s) => s.cashSessions);
3181
3195
  const fetchCashSessions = useFinancialStore((s) => s.fetchCashSessions);
@@ -3187,13 +3201,15 @@ function usePaymentFields(options) {
3187
3201
  const fetchPaymentMethods = useFinancialStore((s) => s.fetchPaymentMethods);
3188
3202
  const [amount, setAmount] = useState(initialAmount);
3189
3203
  const [paymentDate, setPaymentDate] = useState(initialDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
3190
- const [selectedTypeId, setSelectedTypeId] = useState("");
3191
- const [paymentMethodId, setPaymentMethodId] = useState("");
3192
- const [bankAccountId, setBankAccountId] = useState("");
3204
+ const [selectedTypeId, setSelectedTypeId] = useState(initialMethodTypeId ?? "");
3205
+ const [paymentMethodId, setPaymentMethodId] = useState(initialMethodId ?? "");
3206
+ const [bankAccountId, setBankAccountId] = useState(initialAccountId ?? "");
3193
3207
  const [cardBrand, setCardBrand] = useState("");
3194
3208
  const [cardInstallments, setCardInstallments] = useState(1);
3195
3209
  const [notes, setNotes] = useState("");
3196
- const [expandMethodPicker, setExpandMethodPicker] = useState(true);
3210
+ const [expandMethodPicker, setExpandMethodPicker] = useState(!initialMethodTypeId);
3211
+ const [editSource, setEditSource] = useState(false);
3212
+ const sourceAnswered = !!initialMethodTypeId && !!initialAccountId && !editSource;
3197
3213
  useEffect(() => {
3198
3214
  fetchBankAccounts();
3199
3215
  fetchPaymentMethods();
@@ -3217,16 +3233,10 @@ function usePaymentFields(options) {
3217
3233
  const payingByCompanyCard = isPayable && txType === "credit_card";
3218
3234
  const needsCard = !isPayable && (txType === "credit_card" || txType === "debit_card");
3219
3235
  const needsCardInstallments = txType === "credit_card";
3220
- const eligibleAccounts2 = useMemo(() => {
3221
- const active = bankAccounts.filter((a) => a.isActive !== false);
3222
- if (isPayable) {
3223
- if (txType === "credit_card") return active.filter((a) => a.accountType === "card_payable");
3224
- return active.filter((a) => !["card_payable", "card_receivable_credit", "card_receivable_debit"].includes(a.accountType));
3225
- }
3226
- const allowed = selectedType?.allowedAccountTypes && selectedType.allowedAccountTypes.length ? selectedType.allowedAccountTypes : ACCOUNT_TYPES_FOR_TX2[txType];
3227
- if (!allowed || !allowed.length) return bankAccounts;
3228
- return bankAccounts.filter((a) => allowed.includes(a.accountType));
3229
- }, [bankAccounts, selectedType, txType, isPayable]);
3236
+ const eligibleAccounts2 = useMemo(
3237
+ () => eligibleAccountsFor(bankAccounts, direction, selectedType),
3238
+ [bankAccounts, direction, selectedType]
3239
+ );
3230
3240
  useEffect(() => {
3231
3241
  if (selectedTypeId && !bankAccountId && eligibleAccounts2.length) {
3232
3242
  setBankAccountId(eligibleAccounts2[0].id);
@@ -3250,9 +3260,34 @@ function usePaymentFields(options) {
3250
3260
  if (!blockedReason) onSubmit();
3251
3261
  }
3252
3262
  const SelectedIcon = selectedType ? TYPE_ICONS[txType] ?? Banknote : null;
3263
+ const selectedAccount = bankAccounts.find((a) => a.id === bankAccountId);
3253
3264
  const node = /* @__PURE__ */ jsxs("div", { onKeyDown: handleKeyDown, children: [
3254
3265
  /* @__PURE__ */ jsxs("fieldset", { disabled, className: "space-y-4", children: [
3255
- /* @__PURE__ */ jsxs("div", { children: [
3266
+ sourceAnswered && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-button border bg-muted/30 px-3 py-2.5", children: [
3267
+ SelectedIcon && /* @__PURE__ */ jsx(SelectedIcon, { className: `h-4 w-4 shrink-0 ${ICON_COLORS[txType] ?? ""}`, "aria-hidden": true }),
3268
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 truncate text-sm", children: [
3269
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: selectedType?.name ?? "" }),
3270
+ selectedAccount && /* @__PURE__ */ jsxs(Fragment, { children: [
3271
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: " \xB7 " }),
3272
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: selectedAccount.name })
3273
+ ] })
3274
+ ] }),
3275
+ /* @__PURE__ */ jsx(
3276
+ "button",
3277
+ {
3278
+ type: "button",
3279
+ onClick: () => {
3280
+ setEditSource(true);
3281
+ setExpandMethodPicker(true);
3282
+ },
3283
+ "aria-label": t2("financial.payment.changeSource"),
3284
+ title: t2("financial.payment.changeSource"),
3285
+ className: "shrink-0 rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
3286
+ children: /* @__PURE__ */ jsx(Pencil, { className: "h-3 w-3" })
3287
+ }
3288
+ )
3289
+ ] }),
3290
+ /* @__PURE__ */ jsxs("div", { hidden: sourceAnswered, children: [
3256
3291
  /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t2("financial.payment.method") }),
3257
3292
  /* @__PURE__ */ jsx(
3258
3293
  "div",
@@ -3366,7 +3401,7 @@ function usePaymentFields(options) {
3366
3401
  " \u2192"
3367
3402
  ] })
3368
3403
  ] }),
3369
- payingByCompanyCard && /* @__PURE__ */ jsxs("div", { className: "stagger-field", style: { animationDelay: "50ms" }, children: [
3404
+ payingByCompanyCard && !sourceAnswered && /* @__PURE__ */ jsxs("div", { className: "stagger-field", style: { animationDelay: "50ms" }, children: [
3370
3405
  /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t2("financial.payment.whichCard") }),
3371
3406
  eligibleAccounts2.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-1 rounded-lg border border-dashed border-input bg-muted/30 px-3 py-2.5 text-center", children: [
3372
3407
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.payment.noCompanyCards") }),
@@ -3404,7 +3439,7 @@ function usePaymentFields(options) {
3404
3439
  );
3405
3440
  }) })
3406
3441
  ] }),
3407
- !payingByCompanyCard && /* @__PURE__ */ jsxs("div", { className: "stagger-field", style: { animationDelay: "100ms" }, children: [
3442
+ !payingByCompanyCard && !sourceAnswered && /* @__PURE__ */ jsxs("div", { className: "stagger-field", style: { animationDelay: "100ms" }, children: [
3408
3443
  /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t2("financial.payment.bankAccount") }),
3409
3444
  eligibleAccounts2.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-1 rounded-lg border border-dashed border-input bg-muted/30 px-3 py-2.5 text-center", children: [
3410
3445
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: bankAccounts.length > 0 && selectedType ? t2("financial.payment.noAccountForMethod", { method: selectedType.name }) : t2("financial.payment.noAccounts") }),
@@ -3458,16 +3493,21 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
3458
3493
  const payMovement = useFinancialStore((s) => s.payMovement);
3459
3494
  const remaining = movement.amount - movement.paidAmount;
3460
3495
  const [saving, setSaving] = useState(false);
3461
- const command = React22__default.useRef(createPendingCommand(() => createIdempotencyKey("financial:payment"), (input) => input.movementId));
3496
+ const command = React23__default.useRef(createPendingCommand(() => createIdempotencyKey("financial:payment"), (input) => input.movementId));
3462
3497
  const [pending2, setPending] = useState(false);
3463
3498
  const [visible, setVisible] = useState(false);
3464
- React22__default.useEffect(() => {
3499
+ React23__default.useEffect(() => {
3465
3500
  requestAnimationFrame(() => setVisible(true));
3466
3501
  }, []);
3502
+ const source = movement.metadata ?? {};
3503
+ const str2 = (v) => typeof v === "string" && v ? v : void 0;
3467
3504
  const fields = usePaymentFields({
3468
3505
  direction: movement.direction,
3469
3506
  initialAmount: remaining,
3470
3507
  disabled: pending2,
3508
+ initialMethodTypeId: str2(source.payMethodTypeId),
3509
+ initialMethodId: str2(source.payMethodId),
3510
+ initialAccountId: str2(source.payAccountId),
3471
3511
  onSubmit: () => {
3472
3512
  if (!saving) void handlePay();
3473
3513
  }
@@ -3575,6 +3615,76 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
3575
3615
  document.body
3576
3616
  );
3577
3617
  }
3618
+ var NONE = "__none__";
3619
+ var TRIGGER_CLASS = "h-auto w-auto justify-start gap-1 rounded-none border-0 border-b border-dashed border-current bg-transparent p-0 pb-px text-xs font-normal shadow-none ring-offset-0 focus:ring-0 focus:ring-offset-0 [&>svg]:h-3 [&>svg]:w-3";
3620
+ function InstallmentSourceFields({
3621
+ direction,
3622
+ value,
3623
+ onChange,
3624
+ disabled = false
3625
+ }) {
3626
+ const t2 = useTranslation();
3627
+ const paymentMethodTypes = useFinancialStore((s) => s.paymentMethodTypes);
3628
+ const bankAccounts = useFinancialStore((s) => s.bankAccounts);
3629
+ const selectedType = paymentMethodTypes.find((tp) => tp.id === value.payMethodTypeId);
3630
+ const selectedAccount = bankAccounts.find((a) => a.id === value.payAccountId);
3631
+ const accounts = eligibleAccountsFor(bankAccounts, direction, selectedType);
3632
+ function pickMethod(raw) {
3633
+ const typeId = raw === NONE ? "" : raw;
3634
+ const tipo = paymentMethodTypes.find((tp) => tp.id === typeId);
3635
+ const elegiveis = eligibleAccountsFor(bankAccounts, direction, tipo);
3636
+ const conta = elegiveis.length === 1 ? elegiveis[0].id : elegiveis.some((a) => a.id === value.payAccountId) ? value.payAccountId : void 0;
3637
+ onChange({ payMethodTypeId: typeId || void 0, payAccountId: typeId ? conta : void 0 });
3638
+ }
3639
+ const tone = (filled) => filled ? "text-foreground" : "text-muted-foreground";
3640
+ return /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [
3641
+ /* @__PURE__ */ jsxs(
3642
+ Select,
3643
+ {
3644
+ value: value.payMethodTypeId ?? "",
3645
+ disabled,
3646
+ onValueChange: pickMethod,
3647
+ children: [
3648
+ /* @__PURE__ */ jsx(
3649
+ SelectTrigger,
3650
+ {
3651
+ "aria-label": t2("financial.invoiceForm.paidWith"),
3652
+ className: `${TRIGGER_CLASS} ${tone(!!selectedType)}`,
3653
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t2("financial.invoiceForm.paidWith") })
3654
+ }
3655
+ ),
3656
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
3657
+ paymentMethodTypes.map((tp) => /* @__PURE__ */ jsx(SelectItem, { value: tp.id, children: tp.name }, tp.id)),
3658
+ value.payMethodTypeId && /* @__PURE__ */ jsx(SelectItem, { value: NONE, className: "text-muted-foreground", children: t2("financial.invoiceForm.clear") })
3659
+ ] })
3660
+ ]
3661
+ }
3662
+ ),
3663
+ /* @__PURE__ */ jsxs(
3664
+ Select,
3665
+ {
3666
+ value: value.payAccountId ?? "",
3667
+ disabled: disabled || !value.payMethodTypeId,
3668
+ onValueChange: (raw) => onChange({ ...value, payAccountId: raw === NONE ? void 0 : raw }),
3669
+ children: [
3670
+ /* @__PURE__ */ jsx(
3671
+ SelectTrigger,
3672
+ {
3673
+ "aria-label": t2("financial.invoiceForm.paidFrom"),
3674
+ title: value.payMethodTypeId ? void 0 : t2("financial.invoiceForm.selectMethodFirst"),
3675
+ className: `${TRIGGER_CLASS} ${tone(!!selectedAccount)}`,
3676
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t2("financial.payment.account") })
3677
+ }
3678
+ ),
3679
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
3680
+ accounts.map((a) => /* @__PURE__ */ jsx(SelectItem, { value: a.id, children: a.name }, a.id)),
3681
+ value.payAccountId && /* @__PURE__ */ jsx(SelectItem, { value: NONE, className: "text-muted-foreground", children: t2("financial.invoiceForm.clear") })
3682
+ ] })
3683
+ ]
3684
+ }
3685
+ )
3686
+ ] });
3687
+ }
3578
3688
 
3579
3689
  // src/views/focusItem.ts
3580
3690
  var CHAVE = "fayz:financial:focus-item";
@@ -3599,9 +3709,9 @@ function consumirItemParaAbrir(invoiceId) {
3599
3709
  }
3600
3710
  }
3601
3711
  function useStockedItemTypes(itemTypes, entityLookups) {
3602
- const [empty, setEmpty] = React22__default.useState(null);
3712
+ const [empty, setEmpty] = React23__default.useState(null);
3603
3713
  const backed = itemTypes.filter((type) => entityLookups[type.value]?.count).map((type) => type.value).sort().join(",");
3604
- React22__default.useEffect(() => {
3714
+ React23__default.useEffect(() => {
3605
3715
  const kinds = backed ? backed.split(",") : [];
3606
3716
  if (!kinds.length) {
3607
3717
  setEmpty(null);
@@ -3622,7 +3732,7 @@ function useStockedItemTypes(itemTypes, entityLookups) {
3622
3732
  cancelled = true;
3623
3733
  };
3624
3734
  }, [backed]);
3625
- return React22__default.useMemo(
3735
+ return React23__default.useMemo(
3626
3736
  () => empty?.size ? itemTypes.filter((type) => !empty.has(type.value)) : itemTypes,
3627
3737
  [itemTypes, empty]
3628
3738
  );
@@ -3790,6 +3900,13 @@ var formId = 1;
3790
3900
  function fid() {
3791
3901
  return `f${formId++}`;
3792
3902
  }
3903
+ function sourceMeta(inst) {
3904
+ return {
3905
+ payMethodTypeId: inst.payMethodTypeId || null,
3906
+ payMethodId: inst.payMethodId || null,
3907
+ payAccountId: inst.payAccountId || null
3908
+ };
3909
+ }
3793
3910
  function filterByCodeOrName(rows, query) {
3794
3911
  const q = query.trim().toLowerCase();
3795
3912
  if (!q) return rows;
@@ -3860,14 +3977,14 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
3860
3977
  };
3861
3978
  }, [giftCardsEnabled, provider]);
3862
3979
  const entityLookups = useMemo(() => giftCardLookup ? { ...config.entityLookups, gift_card: giftCardLookup } : config.entityLookups, [config.entityLookups, giftCardLookup]);
3863
- const translatedItemTypes = React22__default.useMemo(
3980
+ const translatedItemTypes = React23__default.useMemo(
3864
3981
  () => itemTypesRaw.map((tp) => {
3865
3982
  const traduzido = t2(`financial.itemType.${tp.value}`);
3866
3983
  return traduzido === `financial.itemType.${tp.value}` ? tp : { ...tp, label: traduzido };
3867
3984
  }),
3868
3985
  [itemTypesRaw, t2]
3869
3986
  );
3870
- const offeredItemTypes = React22__default.useMemo(
3987
+ const offeredItemTypes = React23__default.useMemo(
3871
3988
  () => availableInvoiceItemTypes(
3872
3989
  giftCardsEnabled && !translatedItemTypes.some((type) => type.value === "gift_card") ? [...translatedItemTypes, { value: "gift_card", label: t2("financial.itemType.gift_card"), icon: "Gift" }] : translatedItemTypes,
3873
3990
  Object.keys(entityLookups),
@@ -4055,11 +4172,18 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4055
4172
  const movements = await provider.getMovements({ invoiceId: editId });
4056
4173
  const invoiceMovements = movements.data.filter((m) => m.movementKind === "bill").sort((a, b) => (a.installmentNumber ?? 0) - (b.installmentNumber ?? 0));
4057
4174
  setSavedMovements(invoiceMovements);
4058
- setInstallments(invoiceMovements.map((m) => ({
4059
- _id: fid(),
4060
- amount: m.amount,
4061
- dueDate: m.dueDate
4062
- })));
4175
+ setInstallments(invoiceMovements.map((m) => {
4176
+ const meta = m.metadata ?? {};
4177
+ const str2 = (v) => typeof v === "string" && v ? v : void 0;
4178
+ return {
4179
+ _id: fid(),
4180
+ amount: m.amount,
4181
+ dueDate: m.dueDate,
4182
+ payMethodTypeId: str2(meta.payMethodTypeId),
4183
+ payMethodId: str2(meta.payMethodId),
4184
+ payAccountId: str2(meta.payAccountId)
4185
+ };
4186
+ }));
4063
4187
  setLoaded(true);
4064
4188
  setLoading(false);
4065
4189
  })();
@@ -4123,13 +4247,13 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4123
4247
  if (isEdit || totalAmount <= 0 || installments.length > 0) return;
4124
4248
  generateInstallments(1);
4125
4249
  }, [isEdit, totalAmount, installments.length]);
4126
- const fromCard = React22__default.useMemo(
4250
+ const fromCard = React23__default.useMemo(
4127
4251
  () => fromCardId ? bankAccounts.find((a) => a.id === fromCardId) : void 0,
4128
4252
  [fromCardId, bankAccounts]
4129
4253
  );
4130
4254
  const cardLast4 = (fromCard?.accountNumber ?? "").replace(/\D/g, "").slice(-4);
4131
4255
  const nomeJaTemOsDigitos = !!cardLast4 && (fromCard?.name ?? "").replace(/\D/g, "").endsWith(cardLast4);
4132
- const cardMethodName = React22__default.useMemo(() => {
4256
+ const cardMethodName = React23__default.useMemo(() => {
4133
4257
  const id = installments[0]?.payMethodTypeId;
4134
4258
  const escolhida = id ? paymentMethodTypes.find((tp) => tp.id === id) : void 0;
4135
4259
  return (escolhida ?? paymentMethodTypes.find((tp) => tp.transactionType === "credit_card"))?.name;
@@ -4248,8 +4372,12 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4248
4372
  });
4249
4373
  }
4250
4374
  installments.forEach((inst, i) => {
4251
- if (inst.receiptUrl && movInputs[i]) {
4252
- movInputs[i] = { ...movInputs[i], metadata: { ...movInputs[i].metadata ?? {}, receiptUrl: inst.receiptUrl } };
4375
+ const extra = {
4376
+ ...inst.receiptUrl ? { receiptUrl: inst.receiptUrl } : {},
4377
+ ...sourceMeta(inst)
4378
+ };
4379
+ if (Object.keys(extra).length > 0 && movInputs[i]) {
4380
+ movInputs[i] = { ...movInputs[i], metadata: { ...movInputs[i].metadata ?? {}, ...extra } };
4253
4381
  }
4254
4382
  });
4255
4383
  if (isEdit && editId) {
@@ -4269,16 +4397,26 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4269
4397
  });
4270
4398
  const shapeChanged = itemsDirty || installments.length !== savedMovements.length || installments.some((inst, i) => Math.round(inst.amount * 100) !== Math.round((savedMovements[i]?.amount ?? 0) * 100));
4271
4399
  if (canReschedule && provider.rescheduleInvoice && shapeChanged) {
4272
- await provider.rescheduleInvoice(editId, installments.map((inst) => ({
4400
+ await provider.rescheduleInvoice(editId, installments.map((inst, i) => ({
4273
4401
  dueDate: inst.dueDate,
4274
- amount: inst.amount
4402
+ amount: inst.amount,
4403
+ // A RPC SUBSTITUI a metadata quando ela vem. O que já estava lá —
4404
+ // o comprovante, acima de tudo — tem de ir junto, ou reparcelar
4405
+ // apagaria a prova de um pagamento.
4406
+ metadata: { ...savedMovements[i]?.metadata ?? {}, ...sourceMeta(inst) }
4275
4407
  })));
4276
4408
  } else if (provider.updateInstallment) {
4277
4409
  await Promise.all(installments.map((inst, i) => {
4278
4410
  const saved = savedMovements[i];
4279
4411
  if (!saved || saved.status === "paid") return null;
4280
- if (inst.dueDate === saved.dueDate) return null;
4281
- return provider.updateInstallment(saved.id, { dueDate: inst.dueDate });
4412
+ const meta = sourceMeta(inst);
4413
+ const savedMeta = saved.metadata ?? {};
4414
+ const sourceChanged = Object.keys(meta).some((k) => (savedMeta[k] ?? null) !== meta[k]);
4415
+ if (inst.dueDate === saved.dueDate && !sourceChanged) return null;
4416
+ return provider.updateInstallment(saved.id, {
4417
+ ...inst.dueDate !== saved.dueDate ? { dueDate: inst.dueDate } : {},
4418
+ ...sourceChanged ? { metadata: { ...savedMeta, ...meta } } : {}
4419
+ });
4282
4420
  }).filter(Boolean));
4283
4421
  }
4284
4422
  onSaved?.(editId);
@@ -4328,9 +4466,13 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4328
4466
  const [payingNewId, setPayingNewId] = useState(null);
4329
4467
  const createdId = useRef(null);
4330
4468
  async function pagarParcela(index) {
4469
+ const comFonte = (mov) => ({
4470
+ ...mov,
4471
+ metadata: { ...mov.metadata ?? {}, ...sourceMeta(installments[index]) }
4472
+ });
4331
4473
  const saved = savedMovements[index];
4332
4474
  if (saved) {
4333
- setPayingMovement(saved);
4475
+ setPayingMovement(comFonte(saved));
4334
4476
  return;
4335
4477
  }
4336
4478
  const id = await handleSave({ stay: true });
@@ -4342,7 +4484,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4342
4484
  return;
4343
4485
  }
4344
4486
  setPayingNewId(id);
4345
- setPayingMovement(bill);
4487
+ setPayingMovement(comFonte(bill));
4346
4488
  }
4347
4489
  const fields = {
4348
4490
  contactName,
@@ -4417,6 +4559,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4417
4559
  n
4418
4560
  )) })
4419
4561
  ),
4562
+ installments.length > 0 && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.invoiceForm.scheduleSourceHint") }),
4420
4563
  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) => {
4421
4564
  const patch = (next) => {
4422
4565
  const updated = [...installments];
@@ -4428,6 +4571,11 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4428
4571
  const isPartial = saved?.status === "partial";
4429
4572
  const StatusIcon = isPaid2 ? CircleCheckBig : CircleDashed;
4430
4573
  const canAttach = isEdit ? !!saved && !isPaid2 : true;
4574
+ const semFonte = Math.abs(inst.amount) < 5e-3 && !isPaid2 && !isPartial;
4575
+ const fonteTravada = isPaid2 || isPartial;
4576
+ const formaEscolhida = paymentMethodTypes.find((tp) => tp.id === inst.payMethodTypeId);
4577
+ const contaEscolhida = bankAccounts.find((a) => a.id === inst.payAccountId);
4578
+ const faltaParaPagar = !inst.payMethodTypeId ? t2("financial.payment.methodRequired") : !inst.payAccountId ? t2("financial.payment.accountRequired") : null;
4431
4579
  return /* @__PURE__ */ jsxs("div", { children: [
4432
4580
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 px-3 py-2", children: [
4433
4581
  /* @__PURE__ */ jsxs("span", { className: "w-6 shrink-0 text-xs font-medium text-muted-foreground", children: [
@@ -4439,12 +4587,25 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4439
4587
  /* @__PURE__ */ jsx(StatusIcon, { className: "h-2.5 w-2.5" }),
4440
4588
  t2(isPaid2 ? "financial.invoice.statusPaid" : isPartial ? "financial.invoice.statusPartial" : "financial.invoice.statusPending")
4441
4589
  ] }),
4590
+ semFonte ? (
4591
+ // Parcela zerada (brinde, 100% de desconto) não sai por forma
4592
+ // nenhuma — pedir a forma ali só confunde.
4593
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: "\u2014" })
4594
+ ) : fonteTravada ? /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-xs text-muted-foreground", children: [formaEscolhida?.name, contaEscolhida?.name].filter(Boolean).join(" \xB7 ") }) : /* @__PURE__ */ jsx(
4595
+ InstallmentSourceFields,
4596
+ {
4597
+ direction,
4598
+ value: { payMethodTypeId: inst.payMethodTypeId, payAccountId: inst.payAccountId },
4599
+ onChange: (next) => patch(next)
4600
+ }
4601
+ ),
4442
4602
  /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [
4443
4603
  !hasPayments ? /* @__PURE__ */ jsx(
4444
4604
  DatePicker,
4445
4605
  {
4446
4606
  value: inst.dueDate,
4447
- variant: "chip",
4607
+ variant: "inline",
4608
+ format: "numeric",
4448
4609
  onChange: (v) => patch({ dueDate: v })
4449
4610
  }
4450
4611
  ) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: inst.dueDate }),
@@ -4467,12 +4628,22 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4467
4628
  children: inst.receiptUrl ? /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Camera, { className: "h-3.5 w-3.5" })
4468
4629
  }
4469
4630
  ),
4470
- !isPaid2 && (saved || !isEdit && !!contactId && items.length > 0 && inst.amount > 0) && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", disabled: saving, onClick: () => {
4471
- void pagarParcela(i);
4472
- }, children: [
4473
- /* @__PURE__ */ jsx(DollarSign, { className: "mr-1 h-3.5 w-3.5" }),
4474
- t2("financial.invoice.pay")
4475
- ] })
4631
+ !isPaid2 && (saved || !isEdit && !!contactId && items.length > 0 && inst.amount > 0) && /* @__PURE__ */ jsxs(
4632
+ Button,
4633
+ {
4634
+ size: "sm",
4635
+ variant: "outline",
4636
+ disabled: saving || !!faltaParaPagar,
4637
+ title: faltaParaPagar ?? void 0,
4638
+ onClick: () => {
4639
+ void pagarParcela(i);
4640
+ },
4641
+ children: [
4642
+ /* @__PURE__ */ jsx(DollarSign, { className: "mr-1 h-3.5 w-3.5" }),
4643
+ t2("financial.invoice.pay")
4644
+ ]
4645
+ }
4646
+ )
4476
4647
  ] })
4477
4648
  ] }),
4478
4649
  !isEdit && inst.receiptUrl && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t bg-muted/20 px-3 py-2", children: [
@@ -4484,31 +4655,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
4484
4655
  className: "h-8 w-8 shrink-0 rounded border object-cover"
4485
4656
  }
4486
4657
  ),
4487
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] font-medium text-success", children: t2("financial.invoiceForm.willBePaid") }),
4488
- /* @__PURE__ */ jsxs(
4489
- "select",
4490
- {
4491
- value: inst.payMethodTypeId ?? "",
4492
- onChange: (e) => patch({ payMethodTypeId: e.target.value }),
4493
- 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",
4494
- children: [
4495
- /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectMethod") }),
4496
- paymentMethodTypes.map((tp) => /* @__PURE__ */ jsx("option", { value: tp.id, children: tp.name }, tp.id))
4497
- ]
4498
- }
4499
- ),
4500
- /* @__PURE__ */ jsxs(
4501
- "select",
4502
- {
4503
- value: inst.payAccountId ?? (bankAccounts.length === 1 ? bankAccounts[0].id : ""),
4504
- onChange: (e) => patch({ payAccountId: e.target.value }),
4505
- 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",
4506
- children: [
4507
- /* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectAccount") }),
4508
- bankAccounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
4509
- ]
4510
- }
4511
- ),
4658
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[10px] font-medium text-success", children: t2("financial.invoiceForm.willBePaid") }),
4512
4659
  /* @__PURE__ */ jsx(
4513
4660
  "button",
4514
4661
  {
@@ -5088,6 +5235,24 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
5088
5235
  setCancelReason("");
5089
5236
  cancelRequestKey.current = createIdempotencyKey("financial:invoice-cancel");
5090
5237
  }
5238
+ async function patchSource(mov, next) {
5239
+ const metadata = { ...mov.metadata ?? {}, ...next };
5240
+ setMovements((prev) => prev.map((m) => m.id === mov.id ? { ...m, metadata } : m));
5241
+ try {
5242
+ await provider.updateInstallment?.(mov.id, { metadata });
5243
+ } catch {
5244
+ void loadData();
5245
+ }
5246
+ }
5247
+ async function patchDueDate(mov, dueDate) {
5248
+ if (!dueDate || dueDate === mov.dueDate) return;
5249
+ setMovements((prev) => prev.map((m) => m.id === mov.id ? { ...m, dueDate } : m));
5250
+ try {
5251
+ await provider.updateInstallment?.(mov.id, { dueDate });
5252
+ } catch {
5253
+ void loadData();
5254
+ }
5255
+ }
5091
5256
  const reqRef = useRef(0);
5092
5257
  async function loadData() {
5093
5258
  const req = ++reqRef.current;
@@ -5178,8 +5343,8 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
5178
5343
  setCancelling(false);
5179
5344
  }
5180
5345
  }
5181
- const [chartNames, setChartNames] = React22__default.useState(/* @__PURE__ */ new Map());
5182
- React22__default.useEffect(() => {
5346
+ const [chartNames, setChartNames] = React23__default.useState(/* @__PURE__ */ new Map());
5347
+ React23__default.useEffect(() => {
5183
5348
  let cancelled2 = false;
5184
5349
  void provider.getChartOfAccounts().then((rows) => {
5185
5350
  if (!cancelled2) setChartNames(new Map(rows.map((r) => [r.id, r.name])));
@@ -5312,155 +5477,189 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
5312
5477
  mainSections: (
5313
5478
  // O cronograma no MESMO bloco que os itens e o pagamento — antes o título
5314
5479
  // ficava fora do cartão, e a seção lia como se fosse de outra tela.
5315
- /* @__PURE__ */ jsx(OrderDetailBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "-mx-4 -mb-4", children: [
5316
- 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) => {
5317
- const remaining = mov.amount - mov.paidAmount;
5318
- const isPaid2 = mov.status === "paid";
5319
- const isPartial = mov.status === "partial";
5320
- const hasPaidDetails = isPaid2 || isPartial;
5321
- const movStatus = STATUS_COLORS[mov.status] ?? STATUS_COLORS.pending;
5322
- const MovIcon = movStatus.icon;
5323
- const instPayments = settlements.byBillId.get(mov.id) ?? [];
5324
- const hows = instPayments.map((payment) => describePayment(payment, howCtx));
5325
- const paymentSummary = [...new Set(hows.map((how2) => [how2.method, how2.account?.name].filter(Boolean).join(" \xB7 ")).filter(Boolean))].join(" / ");
5326
- const how = hows[0];
5327
- const howCard = how?.account?.accountType === "card_payable" ? how.account : void 0;
5328
- const HowIcon = HOW_ICONS[how?.typeKind ?? ""] ?? (how ? CreditCard : null);
5329
- return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
5330
- "div",
5331
- {
5332
- className: `flex items-center gap-3 px-4 py-3 ${hasPaidDetails ? "cursor-pointer hover:bg-muted/20 transition-colors" : ""}`,
5333
- onClick: hasPaidDetails ? () => setReceiptBill(mov) : void 0,
5334
- children: [
5335
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-muted-foreground w-6 shrink-0", children: [
5336
- "#",
5337
- mov.installmentNumber
5338
- ] }),
5339
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
5340
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5341
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium tabular-nums", children: formatCurrency(mov.amount, currency) }),
5342
- /* @__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: [
5480
+ /* @__PURE__ */ jsxs(OrderDetailBlock, { title: t2("financial.invoice.paymentSchedule"), children: [
5481
+ attachments.length > 0 && /* @__PURE__ */ jsxs(
5482
+ "button",
5483
+ {
5484
+ type: "button",
5485
+ onClick: () => setViewing(0),
5486
+ title: attachments.map((a) => a.fileName).join("\n"),
5487
+ className: "mb-2 inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
5488
+ children: [
5489
+ /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5", "aria-hidden": true }),
5490
+ attachments.length === 1 ? "Ver o anexo" : `Ver os ${attachments.length} anexos`
5491
+ ]
5492
+ }
5493
+ ),
5494
+ /* @__PURE__ */ jsxs("div", { className: "-mx-4 -mb-4", children: [
5495
+ 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) => {
5496
+ const remaining = mov.amount - mov.paidAmount;
5497
+ const isPaid2 = mov.status === "paid";
5498
+ const isPartial = mov.status === "partial";
5499
+ const hasPaidDetails = isPaid2 || isPartial;
5500
+ const movStatus = STATUS_COLORS[mov.status] ?? STATUS_COLORS.pending;
5501
+ const MovIcon = movStatus.icon;
5502
+ const instPayments = settlements.byBillId.get(mov.id) ?? [];
5503
+ const hows = instPayments.map((payment) => describePayment(payment, howCtx));
5504
+ const paymentSummary = [...new Set(hows.map((how2) => [how2.method, how2.account?.name].filter(Boolean).join(" \xB7 ")).filter(Boolean))].join(" / ");
5505
+ const how = hows[0];
5506
+ const howCard = how?.account?.accountType === "card_payable" ? how.account : void 0;
5507
+ const HowIcon = HOW_ICONS[how?.typeKind ?? ""] ?? (how ? CreditCard : null);
5508
+ const srcMeta = mov.metadata ?? {};
5509
+ const srcStr = (v) => typeof v === "string" && v ? v : void 0;
5510
+ const srcTypeId = srcStr(srcMeta.payMethodTypeId);
5511
+ const srcAccountId = srcStr(srcMeta.payAccountId);
5512
+ const editavel = !hasPaidDetails && invoice.status !== "cancelled";
5513
+ const canChooseSource = editavel && Math.abs(mov.amount) >= 5e-3;
5514
+ const faltaParaPagar = !srcTypeId ? t2("financial.payment.methodRequired") : !srcAccountId ? t2("financial.payment.accountRequired") : null;
5515
+ return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
5516
+ "div",
5517
+ {
5518
+ className: `flex items-center gap-3 px-4 py-3 ${hasPaidDetails ? "cursor-pointer hover:bg-muted/20 transition-colors" : ""}`,
5519
+ onClick: hasPaidDetails ? () => setReceiptBill(mov) : void 0,
5520
+ children: [
5521
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-muted-foreground w-6 shrink-0", children: [
5522
+ "#",
5523
+ mov.installmentNumber
5524
+ ] }),
5525
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-x-2 gap-y-0.5", children: [
5526
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-medium tabular-nums", children: formatCurrency(mov.amount, currency) }),
5527
+ /* @__PURE__ */ jsxs("span", { className: `inline-flex shrink-0 items-center gap-0.5 rounded-full px-1.5 py-0.5 text-[9px] font-medium capitalize ${movStatus.bg}`, children: [
5343
5528
  /* @__PURE__ */ jsx(MovIcon, { className: "h-2 w-2" }),
5344
5529
  " ",
5345
5530
  t2(movStatus.labelKey)
5346
- ] })
5531
+ ] }),
5532
+ /* @__PURE__ */ jsxs(
5533
+ "span",
5534
+ {
5535
+ className: "flex shrink-0 items-center gap-1 whitespace-nowrap text-[10px] text-muted-foreground",
5536
+ onClick: (e) => e.stopPropagation(),
5537
+ children: [
5538
+ t2("financial.payment.due"),
5539
+ editavel ? /* @__PURE__ */ jsx(
5540
+ DatePicker,
5541
+ {
5542
+ value: mov.dueDate,
5543
+ variant: "inline",
5544
+ format: "numeric",
5545
+ onChange: (v) => {
5546
+ void patchDueDate(mov, v);
5547
+ }
5548
+ }
5549
+ ) : mov.dueDate,
5550
+ mov.paidAmount > 0 && !isPaid2 && ` \xB7 ${t2("financial.invoice.paidLabel")} ${formatCurrency(mov.paidAmount, currency)}`,
5551
+ mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
5552
+ ]
5553
+ }
5554
+ )
5347
5555
  ] }),
5348
- /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
5349
- t2("financial.invoice.due"),
5350
- " ",
5351
- mov.dueDate,
5352
- mov.paidAmount > 0 && !isPaid2 && ` \xB7 ${t2("financial.invoice.paidLabel")} ${formatCurrency(mov.paidAmount, currency)}`,
5353
- mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
5354
- ] })
5355
- ] }),
5356
- paymentSummary && /* @__PURE__ */ jsx(
5357
- "button",
5358
- {
5359
- type: "button",
5360
- onClick: (e) => {
5361
- e.stopPropagation();
5362
- if (hasPaidDetails) setReceiptBill(mov);
5363
- },
5364
- title: paymentSummary,
5365
- "aria-label": paymentSummary,
5366
- className: "inline-flex shrink-0 items-center rounded-md px-1 py-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
5367
- children: howCard ? /* @__PURE__ */ jsx(CardVisual, { lastFour: howCard.lastFour ?? void 0, color: howCard.color ?? void 0, brandId: howCard.cardBrandId ?? void 0 }) : HowIcon ? /* @__PURE__ */ jsx(HowIcon, { className: "h-4 w-4", "aria-hidden": true }) : null
5368
- }
5369
- ),
5370
- attachments.length > 0 && /* @__PURE__ */ jsxs(
5371
- "button",
5372
- {
5373
- type: "button",
5374
- onClick: (e) => {
5375
- e.stopPropagation();
5376
- setViewing(0);
5377
- },
5378
- title: attachments.map((a) => a.fileName).join("\n"),
5379
- className: "inline-flex shrink-0 items-center gap-0.5 rounded-md px-1.5 py-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
5380
- children: [
5381
- /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5", "aria-hidden": true }),
5382
- attachments.length > 1 ? /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium tabular-nums", children: attachments.length }) : null,
5383
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: attachments.length === 1 ? "Ver o anexo" : `Ver os ${attachments.length} anexos` })
5384
- ]
5385
- }
5386
- ),
5387
- !isPaid2 && invoice.status !== "cancelled" && /* @__PURE__ */ jsx(
5388
- "button",
5389
- {
5390
- onClick: (e) => {
5391
- e.stopPropagation();
5392
- setReceiptFor(mov);
5393
- receiptPicker.open();
5394
- },
5395
- title: t2("financial.quickTx.receiptCapture"),
5396
- "aria-label": t2("financial.quickTx.receiptCapture"),
5397
- 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",
5398
- children: /* @__PURE__ */ jsx(Camera, { className: "h-3 w-3" })
5399
- }
5400
- ),
5401
- !isPaid2 && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
5402
- "button",
5403
- {
5404
- onClick: (e) => {
5405
- e.stopPropagation();
5406
- setPayingMovement(mov);
5407
- },
5408
- 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",
5409
- children: [
5410
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
5411
- t2("financial.invoice.pay"),
5412
- " ",
5413
- remaining < mov.amount ? formatCurrency(remaining, currency) : ""
5414
- ]
5415
- }
5416
- ),
5417
- isPaid2 && instPayments.length > 0 && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
5418
- "button",
5419
- {
5420
- onClick: (e) => {
5421
- e.stopPropagation();
5422
- setReversing(instPayments[instPayments.length - 1]);
5423
- },
5424
- 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",
5425
- children: [
5426
- /* @__PURE__ */ jsx(Undo2, { className: "h-3 w-3" }),
5427
- t2("financial.invoice.reverse")
5428
- ]
5429
- }
5430
- ),
5431
- isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
5432
- "button",
5433
- {
5434
- onClick: (e) => {
5435
- e.stopPropagation();
5436
- setPayingMovement(mov);
5437
- },
5438
- 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",
5439
- children: [
5440
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
5441
- t2("financial.invoice.pay"),
5442
- " ",
5443
- formatCurrency(remaining, currency)
5444
- ]
5445
- }
5446
- ),
5447
- hasPaidDetails && /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": true })
5448
- ]
5449
- }
5450
- ) }, mov.id);
5451
- }) }),
5452
- settlements.unattributed.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t bg-warning-soft/40 px-4 py-2.5", children: [
5453
- /* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-foreground", children: t2("financial.invoice.unlinkedPayments") }),
5454
- /* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] text-muted-foreground", children: t2("financial.invoice.unlinkedPaymentsDesc") }),
5455
- settlements.unattributed.map((p) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
5456
- /* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
5457
- p.paymentDate && /* @__PURE__ */ jsxs(Fragment, { children: [
5458
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
5459
- /* @__PURE__ */ jsx("span", { children: p.paymentDate })
5460
- ] })
5461
- ] }, p.id))
5556
+ canChooseSource && /* @__PURE__ */ jsx("div", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
5557
+ InstallmentSourceFields,
5558
+ {
5559
+ direction,
5560
+ value: { payMethodTypeId: srcTypeId, payAccountId: srcAccountId },
5561
+ onChange: (next) => {
5562
+ void patchSource(mov, {
5563
+ payMethodTypeId: next.payMethodTypeId ?? null,
5564
+ payAccountId: next.payAccountId ?? null
5565
+ });
5566
+ }
5567
+ }
5568
+ ) }),
5569
+ paymentSummary && /* @__PURE__ */ jsx(
5570
+ "button",
5571
+ {
5572
+ type: "button",
5573
+ onClick: (e) => {
5574
+ e.stopPropagation();
5575
+ if (hasPaidDetails) setReceiptBill(mov);
5576
+ },
5577
+ title: paymentSummary,
5578
+ "aria-label": paymentSummary,
5579
+ className: "inline-flex shrink-0 items-center rounded-md px-1 py-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
5580
+ children: howCard ? /* @__PURE__ */ jsx(CardVisual, { lastFour: howCard.lastFour ?? void 0, color: howCard.color ?? void 0, brandId: howCard.cardBrandId ?? void 0 }) : HowIcon ? /* @__PURE__ */ jsx(HowIcon, { className: "h-4 w-4", "aria-hidden": true }) : null
5581
+ }
5582
+ ),
5583
+ !isPaid2 && invoice.status !== "cancelled" && /* @__PURE__ */ jsx(
5584
+ "button",
5585
+ {
5586
+ onClick: (e) => {
5587
+ e.stopPropagation();
5588
+ setReceiptFor(mov);
5589
+ receiptPicker.open();
5590
+ },
5591
+ title: t2("financial.quickTx.receiptCapture"),
5592
+ "aria-label": t2("financial.quickTx.receiptCapture"),
5593
+ 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",
5594
+ children: /* @__PURE__ */ jsx(Camera, { className: "h-3 w-3" })
5595
+ }
5596
+ ),
5597
+ !isPaid2 && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
5598
+ "button",
5599
+ {
5600
+ onClick: (e) => {
5601
+ e.stopPropagation();
5602
+ setPayingMovement(mov);
5603
+ },
5604
+ disabled: !!faltaParaPagar,
5605
+ title: faltaParaPagar ?? void 0,
5606
+ 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 disabled:opacity-40 disabled:pointer-events-none",
5607
+ children: [
5608
+ /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
5609
+ t2("financial.invoice.pay"),
5610
+ " ",
5611
+ remaining < mov.amount ? formatCurrency(remaining, currency) : ""
5612
+ ]
5613
+ }
5614
+ ),
5615
+ isPaid2 && instPayments.length > 0 && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
5616
+ "button",
5617
+ {
5618
+ onClick: (e) => {
5619
+ e.stopPropagation();
5620
+ setReversing(instPayments[instPayments.length - 1]);
5621
+ },
5622
+ 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",
5623
+ children: [
5624
+ /* @__PURE__ */ jsx(Undo2, { className: "h-3 w-3" }),
5625
+ t2("financial.invoice.reverse")
5626
+ ]
5627
+ }
5628
+ ),
5629
+ isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
5630
+ "button",
5631
+ {
5632
+ onClick: (e) => {
5633
+ e.stopPropagation();
5634
+ setPayingMovement(mov);
5635
+ },
5636
+ 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",
5637
+ children: [
5638
+ /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
5639
+ t2("financial.invoice.pay"),
5640
+ " ",
5641
+ formatCurrency(remaining, currency)
5642
+ ]
5643
+ }
5644
+ ),
5645
+ hasPaidDetails && /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": true })
5646
+ ]
5647
+ }
5648
+ ) }, mov.id);
5649
+ }) }),
5650
+ settlements.unattributed.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t bg-warning-soft/40 px-4 py-2.5", children: [
5651
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-foreground", children: t2("financial.invoice.unlinkedPayments") }),
5652
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] text-muted-foreground", children: t2("financial.invoice.unlinkedPaymentsDesc") }),
5653
+ settlements.unattributed.map((p) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
5654
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
5655
+ p.paymentDate && /* @__PURE__ */ jsxs(Fragment, { children: [
5656
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
5657
+ /* @__PURE__ */ jsx("span", { children: p.paymentDate })
5658
+ ] })
5659
+ ] }, p.id))
5660
+ ] })
5462
5661
  ] })
5463
- ] }) })
5662
+ ] })
5464
5663
  ),
5465
5664
  extraSections: sideBlocks
5466
5665
  }
@@ -5763,6 +5962,143 @@ function ReceivablesView({ intent, onNavigate, previousView, back }) {
5763
5962
  }
5764
5963
  );
5765
5964
  }
5965
+ function CashSessionReviewModal({ sessionId, open, onOpenChange, mode, onReviewed }) {
5966
+ const t2 = useTranslation();
5967
+ const { currency } = useFinancialConfig();
5968
+ const provider = useFinancialProvider();
5969
+ const { user } = useAuth();
5970
+ const me = user?.id ?? null;
5971
+ const [summary, setSummary] = useState(null);
5972
+ const [movements, setMovements] = useState(null);
5973
+ const [review, setReview] = useState(null);
5974
+ const [error, setError] = useState(null);
5975
+ const [busy, setBusy] = useState(false);
5976
+ useEffect(() => {
5977
+ if (!open) return;
5978
+ let cancelled = false;
5979
+ setSummary(null);
5980
+ setMovements(null);
5981
+ setReview(null);
5982
+ setError(null);
5983
+ async function load() {
5984
+ try {
5985
+ const [s, m] = await Promise.all([
5986
+ provider.getCashSessionSummary(sessionId),
5987
+ provider.getMovements({ cashSessionId: sessionId, status: "paid", pageSize: 200 })
5988
+ ]);
5989
+ if (cancelled) return;
5990
+ setSummary(s);
5991
+ setMovements(m.data);
5992
+ if (mode === "details") {
5993
+ const reviews = await provider.getCashSessionReviews?.() ?? [];
5994
+ if (cancelled) return;
5995
+ setReview(reviews.find((r) => r.sessionId === sessionId) ?? null);
5996
+ }
5997
+ } catch (e) {
5998
+ if (!cancelled) setError(e?.message ?? String(e));
5999
+ }
6000
+ }
6001
+ void load();
6002
+ return () => {
6003
+ cancelled = true;
6004
+ };
6005
+ }, [open, sessionId, mode, provider]);
6006
+ const session = summary?.session;
6007
+ const fechei = !!me && session?.closedByUserId === me;
6008
+ const loading = summary === null || movements === null;
6009
+ async function decide(verdict) {
6010
+ setBusy(true);
6011
+ setError(null);
6012
+ try {
6013
+ await provider.reviewCashSession?.(sessionId, verdict);
6014
+ onReviewed?.();
6015
+ onOpenChange(false);
6016
+ } catch (e) {
6017
+ setError(e?.message ?? String(e));
6018
+ } finally {
6019
+ setBusy(false);
6020
+ }
6021
+ }
6022
+ function handlePrint() {
6023
+ if (!session) return;
6024
+ const popup = window.open("", "_blank", "width=720,height=900");
6025
+ if (!popup) return;
6026
+ const title = session.bankAccountName ?? t2("financial.cash.title");
6027
+ const rows = (movements ?? []).map((m) => `<tr><td>${m.paymentDate ? new Date(m.paymentDate).toLocaleString() : "\u2014"}</td><td>${m.method ? t2(`financial.method.${m.method}`) : "\u2014"}</td><td style="text-align:right">${m.direction === "credit" ? "+" : "-"}${formatCurrency(m.paidAmount, currency)}</td></tr>`).join("");
6028
+ popup.document.write(`<html><head><title>${title}</title><style>body{font:12px sans-serif;padding:24px}table{width:100%;border-collapse:collapse;margin-top:16px}th,td{border-bottom:1px solid #ddd;padding:8px;text-align:left}h1{font-size:16px;margin-bottom:8px}p{margin:2px 0}</style></head><body><h1>${title}</h1><p>${t2("financial.cash.opened")}: ${new Date(session.openedAt).toLocaleString()}${session.openedByName ? ` ${t2("financial.cash.by")} ${session.openedByName}` : ""}</p>` + (session.closedAt ? `<p>${t2("financial.cash.closeLabel")}: ${new Date(session.closedAt).toLocaleString()}${session.closedByName ? ` ${t2("financial.cash.by")} ${session.closedByName}` : ""}</p>` : "") + `<p>${t2("financial.cash.openingBalance")}: ${formatCurrency(session.openingBalance, currency)}</p><p>${t2("financial.cash.finalValue")}: ${formatCurrency(session.closingBalance ?? 0, currency)}</p><p>${t2("financial.cash.difference")}: ${(session.difference ?? 0) >= 0 ? "+" : ""}${formatCurrency(session.difference ?? 0, currency)}</p><table><thead><tr><th>${t2("financial.cash.opened")}</th><th>${t2("financial.cash.method")}</th><th style="text-align:right">${t2("financial.cash.finalValue")}</th></tr></thead><tbody>${rows}</tbody></table><script>window.print();window.close()</script></body></html>`);
6029
+ popup.document.close();
6030
+ }
6031
+ return /* @__PURE__ */ jsx(Modal, { open, onOpenChange, children: /* @__PURE__ */ jsxs(ModalContent, { size: "lg", noPadding: true, className: "gap-0", children: [
6032
+ /* @__PURE__ */ jsxs(ModalHeader, { className: "border-b px-4 py-3", children: [
6033
+ /* @__PURE__ */ jsx(ModalTitle, { className: "text-sm", children: mode === "review" ? t2("financial.cash.reviewModalTitle") : t2("financial.cash.detailsModalTitle") }),
6034
+ /* @__PURE__ */ jsx(ModalDescription, { className: "text-xs", children: session?.bankAccountName ?? t2("financial.cash.title") })
6035
+ ] }),
6036
+ /* @__PURE__ */ jsxs(ModalBody, { className: "p-4 space-y-4", children: [
6037
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error }),
6038
+ loading ? /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-full" }, i)) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6039
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-muted/30 p-3 space-y-1", children: [
6040
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold text-muted-foreground uppercase mb-1", children: t2("financial.cash.summary") }),
6041
+ /* @__PURE__ */ jsx(SummaryRow, { label: t2("financial.cash.openingBalance"), value: formatCurrency(session.openingBalance, currency) }),
6042
+ /* @__PURE__ */ jsx(SummaryRow, { label: t2("financial.cash.finalValue"), value: formatCurrency(session.closingBalance ?? 0, currency), strong: true }),
6043
+ /* @__PURE__ */ jsx(
6044
+ SummaryRow,
6045
+ {
6046
+ label: t2("financial.cash.difference"),
6047
+ value: `${(session.difference ?? 0) >= 0 ? "+" : ""}${formatCurrency(session.difference ?? 0, currency)}`,
6048
+ tone: (session.difference ?? 0) > 0 ? "success" : (session.difference ?? 0) < 0 ? "destructive" : void 0
6049
+ }
6050
+ ),
6051
+ /* @__PURE__ */ jsx(SummaryRow, { label: t2("financial.cash.movementCount"), value: String(summary.movementCount) }),
6052
+ /* @__PURE__ */ jsx(SummaryRow, { label: t2("financial.cash.totalIn"), value: formatCurrency(summary.totalInflow, currency) }),
6053
+ /* @__PURE__ */ jsx(SummaryRow, { label: t2("financial.cash.totalOut"), value: formatCurrency(summary.totalOutflow, currency) })
6054
+ ] }),
6055
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border", children: [
6056
+ /* @__PURE__ */ jsx("p", { className: "px-3 py-2 text-[10px] font-semibold text-muted-foreground uppercase border-b", children: t2("financial.cash.transactions") }),
6057
+ (movements ?? []).length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: t2("financial.cash.noTransactions") }) : /* @__PURE__ */ jsx("div", { className: "max-h-64 overflow-y-auto divide-y", children: movements.map((m) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 text-xs", children: [
6058
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
6059
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: m.method ? t2(`financial.method.${m.method}`) : "\u2014" }),
6060
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: m.paymentDate ? new Date(m.paymentDate).toLocaleString() : "\u2014" })
6061
+ ] }),
6062
+ /* @__PURE__ */ jsxs("span", { className: `shrink-0 tabular-nums font-medium ${m.direction === "credit" ? "text-success" : "text-destructive"}`, children: [
6063
+ m.direction === "credit" ? "+" : "-",
6064
+ formatCurrency(m.paidAmount, currency)
6065
+ ] })
6066
+ ] }, m.id)) })
6067
+ ] }),
6068
+ mode === "details" && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
6069
+ review?.reviewStatus === "approved" ? t2("financial.cash.verdictApproved") : review?.reviewStatus === "rejected" ? t2("financial.cash.verdictRejected") : t2("financial.cash.verdictPending"),
6070
+ review?.reviewerName ? ` \xB7 ${t2("financial.cash.reviewedBy")} ${review.reviewerName}` : ""
6071
+ ] }),
6072
+ mode === "review" && fechei && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.cash.cannotReviewOwn") })
6073
+ ] })
6074
+ ] }),
6075
+ /* @__PURE__ */ jsxs(ModalFooter, { className: "border-t px-4 py-3", children: [
6076
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: handlePrint, disabled: loading, children: [
6077
+ /* @__PURE__ */ jsx(Printer, { className: "h-3.5 w-3.5" }),
6078
+ " ",
6079
+ t2("financial.cash.print")
6080
+ ] }),
6081
+ mode === "review" && !fechei && /* @__PURE__ */ jsxs(Fragment, { children: [
6082
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: busy || loading, onClick: () => void decide("rejected"), children: [
6083
+ /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" }),
6084
+ " ",
6085
+ t2("financial.cash.reject")
6086
+ ] }),
6087
+ /* @__PURE__ */ jsxs(Button, { size: "sm", disabled: busy || loading, onClick: () => void decide("approved"), children: [
6088
+ /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }),
6089
+ " ",
6090
+ t2("financial.cash.confirmAndApprove")
6091
+ ] })
6092
+ ] })
6093
+ ] })
6094
+ ] }) });
6095
+ }
6096
+ function SummaryRow({ label, value, strong, tone }) {
6097
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
6098
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
6099
+ /* @__PURE__ */ jsx("span", { className: `tabular-nums ${strong ? "text-sm font-bold" : "font-medium"} ${tone === "success" ? "text-success" : tone === "destructive" ? "text-destructive" : ""}`, children: value })
6100
+ ] });
6101
+ }
5766
6102
  var BRL_DENOMINATIONS = [
5767
6103
  { label: "R$ 200", value: 200, type: "bill" },
5768
6104
  { label: "R$ 100", value: 100, type: "bill" },
@@ -5847,34 +6183,10 @@ function CashOpeningPanel({
5847
6183
  setSelectedAccountId(cashAccounts[0].id);
5848
6184
  }
5849
6185
  }, [cashAccounts, selectedAccountId]);
5850
- const [mode, setMode] = useState("quick");
5851
- const [quickBalance, setQuickBalance] = useState(0);
5852
- const [counts, setCounts] = useState({});
5853
- const [notes, setNotes] = useState("");
5854
- const [showNotes, setShowNotes] = useState(false);
5855
- const [confirming, setConfirming] = useState(false);
6186
+ const [balance, setBalance] = useState(0);
5856
6187
  const [submitting, setSubmitting] = useState(false);
5857
6188
  const t2 = useTranslation();
5858
- const countedTotal = useMemo(() => {
5859
- return BRL_DENOMINATIONS.reduce((sum, d) => sum + (counts[d.value] ?? 0) * d.value, 0);
5860
- }, [counts]);
5861
- const effectiveBalance = mode === "count" ? countedTotal : quickBalance;
5862
- function resetForm() {
5863
- setQuickBalance(0);
5864
- setCounts({});
5865
- setNotes("");
5866
- setShowNotes(false);
5867
- setConfirming(false);
5868
- }
5869
- async function handleConfirm() {
5870
- setSubmitting(true);
5871
- try {
5872
- await onOpen(selectedAccountId, effectiveBalance, notes || void 0);
5873
- resetForm();
5874
- } finally {
5875
- setSubmitting(false);
5876
- }
5877
- }
6189
+ const { user } = useAuth();
5878
6190
  if (cashAccounts.length === 0) {
5879
6191
  return /* @__PURE__ */ jsx(
5880
6192
  GetStarted,
@@ -5892,150 +6204,62 @@ function CashOpeningPanel({
5892
6204
  }
5893
6205
  );
5894
6206
  }
5895
- if (confirming) {
5896
- return /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
5897
- /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-primary/20 bg-primary/5 p-4 space-y-3", children: [
5898
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: t2("financial.cash.confirmOpening") }),
5899
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 text-xs", children: [
5900
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t2("financial.cash.register") }),
5901
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: cashAccounts.find((a) => a.id === selectedAccountId)?.name }),
5902
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t2("financial.cash.openingBalance") }),
5903
- /* @__PURE__ */ jsx("span", { className: "font-bold text-base", children: formatCurrency(effectiveBalance, currency) }),
5904
- mode === "count" && /* @__PURE__ */ jsxs(Fragment, { children: [
5905
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t2("financial.cash.method") }),
5906
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: t2("financial.cash.denominationCount") })
5907
- ] }),
5908
- notes && /* @__PURE__ */ jsxs(Fragment, { children: [
5909
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t2("financial.cash.notes") }),
5910
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: notes })
5911
- ] })
5912
- ] })
5913
- ] }),
5914
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
5915
- /* @__PURE__ */ jsxs(
5916
- "button",
5917
- {
5918
- onClick: () => setConfirming(false),
5919
- className: "inline-flex items-center gap-1.5 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",
5920
- children: [
5921
- /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }),
5922
- " ",
5923
- t2("common.back")
5924
- ]
5925
- }
5926
- ),
5927
- /* @__PURE__ */ jsxs(
5928
- "button",
5929
- {
5930
- onClick: handleConfirm,
5931
- disabled: submitting,
5932
- 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",
5933
- children: [
5934
- /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
5935
- " ",
5936
- submitting ? t2("financial.cash.opening") : t2("financial.cash.confirmAndOpen")
5937
- ]
5938
- }
5939
- )
5940
- ] })
5941
- ] });
6207
+ async function handleOpen() {
6208
+ setSubmitting(true);
6209
+ try {
6210
+ await onOpen(selectedAccountId, balance);
6211
+ setBalance(0);
6212
+ } finally {
6213
+ setSubmitting(false);
6214
+ }
5942
6215
  }
5943
6216
  return /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
5944
- cashAccounts.length > 1 && /* @__PURE__ */ jsxs("div", { children: [
5945
- /* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium text-muted-foreground uppercase", children: t2("financial.cash.register") }),
5946
- /* @__PURE__ */ jsx(
5947
- "select",
5948
- {
5949
- value: selectedAccountId,
5950
- onChange: (e) => setSelectedAccountId(e.target.value),
5951
- className: "w-full mt-0.5 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2 py-1.5 text-xs",
5952
- children: cashAccounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
5953
- }
5954
- )
5955
- ] }),
5956
- /* @__PURE__ */ jsxs("div", { className: "flex gap-1 rounded-lg border p-0.5 w-fit", children: [
5957
- /* @__PURE__ */ jsx(
5958
- "button",
5959
- {
5960
- onClick: () => setMode("quick"),
5961
- className: `px-3 py-1 text-xs rounded-md font-medium transition-colors ${mode === "quick" ? "bg-primary text-primary-foreground" : "hover:bg-muted"}`,
5962
- children: t2("financial.cash.quickAmount")
5963
- }
5964
- ),
5965
- /* @__PURE__ */ jsxs(
5966
- "button",
5967
- {
5968
- onClick: () => setMode("count"),
5969
- className: `inline-flex items-center gap-1 px-3 py-1 text-xs rounded-md font-medium transition-colors ${mode === "count" ? "bg-primary text-primary-foreground" : "hover:bg-muted"}`,
5970
- children: [
5971
- /* @__PURE__ */ jsx(Calculator, { className: "h-3 w-3" }),
5972
- " ",
5973
- t2("financial.cash.countBillsCoins")
5974
- ]
5975
- }
5976
- )
5977
- ] }),
5978
- mode === "quick" && /* @__PURE__ */ jsx(
5979
- CurrencyInput,
5980
- {
5981
- value: quickBalance,
5982
- onChange: setQuickBalance,
5983
- symbol: currency.symbol,
5984
- locale: currency.locale,
5985
- currencyCode: currency.code,
5986
- label: t2("financial.cash.openingBalance")
5987
- }
5988
- ),
5989
- mode === "count" && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
6217
+ /* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2", children: [
6218
+ /* @__PURE__ */ jsxs("div", { children: [
6219
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium text-muted-foreground uppercase", children: t2("financial.cash.user") }),
6220
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm font-medium truncate", children: user?.email ?? "\u2014" })
6221
+ ] }),
6222
+ /* @__PURE__ */ jsxs("div", { children: [
6223
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium text-muted-foreground uppercase", children: t2("financial.cash.openingDate") }),
6224
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm font-medium capitalize", children: (/* @__PURE__ */ new Date()).toLocaleDateString("pt-BR", { weekday: "long", day: "2-digit", month: "long", year: "numeric" }) })
6225
+ ] })
6226
+ ] }) }),
6227
+ /* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2", children: [
5990
6228
  /* @__PURE__ */ jsx(
5991
- DenominationCounter,
6229
+ CurrencyInput,
5992
6230
  {
5993
- denominations: BRL_DENOMINATIONS,
5994
- counts,
5995
- onChange: setCounts,
5996
- currency
6231
+ value: balance,
6232
+ onChange: setBalance,
6233
+ symbol: currency.symbol,
6234
+ locale: currency.locale,
6235
+ currencyCode: currency.code,
6236
+ label: t2("financial.cash.openingBalance")
5997
6237
  }
5998
6238
  ),
5999
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-2 border-t", children: [
6000
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground uppercase", children: t2("financial.cash.countedTotal") }),
6001
- /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: formatCurrency(countedTotal, currency) })
6239
+ /* @__PURE__ */ jsxs("div", { children: [
6240
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium text-muted-foreground uppercase", children: t2("financial.cash.accountLabel") }),
6241
+ /* @__PURE__ */ jsx(
6242
+ "select",
6243
+ {
6244
+ value: selectedAccountId,
6245
+ onChange: (e) => setSelectedAccountId(e.target.value),
6246
+ className: "w-full mt-0.5 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2 py-1.5 text-xs",
6247
+ children: cashAccounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
6248
+ }
6249
+ )
6002
6250
  ] })
6003
- ] }),
6004
- /* @__PURE__ */ jsx("div", { children: !showNotes ? /* @__PURE__ */ jsxs(
6005
- "button",
6006
- {
6007
- onClick: () => setShowNotes(true),
6008
- className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors",
6009
- children: [
6010
- /* @__PURE__ */ jsx(StickyNote, { className: "h-3 w-3" }),
6011
- " ",
6012
- t2("financial.cash.addNotes")
6013
- ]
6014
- }
6015
- ) : /* @__PURE__ */ jsxs("div", { children: [
6016
- /* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium text-muted-foreground uppercase", children: t2("financial.cash.notes") }),
6017
- /* @__PURE__ */ jsx(
6018
- "textarea",
6019
- {
6020
- value: notes,
6021
- onChange: (e) => setNotes(e.target.value),
6022
- rows: 2,
6023
- placeholder: t2("financial.cash.openingObservations"),
6024
- className: "w-full mt-0.5 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2 py-1.5 text-xs resize-none"
6025
- }
6026
- )
6027
6251
  ] }) }),
6028
6252
  /* @__PURE__ */ jsxs(
6029
6253
  "button",
6030
6254
  {
6031
6255
  "data-tour": "financial.cash-open",
6032
- onClick: () => setConfirming(true),
6033
- disabled: effectiveBalance < 0,
6256
+ onClick: () => void handleOpen(),
6257
+ disabled: submitting || balance < 0,
6034
6258
  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",
6035
6259
  children: [
6036
- /* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }),
6260
+ /* @__PURE__ */ jsx(Inbox, { className: "h-3 w-3" }),
6037
6261
  " ",
6038
- t2("financial.cash.openSession")
6262
+ submitting ? t2("financial.cash.opening") : t2("financial.quickActions.openCashRegister")
6039
6263
  ]
6040
6264
  }
6041
6265
  )
@@ -6166,48 +6390,27 @@ function CashClosingPanel({
6166
6390
  )
6167
6391
  ] });
6168
6392
  }
6169
- function ReviewQueue({ variant }) {
6393
+ function ReviewQueue({
6394
+ variant,
6395
+ rows,
6396
+ error,
6397
+ onReload,
6398
+ showHeader = true
6399
+ }) {
6170
6400
  const t2 = useTranslation();
6171
6401
  const { currency } = useFinancialConfig();
6172
- const provider = useFinancialProvider();
6173
- const [rows, setRows] = useState(null);
6174
6402
  const { user } = useAuth();
6175
6403
  const me = user?.id ?? null;
6176
- const [busy, setBusy] = useState(null);
6177
- const [error, setError] = useState(null);
6178
- const load = React22__default.useCallback(async () => {
6179
- try {
6180
- setError(null);
6181
- setRows(await provider.getCashSessionReviews?.() ?? []);
6182
- } catch (e) {
6183
- setError(e?.message ?? String(e));
6184
- setRows([]);
6185
- }
6186
- }, [provider]);
6187
- useEffect(() => {
6188
- void load();
6189
- }, [load]);
6404
+ const [reviewing, setReviewing] = useState(null);
6190
6405
  const pendentes = (rows ?? []).filter((r) => r.reviewStatus === "pending");
6191
- async function review(r, verdict) {
6192
- setBusy(r.sessionId);
6193
- setError(null);
6194
- try {
6195
- await provider.reviewCashSession?.(r.sessionId, verdict);
6196
- await load();
6197
- } catch (e) {
6198
- setError(e?.message ?? String(e));
6199
- } finally {
6200
- setBusy(null);
6201
- }
6202
- }
6203
6406
  const sectionClass = variant === "panel" ? "" : "rounded-card border bg-card shadow-sm";
6204
6407
  return /* @__PURE__ */ jsxs("div", { className: sectionClass, children: [
6205
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b px-4 py-3", children: [
6408
+ showHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b px-4 py-3", children: [
6206
6409
  /* @__PURE__ */ jsx(ClipboardCheck, { className: "h-4 w-4 text-info" }),
6207
6410
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.reviewQueue") }),
6208
6411
  pendentes.length > 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-info/10 px-2 py-0.5 text-[10px] font-semibold text-info", children: pendentes.length })
6209
6412
  ] }),
6210
- /* @__PURE__ */ jsx("p", { className: "px-4 pt-3 text-[11px] text-muted-foreground", children: "Quem abre e fecha o caixa n\xE3o \xE9 quem confere. Um fechamento fica aqui at\xE9 algu\xE9m da ger\xEAncia ou do financeiro aprovar." }),
6413
+ showHeader && /* @__PURE__ */ jsx("p", { className: "px-4 pt-3 text-[11px] text-muted-foreground", children: "Quem abre e fecha o caixa n\xE3o \xE9 quem confere. Um fechamento fica aqui at\xE9 algu\xE9m da ger\xEAncia ou do financeiro aprovar." }),
6211
6414
  error && /* @__PURE__ */ jsx("p", { className: "px-4 pt-2 text-xs text-destructive", children: error }),
6212
6415
  rows === null ? /* @__PURE__ */ jsx("div", { className: "space-y-2 p-4", children: [0, 1].map((i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
6213
6416
  /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-48" }),
@@ -6233,56 +6436,64 @@ function ReviewQueue({ variant }) {
6233
6436
  fechei ? (
6234
6437
  // Estado que impede não oferece a ação — e diz por quê.
6235
6438
  /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.cash.cannotReviewOwn") })
6236
- ) : /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
6237
- /* @__PURE__ */ jsxs(
6238
- Button,
6239
- {
6240
- variant: "outline",
6241
- size: "sm",
6242
- disabled: busy === r.sessionId,
6243
- onClick: () => void review(r, "rejected"),
6244
- children: [
6245
- /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" }),
6246
- " ",
6247
- t2("financial.cash.reject")
6248
- ]
6249
- }
6250
- ),
6251
- /* @__PURE__ */ jsxs(
6252
- Button,
6253
- {
6254
- size: "sm",
6255
- disabled: busy === r.sessionId,
6256
- onClick: () => void review(r, "approved"),
6257
- children: [
6258
- /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }),
6259
- " ",
6260
- t2("financial.cash.approve")
6261
- ]
6262
- }
6263
- )
6439
+ ) : /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setReviewing(r.sessionId), children: [
6440
+ /* @__PURE__ */ jsx(ClipboardCheck, { className: "h-3.5 w-3.5" }),
6441
+ " ",
6442
+ t2("financial.cash.review")
6264
6443
  ] })
6265
6444
  ] }, r.sessionId);
6266
- }) })
6445
+ }) }),
6446
+ reviewing && /* @__PURE__ */ jsx(
6447
+ CashSessionReviewModal,
6448
+ {
6449
+ sessionId: reviewing,
6450
+ mode: "review",
6451
+ open: true,
6452
+ onOpenChange: (o) => {
6453
+ if (!o) setReviewing(null);
6454
+ },
6455
+ onReviewed: () => {
6456
+ setReviewing(null);
6457
+ onReload();
6458
+ }
6459
+ }
6460
+ )
6267
6461
  ] });
6268
6462
  }
6269
6463
  function CashRegistersView({ variant = "page" } = {}) {
6270
6464
  const t2 = useTranslation();
6271
6465
  const { currency, labels } = useFinancialConfig();
6466
+ const provider = useFinancialProvider();
6272
6467
  const bankAccounts = useFinancialStore((s) => s.bankAccounts);
6273
6468
  const cashSessions = useFinancialStore((s) => s.cashSessions);
6274
6469
  const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
6275
6470
  const fetchCashSessions = useFinancialStore((s) => s.fetchCashSessions);
6276
6471
  const openCashSession = useFinancialStore((s) => s.openCashSession);
6277
6472
  const closeCashSession = useFinancialStore((s) => s.closeCashSession);
6473
+ const setCashSessionModalOpen = useFinancialStore((s) => s.setCashSessionModalOpen);
6278
6474
  const [expandedClosing, setExpandedClosing] = useState(null);
6475
+ const [viewingDetails, setViewingDetails] = useState(null);
6476
+ const [tab, setTab] = useState("open");
6477
+ const [reviewRows, setReviewRows] = useState(null);
6478
+ const [reviewError, setReviewError] = useState(null);
6479
+ const loadReviews = React23__default.useCallback(async () => {
6480
+ try {
6481
+ setReviewError(null);
6482
+ setReviewRows(await provider.getCashSessionReviews?.() ?? []);
6483
+ } catch (e) {
6484
+ setReviewError(e?.message ?? String(e));
6485
+ setReviewRows([]);
6486
+ }
6487
+ }, [provider]);
6279
6488
  useEffect(() => {
6280
6489
  fetchBankAccounts();
6281
6490
  fetchCashSessions();
6491
+ void loadReviews();
6282
6492
  }, []);
6283
6493
  const cashAccounts = bankAccounts.filter((a) => a.accountType === "cash_physical");
6284
6494
  const openSessions = cashSessions.filter((s) => s.status === "open");
6285
6495
  const closedSessions = cashSessions.filter((s) => s.status === "closed");
6496
+ const pendingReviews = (reviewRows ?? []).filter((r) => r.reviewStatus === "pending");
6286
6497
  const inPanel = variant === "panel";
6287
6498
  const sectionClass = inPanel ? "" : "rounded-card border bg-card shadow-sm";
6288
6499
  async function handleOpen(accountId, balance, notes) {
@@ -6292,110 +6503,153 @@ function CashRegistersView({ variant = "page" } = {}) {
6292
6503
  await closeCashSession({ sessionId, closingBalance: balance, notes });
6293
6504
  setExpandedClosing(null);
6294
6505
  }
6295
- return /* @__PURE__ */ jsxs("div", { className: inPanel ? "" : "space-y-6", children: [
6296
- !inPanel && /* @__PURE__ */ jsx(
6297
- SubpageHeader,
6298
- {
6299
- title: labels.cashRegisters ?? t2("financial.cash.title"),
6300
- subtitle: t2("financial.cash.subtitle")
6301
- }
6302
- ),
6303
- /* @__PURE__ */ jsxs("div", { className: sectionClass, children: [
6304
- /* @__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: [
6305
- /* @__PURE__ */ jsx(Landmark, { className: "h-4 w-4 text-info" }),
6306
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.openSessions") }),
6307
- openSessions.length > 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-info/10 text-info px-2 py-0.5 text-[10px] font-semibold", children: openSessions.length })
6308
- ] }) }),
6309
- openSessions.length === 0 ? /* @__PURE__ */ jsx(CashOpeningPanel, { cashAccounts, currency, onOpen: handleOpen }) : /* @__PURE__ */ jsx("div", { className: "divide-y", children: openSessions.map((session) => /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-3", children: [
6310
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
6311
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
6312
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground truncate", children: session.bankAccountName ?? t2("financial.cash.title") }),
6313
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
6314
- t2("financial.cash.opened"),
6315
- " ",
6316
- new Date(session.openedAt).toLocaleString(),
6317
- session.openedByName ? ` ${t2("financial.cash.by")} ${session.openedByName}` : ""
6318
- ] })
6319
- ] }),
6320
- /* @__PURE__ */ jsxs("div", { className: "text-right shrink-0", children: [
6321
- /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground uppercase", children: t2("financial.cash.openingBalance") }),
6322
- /* @__PURE__ */ jsx("p", { className: "text-lg font-bold tabular-nums text-foreground", children: formatCurrency(session.openingBalance, currency) })
6323
- ] })
6324
- ] }),
6325
- expandedClosing === session.id ? /* @__PURE__ */ jsx(CashClosingPanel, { session, currency, onClose: handleClose }) : /* @__PURE__ */ jsxs(
6326
- "button",
6327
- {
6328
- "data-tour": "financial.cash-close",
6329
- onClick: () => setExpandedClosing(session.id),
6330
- className: "inline-flex items-center gap-1.5 text-xs text-destructive hover:text-destructive font-medium transition-colors",
6331
- children: [
6332
- /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" }),
6333
- " ",
6334
- t2("financial.cash.closeThisSession")
6335
- ]
6336
- }
6337
- )
6338
- ] }, session.id)) })
6506
+ const openSessionsList = /* @__PURE__ */ jsx("div", { className: "divide-y", children: openSessions.map((session) => /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-3", children: [
6507
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
6508
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
6509
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground truncate", children: session.bankAccountName ?? t2("financial.cash.title") }),
6510
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
6511
+ t2("financial.cash.opened"),
6512
+ " ",
6513
+ new Date(session.openedAt).toLocaleString(),
6514
+ session.openedByName ? ` ${t2("financial.cash.by")} ${session.openedByName}` : ""
6515
+ ] })
6516
+ ] }),
6517
+ /* @__PURE__ */ jsxs("div", { className: "text-right shrink-0", children: [
6518
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground uppercase", children: t2("financial.cash.openingBalance") }),
6519
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-bold tabular-nums text-foreground", children: formatCurrency(session.openingBalance, currency) })
6520
+ ] })
6339
6521
  ] }),
6340
- openSessions.length > 0 && cashAccounts.length > openSessions.length && /* @__PURE__ */ jsx("div", { className: inPanel ? "border-t" : "", children: /* @__PURE__ */ jsx(OpenAnotherCollapsible, { cashAccounts, openSessions, currency, onOpen: handleOpen, variant }) }),
6341
- /* @__PURE__ */ jsx(ReviewQueue, { variant }),
6342
- inPanel ? /* @__PURE__ */ jsxs(
6343
- "a",
6522
+ expandedClosing === session.id ? /* @__PURE__ */ jsx(CashClosingPanel, { session, currency, onClose: handleClose }) : /* @__PURE__ */ jsxs(
6523
+ "button",
6344
6524
  {
6345
- href: "#/financial/cash/registers",
6346
- className: "flex items-center gap-2 border-t px-4 py-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground",
6525
+ "data-tour": "financial.cash-close",
6526
+ onClick: () => setExpandedClosing(session.id),
6527
+ className: "inline-flex items-center gap-1.5 text-xs text-destructive hover:text-destructive font-medium transition-colors",
6347
6528
  children: [
6348
- /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5" }),
6349
- t2("financial.cash.sessionHistory"),
6350
- closedSessions.length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
6351
- "(",
6352
- closedSessions.length,
6353
- ")"
6354
- ] })
6529
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" }),
6530
+ " ",
6531
+ t2("financial.cash.closeThisSession")
6355
6532
  ]
6356
6533
  }
6357
- ) : /* @__PURE__ */ jsxs("div", { className: sectionClass, children: [
6358
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
6359
- /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-muted-foreground" }),
6360
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.sessionHistory") })
6534
+ )
6535
+ ] }, session.id)) });
6536
+ const closedSessionsList = /* @__PURE__ */ jsx("div", { className: "divide-y", children: closedSessions.map((session) => /* @__PURE__ */ jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
6537
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
6538
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: session.bankAccountName ?? t2("financial.cash.title") }),
6539
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
6540
+ new Date(session.openedAt).toLocaleDateString(),
6541
+ " \u2014 ",
6542
+ session.closedAt ? new Date(session.closedAt).toLocaleDateString() : ""
6361
6543
  ] }),
6362
- closedSessions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-sm text-muted-foreground", children: t2("financial.cash.noClosedSessions") }) : /* @__PURE__ */ jsx("div", { className: "divide-y", children: closedSessions.map((session) => /* @__PURE__ */ jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
6363
- /* @__PURE__ */ jsxs("div", { children: [
6364
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: session.bankAccountName ?? t2("financial.cash.title") }),
6365
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
6366
- new Date(session.openedAt).toLocaleDateString(),
6367
- " \u2014 ",
6368
- session.closedAt ? new Date(session.closedAt).toLocaleDateString() : ""
6544
+ session.openedByName && /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
6545
+ t2("financial.cash.by"),
6546
+ " ",
6547
+ session.openedByName
6548
+ ] })
6549
+ ] }),
6550
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-start gap-3", children: [
6551
+ /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
6552
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
6553
+ /* @__PURE__ */ jsxs("span", { children: [
6554
+ t2("financial.cash.openLabel"),
6555
+ ": ",
6556
+ formatCurrency(session.openingBalance, currency)
6369
6557
  ] }),
6370
- session.openedByName && /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
6371
- t2("financial.cash.by"),
6372
- " ",
6373
- session.openedByName
6558
+ /* @__PURE__ */ jsxs("span", { children: [
6559
+ t2("financial.cash.closeLabel"),
6560
+ ": ",
6561
+ formatCurrency(session.closingBalance ?? 0, currency)
6374
6562
  ] })
6375
6563
  ] }),
6376
- /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
6377
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
6378
- /* @__PURE__ */ jsxs("span", { children: [
6379
- t2("financial.cash.openLabel"),
6380
- ": ",
6381
- formatCurrency(session.openingBalance, currency)
6382
- ] }),
6383
- /* @__PURE__ */ jsxs("span", { children: [
6384
- t2("financial.cash.closeLabel"),
6385
- ": ",
6386
- formatCurrency(session.closingBalance ?? 0, currency)
6387
- ] })
6388
- ] }),
6389
- session.difference !== void 0 && session.difference !== 0 && /* @__PURE__ */ jsxs("p", { className: `text-xs font-semibold tabular-nums mt-0.5 ${session.difference > 0 ? "text-success" : "text-destructive"}`, children: [
6390
- t2("financial.cash.diff"),
6391
- ": ",
6392
- session.difference >= 0 ? "+" : "",
6393
- formatCurrency(session.difference, currency)
6394
- ] }),
6395
- session.notes && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground mt-0.5 max-w-[200px] truncate", title: session.notes, children: session.notes })
6396
- ] })
6397
- ] }) }, session.id)) })
6564
+ session.difference !== void 0 && session.difference !== 0 && /* @__PURE__ */ jsxs("p", { className: `text-xs font-semibold tabular-nums mt-0.5 ${session.difference > 0 ? "text-success" : "text-destructive"}`, children: [
6565
+ t2("financial.cash.diff"),
6566
+ ": ",
6567
+ session.difference >= 0 ? "+" : "",
6568
+ formatCurrency(session.difference, currency)
6569
+ ] }),
6570
+ session.notes && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground mt-0.5 max-w-[200px] truncate", title: session.notes, children: session.notes })
6571
+ ] }),
6572
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setViewingDetails(session.id), children: [
6573
+ /* @__PURE__ */ jsx(Eye, { className: "h-3.5 w-3.5" }),
6574
+ " ",
6575
+ t2("financial.cash.details")
6576
+ ] })
6398
6577
  ] })
6578
+ ] }) }, session.id)) });
6579
+ return /* @__PURE__ */ jsxs("div", { className: inPanel ? "" : "space-y-4", children: [
6580
+ !inPanel && /* @__PURE__ */ jsx(
6581
+ SubpageHeader,
6582
+ {
6583
+ title: labels.cashRegisters ?? t2("financial.cash.title"),
6584
+ subtitle: t2("financial.cash.subtitle"),
6585
+ actions: cashAccounts.length > openSessions.length ? (
6586
+ // A ABERTURA mora no diálogo da casca, não na página: este botão
6587
+ // abre O MESMO modal do ícone do cabeçalho, em vez de desenhar o
6588
+ // formulário de novo aqui dentro.
6589
+ /* @__PURE__ */ jsxs(Button, { "data-tour": "financial.cash-open", size: "sm", onClick: () => setCashSessionModalOpen(true), children: [
6590
+ /* @__PURE__ */ jsx(Inbox, { className: "h-3.5 w-3.5" }),
6591
+ " ",
6592
+ t2("financial.quickActions.openCashRegister")
6593
+ ] })
6594
+ ) : void 0
6595
+ }
6596
+ ),
6597
+ inPanel ? /* @__PURE__ */ jsxs(Fragment, { children: [
6598
+ /* @__PURE__ */ jsxs("div", { className: sectionClass, children: [
6599
+ /* @__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: [
6600
+ /* @__PURE__ */ jsx(Landmark, { className: "h-4 w-4 text-info" }),
6601
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.openSessions") }),
6602
+ openSessions.length > 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-info/10 text-info px-2 py-0.5 text-[10px] font-semibold", children: openSessions.length })
6603
+ ] }) }),
6604
+ openSessions.length === 0 ? /* @__PURE__ */ jsx(CashOpeningPanel, { cashAccounts, currency, onOpen: handleOpen }) : openSessionsList
6605
+ ] }),
6606
+ openSessions.length > 0 && cashAccounts.length > openSessions.length && /* @__PURE__ */ jsx("div", { className: "border-t", children: /* @__PURE__ */ jsx(OpenAnotherCollapsible, { cashAccounts, openSessions, currency, onOpen: handleOpen, variant }) }),
6607
+ /* @__PURE__ */ jsx(ReviewQueue, { variant, rows: reviewRows, error: reviewError, onReload: loadReviews }),
6608
+ /* @__PURE__ */ jsxs(
6609
+ "a",
6610
+ {
6611
+ href: "#/financial/cash/registers",
6612
+ className: "flex items-center gap-2 border-t px-4 py-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground",
6613
+ children: [
6614
+ /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5" }),
6615
+ t2("financial.cash.sessionHistory"),
6616
+ closedSessions.length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
6617
+ "(",
6618
+ closedSessions.length,
6619
+ ")"
6620
+ ] })
6621
+ ]
6622
+ }
6623
+ )
6624
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6625
+ /* @__PURE__ */ jsx(
6626
+ PageTabs,
6627
+ {
6628
+ "aria-label": labels.cashRegisters ?? t2("financial.cash.title"),
6629
+ value: tab,
6630
+ onChange: setTab,
6631
+ tabs: [
6632
+ { value: "open", label: t2("financial.cash.tabOpen"), icon: Landmark, count: openSessions.length },
6633
+ { value: "closed", label: t2("financial.cash.tabClosed"), icon: Clock, count: closedSessions.length },
6634
+ { value: "review", label: t2("financial.cash.tabReview"), icon: ClipboardCheck, count: pendingReviews.length }
6635
+ ]
6636
+ }
6637
+ ),
6638
+ tab === "open" && /* @__PURE__ */ jsx("div", { className: sectionClass, children: openSessions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-4 py-8 text-center text-sm text-muted-foreground", children: t2("financial.cash.noOpenSessions") }) : openSessionsList }),
6639
+ tab === "closed" && /* @__PURE__ */ jsx("div", { className: sectionClass, children: closedSessions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-sm text-muted-foreground", children: t2("financial.cash.noClosedSessions") }) : closedSessionsList }),
6640
+ tab === "review" && /* @__PURE__ */ jsx(ReviewQueue, { variant, rows: reviewRows, error: reviewError, onReload: loadReviews, showHeader: false })
6641
+ ] }),
6642
+ viewingDetails && /* @__PURE__ */ jsx(
6643
+ CashSessionReviewModal,
6644
+ {
6645
+ sessionId: viewingDetails,
6646
+ mode: "details",
6647
+ open: true,
6648
+ onOpenChange: (o) => {
6649
+ if (!o) setViewingDetails(null);
6650
+ }
6651
+ }
6652
+ )
6399
6653
  ] });
6400
6654
  }
6401
6655
  function OpenAnotherCollapsible({
@@ -6822,7 +7076,7 @@ function StatementsView({ onNavigate }) {
6822
7076
  "extrato"
6823
7077
  );
6824
7078
  };
6825
- const headerActions = React22__default.useMemo(() => [{
7079
+ const headerActions = React23__default.useMemo(() => [{
6826
7080
  id: "new-transfer",
6827
7081
  label: t2("financial.statements.newTransfer"),
6828
7082
  icon: ArrowRightLeft,
@@ -7018,15 +7272,15 @@ function CommissionsView() {
7018
7272
  const fetchCommissions = useFinancialStore((s) => s.fetchCommissions);
7019
7273
  const generateCommissions = useFinancialStore((s) => s.generateCommissions);
7020
7274
  const provider = useFinancialProvider();
7021
- const [from, setFrom] = React22__default.useState(firstOfMonth());
7022
- const [to, setTo] = React22__default.useState(today2());
7023
- const [preset, setPreset] = React22__default.useState("this_month");
7024
- const [running, setRunning] = React22__default.useState(false);
7025
- const [lastRun, setLastRun] = React22__default.useState(null);
7026
- const [generation, setGeneration] = React22__default.useState(null);
7027
- const [busy, setBusy] = React22__default.useState(null);
7028
- const [error, setError] = React22__default.useState(null);
7029
- const loadGeneration = React22__default.useCallback(async () => {
7275
+ const [from, setFrom] = React23__default.useState(firstOfMonth());
7276
+ const [to, setTo] = React23__default.useState(today2());
7277
+ const [preset, setPreset] = React23__default.useState("this_month");
7278
+ const [running, setRunning] = React23__default.useState(false);
7279
+ const [lastRun, setLastRun] = React23__default.useState(null);
7280
+ const [generation, setGeneration] = React23__default.useState(null);
7281
+ const [busy, setBusy] = React23__default.useState(null);
7282
+ const [error, setError] = React23__default.useState(null);
7283
+ const loadGeneration = React23__default.useCallback(async () => {
7030
7284
  try {
7031
7285
  const gens = await provider.getCommissionGenerations?.() ?? [];
7032
7286
  const doPeriodo = gens.filter((g) => g.periodStart === from && g.periodEnd === to);
@@ -7034,11 +7288,11 @@ function CommissionsView() {
7034
7288
  } catch {
7035
7289
  }
7036
7290
  }, [provider, from, to]);
7037
- React22__default.useEffect(() => {
7291
+ React23__default.useEffect(() => {
7038
7292
  void fetchCommissions({ periodStart: from, periodEnd: to });
7039
7293
  void loadGeneration();
7040
7294
  }, [fetchCommissions, loadGeneration, from, to]);
7041
- const byPerson = React22__default.useMemo(() => {
7295
+ const byPerson = React23__default.useMemo(() => {
7042
7296
  const map = /* @__PURE__ */ new Map();
7043
7297
  for (const c of commissions2) {
7044
7298
  const key = c.personId;
@@ -7091,7 +7345,7 @@ function CommissionsView() {
7091
7345
  setBusy(null);
7092
7346
  }
7093
7347
  }
7094
- const headerActions = React22__default.useMemo(() => [{
7348
+ const headerActions = React23__default.useMemo(() => [{
7095
7349
  id: "commission-rules",
7096
7350
  label: "Configurar regras de comiss\xE3o",
7097
7351
  icon: Settings,
@@ -7539,13 +7793,13 @@ function money2(v) {
7539
7793
  function GoalsView() {
7540
7794
  const { labels } = useFinancialConfig();
7541
7795
  const provider = useFinancialProvider();
7542
- const [units, setUnits] = React22__default.useState([]);
7543
- const [unitId, setUnitId] = React22__default.useState("");
7544
- const [year, setYear] = React22__default.useState((/* @__PURE__ */ new Date()).getFullYear());
7545
- const [rows, setRows] = React22__default.useState(null);
7546
- const [editing, setEditing] = React22__default.useState(null);
7547
- const [error, setError] = React22__default.useState(null);
7548
- React22__default.useEffect(() => {
7796
+ const [units, setUnits] = React23__default.useState([]);
7797
+ const [unitId, setUnitId] = React23__default.useState("");
7798
+ const [year, setYear] = React23__default.useState((/* @__PURE__ */ new Date()).getFullYear());
7799
+ const [rows, setRows] = React23__default.useState(null);
7800
+ const [editing, setEditing] = React23__default.useState(null);
7801
+ const [error, setError] = React23__default.useState(null);
7802
+ React23__default.useEffect(() => {
7549
7803
  void (async () => {
7550
7804
  const cat = await provider.getCommissionCatalogs?.();
7551
7805
  const us = cat?.units ?? [];
@@ -7553,7 +7807,7 @@ function GoalsView() {
7553
7807
  setUnitId((cur) => cur || us[0]?.id || "");
7554
7808
  })();
7555
7809
  }, [provider]);
7556
- const load = React22__default.useCallback(async () => {
7810
+ const load = React23__default.useCallback(async () => {
7557
7811
  if (!unitId) return;
7558
7812
  setRows(null);
7559
7813
  try {
@@ -7564,7 +7818,7 @@ function GoalsView() {
7564
7818
  setRows([]);
7565
7819
  }
7566
7820
  }, [provider, unitId, year]);
7567
- React22__default.useEffect(() => {
7821
+ React23__default.useEffect(() => {
7568
7822
  void load();
7569
7823
  }, [load]);
7570
7824
  const totalRealizado = (rows ?? []).reduce((s, r) => s + r.achievedAmount, 0);
@@ -7695,11 +7949,11 @@ function GoalDialog({
7695
7949
  onSaved
7696
7950
  }) {
7697
7951
  const provider = useFinancialProvider();
7698
- const [target, setTarget] = React22__default.useState(String(goal.targetAmount || ""));
7699
- const [sup, setSup] = React22__default.useState(String(goal.superAmount || ""));
7700
- const [hyper, setHyper] = React22__default.useState(String(goal.hyperAmount || ""));
7701
- const [saving, setSaving] = React22__default.useState(false);
7702
- const [error, setError] = React22__default.useState(null);
7952
+ const [target, setTarget] = React23__default.useState(String(goal.targetAmount || ""));
7953
+ const [sup, setSup] = React23__default.useState(String(goal.superAmount || ""));
7954
+ const [hyper, setHyper] = React23__default.useState(String(goal.hyperAmount || ""));
7955
+ const [saving, setSaving] = React23__default.useState(false);
7956
+ const [error, setError] = React23__default.useState(null);
7703
7957
  const t2 = Number(target) || 0;
7704
7958
  const s = Number(sup) || 0;
7705
7959
  const h = Number(hyper) || 0;
@@ -7790,21 +8044,21 @@ function GiftCardsView() {
7790
8044
  const provider = useFinancialProvider();
7791
8045
  const { currency } = useFinancialConfig();
7792
8046
  const can = usePermissionOptional();
7793
- const [enabled, setEnabled] = React22__default.useState(null);
7794
- const [cards, setCards] = React22__default.useState(null);
7795
- const [error, setError] = React22__default.useState(null);
7796
- const [issuing, setIssuing] = React22__default.useState(false);
7797
- const [issued, setIssued] = React22__default.useState(null);
7798
- const [activating, setActivating] = React22__default.useState(null);
7799
- const [search, setSearch] = React22__default.useState("");
7800
- const [state, setState] = React22__default.useState("");
7801
- const [minValue, setMinValue] = React22__default.useState("");
7802
- const [maxValue, setMaxValue] = React22__default.useState("");
7803
- const [from, setFrom] = React22__default.useState("");
7804
- const [to, setTo] = React22__default.useState("");
7805
- const money3 = React22__default.useCallback((v) => formatCurrency(v, currency), [currency]);
7806
- const seq = React22__default.useRef(0);
7807
- const load = React22__default.useCallback(async () => {
8047
+ const [enabled, setEnabled] = React23__default.useState(null);
8048
+ const [cards, setCards] = React23__default.useState(null);
8049
+ const [error, setError] = React23__default.useState(null);
8050
+ const [issuing, setIssuing] = React23__default.useState(false);
8051
+ const [issued, setIssued] = React23__default.useState(null);
8052
+ const [activating, setActivating] = React23__default.useState(null);
8053
+ const [search, setSearch] = React23__default.useState("");
8054
+ const [state, setState] = React23__default.useState("");
8055
+ const [minValue, setMinValue] = React23__default.useState("");
8056
+ const [maxValue, setMaxValue] = React23__default.useState("");
8057
+ const [from, setFrom] = React23__default.useState("");
8058
+ const [to, setTo] = React23__default.useState("");
8059
+ const money3 = React23__default.useCallback((v) => formatCurrency(v, currency), [currency]);
8060
+ const seq = React23__default.useRef(0);
8061
+ const load = React23__default.useCallback(async () => {
7808
8062
  const mine = ++seq.current;
7809
8063
  setCards(null);
7810
8064
  setError(null);
@@ -7825,7 +8079,7 @@ function GiftCardsView() {
7825
8079
  setCards([]);
7826
8080
  }
7827
8081
  }, [provider, search, state, minValue, maxValue, from, to]);
7828
- React22__default.useEffect(() => {
8082
+ React23__default.useEffect(() => {
7829
8083
  void (async () => {
7830
8084
  try {
7831
8085
  setEnabled(await provider.giftCardsEnabled?.() === true);
@@ -7834,10 +8088,10 @@ function GiftCardsView() {
7834
8088
  }
7835
8089
  })();
7836
8090
  }, [provider]);
7837
- React22__default.useEffect(() => {
8091
+ React23__default.useEffect(() => {
7838
8092
  if (enabled) void load();
7839
8093
  }, [enabled, load]);
7840
- const columns = React22__default.useMemo(() => [
8094
+ const columns = React23__default.useMemo(() => [
7841
8095
  {
7842
8096
  accessorKey: "code",
7843
8097
  header: "C\xF3digo",
@@ -8018,12 +8272,12 @@ function IssueDialog({
8018
8272
  onIssued
8019
8273
  }) {
8020
8274
  const provider = useFinancialProvider();
8021
- const [quantity, setQuantity] = React22__default.useState("10");
8022
- const [amount, setAmount] = React22__default.useState("100");
8023
- const [expiresAt, setExpiresAt] = React22__default.useState("");
8024
- const [note, setNote] = React22__default.useState("");
8025
- const [saving, setSaving] = React22__default.useState(false);
8026
- const [error, setError] = React22__default.useState(null);
8275
+ const [quantity, setQuantity] = React23__default.useState("10");
8276
+ const [amount, setAmount] = React23__default.useState("100");
8277
+ const [expiresAt, setExpiresAt] = React23__default.useState("");
8278
+ const [note, setNote] = React23__default.useState("");
8279
+ const [saving, setSaving] = React23__default.useState(false);
8280
+ const [error, setError] = React23__default.useState(null);
8027
8281
  const q = Math.trunc(Number(quantity)) || 0;
8028
8282
  const a = Number(amount) || 0;
8029
8283
  const total = q * a;
@@ -8739,7 +8993,7 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
8739
8993
  /* @__PURE__ */ jsx("p", { className: "truncate text-sm", children: c.invoice?.contactName || c.movement.notes || c.invoice?.fiscalNumber || t2("financial.reconciliation.match") }),
8740
8994
  /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
8741
8995
  shortDate2(c.movement.dueDate),
8742
- /* @__PURE__ */ jsx("span", { className: "ml-1.5 inline-flex items-center rounded bg-primary/10 px-1 py-px font-medium text-primary", children: t2("financial.reconciliation.matchScore", { score: Math.round(c.score * 100) }) }),
8996
+ /* @__PURE__ */ jsx("span", { className: "ml-1.5 inline-flex items-center rounded bg-magic/10 px-1 py-px font-medium text-magic-soft-foreground", children: t2("financial.reconciliation.matchScore", { score: Math.round(c.score * 100) }) }),
8743
8997
  /* @__PURE__ */ jsx("span", { className: `ml-1.5 inline-flex items-center rounded px-1 py-px font-medium ${mode === "settle" ? "bg-warning/10 text-warning" : "bg-success/10 text-success"}`, children: mode === "settle" ? t2("financial.reconciliation.candidateOpen", { amount: fmt(openAmount) }) : t2("financial.reconciliation.candidatePaid") })
8744
8998
  ] })
8745
8999
  ] }),
@@ -9199,9 +9453,9 @@ function ReconciliationView() {
9199
9453
  dateFrom: defaultFrom,
9200
9454
  dateTo: isoDay(/* @__PURE__ */ new Date()),
9201
9455
  direction: "all",
9202
- status: "all",
9203
- search: ""
9456
+ status: "all"
9204
9457
  });
9458
+ const [appliedSearch, setAppliedSearch] = useState("");
9205
9459
  const [page, setPage] = useState(1);
9206
9460
  const [result, setResult] = useState(null);
9207
9461
  const [loading, setLoading] = useState(false);
@@ -9213,7 +9467,16 @@ function ReconciliationView() {
9213
9467
  const supported = typeof provider.getStatementLines === "function";
9214
9468
  useEffect(() => {
9215
9469
  setPage(1);
9216
- }, [applied]);
9470
+ }, [applied, appliedSearch]);
9471
+ useEffect(() => {
9472
+ setCandidatos({});
9473
+ }, [applied, appliedSearch, page]);
9474
+ useEffect(() => {
9475
+ const termo = search.trim();
9476
+ if (termo === appliedSearch) return;
9477
+ const id = setTimeout(() => setAppliedSearch(termo), 300);
9478
+ return () => clearTimeout(id);
9479
+ }, [search, appliedSearch]);
9217
9480
  const load = useCallback(async () => {
9218
9481
  if (!provider.getStatementLines || !applied.accountId) {
9219
9482
  setResult(null);
@@ -9226,7 +9489,7 @@ function ReconciliationView() {
9226
9489
  dateRange: { from: applied.dateFrom, to: applied.dateTo },
9227
9490
  direction: applied.direction === "all" ? void 0 : applied.direction,
9228
9491
  status: applied.status === "all" ? void 0 : applied.status,
9229
- search: applied.search.trim() || void 0,
9492
+ search: appliedSearch || void 0,
9230
9493
  page,
9231
9494
  pageSize: PAGE_SIZE
9232
9495
  });
@@ -9234,16 +9497,30 @@ function ReconciliationView() {
9234
9497
  } finally {
9235
9498
  setLoading(false);
9236
9499
  }
9237
- }, [provider, applied, page]);
9500
+ }, [provider, applied, appliedSearch, page]);
9238
9501
  useEffect(() => {
9239
9502
  void load();
9240
9503
  }, [load]);
9504
+ const linhasAbertas = useMemo(
9505
+ () => (result?.rows ?? []).filter((linha) => !linha.reconciledAt),
9506
+ [result]
9507
+ );
9508
+ useEffect(() => {
9509
+ if (!provider.suggestReconciliationBatch || linhasAbertas.length === 0) return;
9510
+ let vivo = true;
9511
+ provider.suggestReconciliationBatch(linhasAbertas).then((achados) => {
9512
+ if (vivo) setCandidatos((prev) => ({ ...achados, ...prev }));
9513
+ }).catch((error) => console.warn("[financial] n\xE3o deu para sugerir a concilia\xE7\xE3o", error));
9514
+ return () => {
9515
+ vivo = false;
9516
+ };
9517
+ }, [provider, linhasAbertas]);
9241
9518
  function filtrar() {
9242
- setApplied({ accountId, dateFrom, dateTo, direction, status, search });
9519
+ setApplied({ accountId, dateFrom, dateTo, direction, status });
9243
9520
  }
9244
9521
  const filtrosPendentes = useMemo(
9245
- () => JSON.stringify({ accountId, dateFrom, dateTo, direction, status, search }) !== JSON.stringify(applied),
9246
- [accountId, dateFrom, dateTo, direction, status, search, applied]
9522
+ () => JSON.stringify({ accountId, dateFrom, dateTo, direction, status }) !== JSON.stringify(applied),
9523
+ [accountId, dateFrom, dateTo, direction, status, applied]
9247
9524
  );
9248
9525
  async function sugerir(line) {
9249
9526
  if (!provider.suggestReconciliation) return;
@@ -9281,11 +9558,11 @@ function ReconciliationView() {
9281
9558
  setBusy(false);
9282
9559
  }
9283
9560
  }
9284
- const RefreshIcon = React22__default.useMemo(
9561
+ const RefreshIcon = React23__default.useMemo(
9285
9562
  () => ({ className }) => /* @__PURE__ */ jsx(RefreshCw, { className: `${className ?? ""} ${loading ? "animate-spin" : ""}` }),
9286
9563
  [loading]
9287
9564
  );
9288
- const headerActions = React22__default.useMemo(() => [{
9565
+ const headerActions = React23__default.useMemo(() => [{
9289
9566
  id: "refresh",
9290
9567
  label: t2("financial.reconciliation.refresh"),
9291
9568
  icon: RefreshIcon,
@@ -9403,7 +9680,7 @@ function ReconciliationView() {
9403
9680
  value: search,
9404
9681
  onChange: (e) => setSearch(e.target.value),
9405
9682
  onKeyDown: (e) => {
9406
- if (e.key === "Enter") filtrar();
9683
+ if (e.key === "Enter") setAppliedSearch(search.trim());
9407
9684
  },
9408
9685
  placeholder: t2("financial.reconciliation.searchPlaceholder"),
9409
9686
  "aria-label": t2("financial.reconciliation.searchPlaceholder"),
@@ -9512,122 +9789,161 @@ function StatementCard({
9512
9789
  const conciliada = !!line.reconciledAt;
9513
9790
  const sugestao = candidatos?.[0];
9514
9791
  const delta = sugestao ? round24(sugestao.movement.amount - line.amount) : 0;
9515
- return /* @__PURE__ */ jsxs(Card, { className: `relative space-y-3 overflow-hidden p-4 ${conciliada ? "border-2 border-success/50" : sugestao ? "border-2 border-warning/60" : ""}`, children: [
9516
- (conciliada || sugestao) && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute right-0 top-0 z-10 h-24 w-24 overflow-hidden", "aria-hidden": true, children: /* @__PURE__ */ jsx(
9517
- "div",
9518
- {
9519
- className: `absolute py-1 text-center text-[10px] font-bold uppercase tracking-wide text-white shadow-md ${conciliada ? "bg-success" : "bg-warning"}`,
9520
- style: { transform: "rotate(45deg)", width: "140px", top: "22px", right: "-38px" },
9521
- children: conciliada ? t2("financial.reconciliation.reconciled") : t2("financial.reconciliation.suggestion")
9522
- }
9523
- ) }),
9524
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [
9525
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [
9526
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: line.date ? shortDate3(line.date) : "\u2014" }),
9527
- /* @__PURE__ */ jsx("span", { className: `rounded border px-1.5 py-px text-[11px] font-medium ${isCredit ? "border-success/60 text-success" : "border-destructive/60 text-destructive"}`, children: isCredit ? t2("financial.reconciliation.credit") : t2("financial.reconciliation.debit") }),
9528
- /* @__PURE__ */ jsx("span", { className: "truncate text-base font-semibold", children: line.description || line.counterpartyName || line.externalId }),
9529
- conciliada && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-success/10 px-1.5 py-0.5 text-[11px] font-medium text-success", children: [
9530
- /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
9531
- t2("financial.reconciliation.reconciled")
9792
+ return (
9793
+ // O cartão inteiro ganha um fundo de leve: a fita diz o estado a dois
9794
+ // metros, mas é o fundo que separa as linhas que pedem decisão das que já
9795
+ // não pedem, quando se rola cinquenta de uma vez.
9796
+ /* @__PURE__ */ jsxs(Card, { className: `relative space-y-3 overflow-hidden p-4 ${conciliada ? "border-2 border-success/50 bg-success-soft/25" : sugestao ? "border-2 border-magic/60 bg-magic-soft/40" : ""}`, children: [
9797
+ (conciliada || sugestao) && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute right-0 top-0 z-10 h-24 w-24 overflow-hidden", "aria-hidden": true, children: /* @__PURE__ */ jsx(
9798
+ "div",
9799
+ {
9800
+ className: `absolute py-1 text-center text-[10px] font-bold uppercase tracking-wide text-white shadow-md ${conciliada ? "bg-success" : "bg-magic"}`,
9801
+ style: { transform: "rotate(45deg)", width: "140px", top: "22px", right: "-38px" },
9802
+ children: conciliada ? t2("financial.reconciliation.reconciled") : t2("financial.reconciliation.suggestion")
9803
+ }
9804
+ ) }),
9805
+ /* @__PURE__ */ jsxs("div", { className: `flex flex-wrap items-start justify-between gap-4 ${conciliada || sugestao ? "pr-24" : ""}`, children: [
9806
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [
9807
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: line.date ? shortDate3(line.date) : "\u2014" }),
9808
+ /* @__PURE__ */ jsx("span", { className: `rounded border px-1.5 py-px text-[11px] font-medium ${isCredit ? "border-success/60 text-success" : "border-destructive/60 text-destructive"}`, children: isCredit ? t2("financial.reconciliation.credit") : t2("financial.reconciliation.debit") }),
9809
+ /* @__PURE__ */ jsx("span", { className: "truncate text-base font-semibold", children: line.description || line.counterpartyName || line.externalId }),
9810
+ conciliada && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-success/10 px-1.5 py-0.5 text-[11px] font-medium text-success", children: [
9811
+ /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
9812
+ t2("financial.reconciliation.reconciled")
9813
+ ] }),
9814
+ conciliada && line.matchedCounterpartyName && /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
9815
+ isCredit ? t2("financial.reconciliation.payerLabel") : t2("financial.reconciliation.supplierLabel"),
9816
+ " ",
9817
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: line.matchedCounterpartyName })
9818
+ ] })
9532
9819
  ] }),
9533
- conciliada && line.matchedCounterpartyName && /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
9534
- isCredit ? t2("financial.reconciliation.payerLabel") : t2("financial.reconciliation.supplierLabel"),
9535
- " ",
9536
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: line.matchedCounterpartyName })
9820
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right", children: [
9821
+ /* @__PURE__ */ jsxs("div", { className: `whitespace-nowrap text-lg font-bold tabular-nums ${isCredit ? "text-success" : "text-destructive"}`, children: [
9822
+ isCredit ? "+ " : "\u2212 ",
9823
+ fmt(line.amount)
9824
+ ] }),
9825
+ line.balanceAfter != null && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground tabular-nums", children: [
9826
+ t2("financial.reconciliation.balanceAt"),
9827
+ " ",
9828
+ fmt(line.balanceAfter)
9829
+ ] })
9537
9830
  ] })
9538
9831
  ] }),
9539
- /* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right", children: [
9540
- /* @__PURE__ */ jsxs("div", { className: `whitespace-nowrap text-lg font-bold tabular-nums ${isCredit ? "text-success" : "text-destructive"}`, children: [
9541
- isCredit ? "+ " : "\u2212 ",
9542
- fmt(line.amount)
9543
- ] }),
9544
- line.balanceAfter != null && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground tabular-nums", children: [
9545
- t2("financial.reconciliation.balanceAt"),
9832
+ (line.document || line.code || line.category || line.transactionType) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-3 text-xs text-muted-foreground", children: [
9833
+ line.document && /* @__PURE__ */ jsxs("span", { children: [
9834
+ t2("financial.reconciliation.docPrefix"),
9546
9835
  " ",
9547
- fmt(line.balanceAfter)
9548
- ] })
9549
- ] })
9550
- ] }),
9551
- (line.document || line.code || line.category || line.transactionType) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-3 text-xs text-muted-foreground", children: [
9552
- line.document && /* @__PURE__ */ jsxs("span", { children: [
9553
- t2("financial.reconciliation.docPrefix"),
9554
- " ",
9555
- line.document
9556
- ] }),
9557
- line.code && /* @__PURE__ */ jsx("span", { children: line.code }),
9558
- line.category && /* @__PURE__ */ jsx("span", { children: line.category }),
9559
- line.transactionType && /* @__PURE__ */ jsx("span", { children: line.transactionType })
9560
- ] }),
9561
- !conciliada && sugestao && /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-warning/50 bg-warning/5 p-3", children: [
9562
- /* @__PURE__ */ jsxs("div", { className: "text-sm", children: [
9563
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: t2("financial.reconciliation.suggestionLabel") }),
9564
- " ",
9565
- sugestao.invoice?.contactName ?? sugestao.movement.notes ?? "\u2014",
9566
- sugestao.movement.installmentNumber ? ` \xB7 ${t2("financial.reconciliation.installment")} ${sugestao.movement.installmentNumber}` : "",
9567
- sugestao.movement.dueDate ? ` \xB7 ${t2("financial.reconciliation.dueShort")} ${shortDate3(sugestao.movement.dueDate)}` : "",
9568
- " \xB7 ",
9569
- /* @__PURE__ */ jsx("span", { className: "font-semibold", children: fmt(sugestao.movement.amount) }),
9570
- Math.abs(delta) >= 0.01 && /* @__PURE__ */ jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: [
9571
- "(\u0394 ",
9572
- delta >= 0 ? "+" : "",
9573
- fmt(delta),
9574
- ")"
9836
+ line.document
9575
9837
  ] }),
9576
- /* @__PURE__ */ jsx("span", { className: "ml-2 inline-flex items-center rounded bg-primary/10 px-1 py-px text-[11px] font-medium text-primary", children: t2("financial.reconciliation.matchScore", { score: Math.round(sugestao.score * 100) }) })
9838
+ line.code && /* @__PURE__ */ jsx("span", { children: line.code }),
9839
+ line.category && /* @__PURE__ */ jsx("span", { children: line.category }),
9840
+ line.transactionType && /* @__PURE__ */ jsx("span", { children: line.transactionType })
9577
9841
  ] }),
9578
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
9579
- /* @__PURE__ */ jsxs(Button, { size: "sm", disabled: busy, onClick: () => onAceitarCandidato(sugestao.movement.id), children: [
9580
- /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
9581
- t2("financial.reconciliation.match")
9582
- ] }),
9583
- candidatos && candidatos.length > 1 && /* @__PURE__ */ jsx("span", { className: "self-center text-xs text-muted-foreground", children: t2("financial.reconciliation.moreCandidates", { count: candidatos.length - 1 }) })
9584
- ] })
9585
- ] }),
9586
- !conciliada && candidatos && candidatos.length === 0 && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed px-3 py-2 text-xs text-muted-foreground", children: t2("financial.reconciliation.noCandidates") }),
9587
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-2", children: [
9588
- !conciliada && /* @__PURE__ */ jsxs(Fragment, { children: [
9589
- /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: sugerindo || busy, onClick: onSugerir, children: [
9590
- /* @__PURE__ */ jsx(Sparkles, { className: `h-3 w-3 ${sugerindo ? "animate-pulse" : ""}` }),
9591
- t2("financial.reconciliation.suggest")
9592
- ] }),
9593
- /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", disabled: busy, onClick: onTransferir, children: [
9594
- /* @__PURE__ */ jsx(ArrowRightLeft, { className: "h-3 w-3" }),
9595
- t2("financial.reconciliation.markTransfer")
9842
+ !conciliada && sugestao && // O QUE SE LÊ À ESQUERDA, O QUE SE CLICA À DIREITA.
9843
+ //
9844
+ // «Conciliar» embaixo do texto empurrava a decisão para a terceira
9845
+ // linha do bloco e deixava o cartão com botão em três alturas
9846
+ // diferentes. Ele é a única ação deste bloco, então mora na borda
9847
+ // direita, na altura da frase que ele aceita.
9848
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-3 rounded-md border border-magic/40 bg-card/70 p-3", children: [
9849
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 text-sm", children: [
9850
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 font-medium text-magic-soft-foreground", children: [
9851
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": true }),
9852
+ t2("financial.reconciliation.suggestionLabel")
9853
+ ] }),
9854
+ " ",
9855
+ sugestao.invoice?.contactName ?? sugestao.movement.notes ?? "\u2014",
9856
+ sugestao.movement.installmentNumber ? ` \xB7 ${t2("financial.reconciliation.installment")} ${sugestao.movement.installmentNumber}` : "",
9857
+ sugestao.movement.dueDate ? ` \xB7 ${t2("financial.reconciliation.dueShort")} ${shortDate3(sugestao.movement.dueDate)}` : "",
9858
+ " \xB7 ",
9859
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", children: fmt(sugestao.movement.amount) }),
9860
+ Math.abs(delta) >= 0.01 && /* @__PURE__ */ jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: [
9861
+ "(\u0394 ",
9862
+ delta >= 0 ? "+" : "",
9863
+ fmt(delta),
9864
+ ")"
9865
+ ] }),
9866
+ /* @__PURE__ */ jsx("span", { className: "ml-2 inline-flex items-center rounded bg-magic/10 px-1 py-px text-[11px] font-medium text-magic-soft-foreground", children: t2("financial.reconciliation.matchScore", { score: Math.round(sugestao.score * 100) }) }),
9867
+ candidatos && candidatos.length > 1 && /* @__PURE__ */ jsx(
9868
+ "button",
9869
+ {
9870
+ type: "button",
9871
+ onClick: onVincular,
9872
+ className: "ml-2 text-xs text-muted-foreground underline-offset-2 hover:underline",
9873
+ children: t2("financial.reconciliation.moreCandidates", { count: candidatos.length - 1 })
9874
+ }
9875
+ )
9596
9876
  ] }),
9597
- /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onVincular, children: [
9877
+ /* @__PURE__ */ jsxs(Button, { size: "sm", className: "shrink-0", disabled: busy, onClick: () => onAceitarCandidato(sugestao.movement.id), children: [
9598
9878
  /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
9599
- t2("financial.reconciliation.linkManually")
9600
- ] }),
9601
- /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", disabled: busy, onClick: onLancarConta, children: [
9602
- /* @__PURE__ */ jsx(Receipt, { className: "h-3 w-3" }),
9603
- t2("financial.reconciliation.createBill")
9879
+ t2("financial.reconciliation.match")
9604
9880
  ] })
9605
9881
  ] }),
9606
- conciliada && /* @__PURE__ */ jsxs(Fragment, { children: [
9607
- /* @__PURE__ */ jsx(Hint, { label: line.matchedInvoiceId ? t2("financial.reconciliation.viewBill") : t2("financial.reconciliation.viewBillUnavailable"), children: /* @__PURE__ */ jsxs(
9882
+ !conciliada && candidatos && candidatos.length === 0 && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed px-3 py-2 text-xs text-muted-foreground", children: t2("financial.reconciliation.noCandidates") }),
9883
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-2", children: [
9884
+ !conciliada && /* @__PURE__ */ jsxs(Fragment, { children: [
9885
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", disabled: busy, onClick: onTransferir, children: [
9886
+ /* @__PURE__ */ jsx(ArrowRightLeft, { className: "h-3 w-3" }),
9887
+ t2("financial.reconciliation.markTransfer")
9888
+ ] }),
9889
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onVincular, children: [
9890
+ /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
9891
+ t2("financial.reconciliation.linkManually")
9892
+ ] }),
9893
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", disabled: busy, onClick: onLancarConta, children: [
9894
+ /* @__PURE__ */ jsx(Receipt, { className: "h-3 w-3" }),
9895
+ t2("financial.reconciliation.createBill")
9896
+ ] })
9897
+ ] }),
9898
+ conciliada && /* @__PURE__ */ jsxs(Fragment, { children: [
9899
+ /* @__PURE__ */ jsx(Hint, { label: line.matchedInvoiceId ? t2("financial.reconciliation.viewBill") : t2("financial.reconciliation.viewBillUnavailable"), children: /* @__PURE__ */ jsxs(
9900
+ Button,
9901
+ {
9902
+ variant: "outline",
9903
+ size: "sm",
9904
+ disabled: !line.matchedInvoiceId,
9905
+ onClick: onVerConta,
9906
+ children: [
9907
+ /* @__PURE__ */ jsx(FileSearch, { className: "h-3 w-3" }),
9908
+ t2("financial.reconciliation.viewBill")
9909
+ ]
9910
+ }
9911
+ ) }),
9912
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", disabled: busy, onClick: onDesfazer, children: [
9913
+ /* @__PURE__ */ jsx(Undo2, { className: "h-3 w-3" }),
9914
+ t2("financial.reconciliation.undo")
9915
+ ] })
9916
+ ] }),
9917
+ !conciliada && !sugestao && /* @__PURE__ */ jsxs(
9608
9918
  Button,
9609
9919
  {
9610
9920
  variant: "outline",
9611
9921
  size: "sm",
9612
- disabled: !line.matchedInvoiceId,
9613
- onClick: onVerConta,
9922
+ className: "ml-auto border-magic/50 text-magic-soft-foreground hover:bg-magic-soft/50",
9923
+ disabled: sugerindo || busy,
9924
+ onClick: onSugerir,
9614
9925
  children: [
9615
- /* @__PURE__ */ jsx(FileSearch, { className: "h-3 w-3" }),
9616
- t2("financial.reconciliation.viewBill")
9926
+ /* @__PURE__ */ jsx(Sparkles, { className: `h-3 w-3 ${sugerindo ? "animate-pulse" : ""}` }),
9927
+ t2("financial.reconciliation.suggest")
9617
9928
  ]
9618
9929
  }
9619
- ) }),
9620
- /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", disabled: busy, onClick: onDesfazer, children: [
9621
- /* @__PURE__ */ jsx(Undo2, { className: "h-3 w-3" }),
9622
- t2("financial.reconciliation.undo")
9623
- ] })
9624
- ] }),
9625
- /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", className: "ml-auto", onClick: onDetalhes, children: [
9626
- /* @__PURE__ */ jsx(Eye, { className: "h-3 w-3" }),
9627
- t2("financial.reconciliation.details")
9930
+ ),
9931
+ /* @__PURE__ */ jsxs(
9932
+ Button,
9933
+ {
9934
+ variant: "ghost",
9935
+ size: "sm",
9936
+ className: conciliada || sugestao ? "ml-auto" : "",
9937
+ onClick: onDetalhes,
9938
+ children: [
9939
+ /* @__PURE__ */ jsx(Eye, { className: "h-3 w-3" }),
9940
+ t2("financial.reconciliation.details")
9941
+ ]
9942
+ }
9943
+ )
9628
9944
  ] })
9629
9945
  ] })
9630
- ] });
9946
+ );
9631
9947
  }
9632
9948
  function round24(n) {
9633
9949
  return Math.round(n * 100) / 100;
@@ -9671,13 +9987,13 @@ function RecurringBillsList({ direction, onNavigate }) {
9671
9987
  const provider = useFinancialProvider();
9672
9988
  const { currency } = useFinancialConfig();
9673
9989
  const can = usePermissionOptional();
9674
- const [rows, setRows] = React22__default.useState(null);
9675
- const [search, setSearch] = React22__default.useState("");
9676
- const [status, setStatus] = React22__default.useState("all");
9677
- const [changing, setChanging] = React22__default.useState(null);
9990
+ const [rows, setRows] = React23__default.useState(null);
9991
+ const [search, setSearch] = React23__default.useState("");
9992
+ const [status, setStatus] = React23__default.useState("all");
9993
+ const [changing, setChanging] = React23__default.useState(null);
9678
9994
  const isPayable = direction === "payable";
9679
9995
  const prefix = isPayable ? "payables" : "receivables";
9680
- const load = React22__default.useCallback(async () => {
9996
+ const load = React23__default.useCallback(async () => {
9681
9997
  setRows(null);
9682
9998
  try {
9683
9999
  setRows(await provider.getRecurringBills?.(direction) ?? []);
@@ -9686,10 +10002,10 @@ function RecurringBillsList({ direction, onNavigate }) {
9686
10002
  toast.error(error?.message ?? "N\xE3o foi poss\xEDvel carregar as recorr\xEAncias.");
9687
10003
  }
9688
10004
  }, [provider, direction]);
9689
- React22__default.useEffect(() => {
10005
+ React23__default.useEffect(() => {
9690
10006
  void load();
9691
10007
  }, [load]);
9692
- const filtered = React22__default.useMemo(() => {
10008
+ const filtered = React23__default.useMemo(() => {
9693
10009
  const term = search.trim().toLowerCase();
9694
10010
  const visiveis = (rows ?? []).filter((row) => {
9695
10011
  if (status === "active" && !row.isActive) return false;
@@ -9699,7 +10015,7 @@ function RecurringBillsList({ direction, onNavigate }) {
9699
10015
  });
9700
10016
  return [...visiveis].sort((a, b) => Number(b.isActive) - Number(a.isActive) || (a.dueDay ?? 0) - (b.dueDay ?? 0) || a.description.localeCompare(b.description));
9701
10017
  }, [rows, search, status]);
9702
- const porMes = React22__default.useMemo(() => {
10018
+ const porMes = React23__default.useMemo(() => {
9703
10019
  const fator = { weekly: 52 / 12, monthly: 1, quarterly: 1 / 3, yearly: 1 / 12 };
9704
10020
  const ativas = filtered.filter((row) => row.isActive);
9705
10021
  return {
@@ -9720,7 +10036,7 @@ function RecurringBillsList({ direction, onNavigate }) {
9720
10036
  setChanging(null);
9721
10037
  }
9722
10038
  }
9723
- const columns = React22__default.useMemo(() => [
10039
+ const columns = React23__default.useMemo(() => [
9724
10040
  {
9725
10041
  accessorKey: "description",
9726
10042
  header: "Descri\xE7\xE3o",
@@ -9817,40 +10133,40 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
9817
10133
  const provider = useFinancialProvider();
9818
10134
  const { locations, currency, itemTypes: configuredItemTypes, entityLookups } = useFinancialConfig();
9819
10135
  const isPayable = direction === "payable";
9820
- const [value, setValue] = React22__default.useState(null);
9821
- const [loaded, setLoaded] = React22__default.useState(!valueId);
9822
- const [contact, setContact] = React22__default.useState(null);
9823
- const [counterpartyAccounting, setCounterpartyAccounting] = React22__default.useState({
10136
+ const [value, setValue] = React23__default.useState(null);
10137
+ const [loaded, setLoaded] = React23__default.useState(!valueId);
10138
+ const [contact, setContact] = React23__default.useState(null);
10139
+ const [counterpartyAccounting, setCounterpartyAccounting] = React23__default.useState({
9824
10140
  allowedAccountIds: [],
9825
10141
  limitAccounts: false,
9826
10142
  addItemsAutomatically: false
9827
10143
  });
9828
- const accountingPolicyRequest = React22__default.useRef(0);
9829
- const counterpartyLookup = React22__default.useMemo(
10144
+ const accountingPolicyRequest = React23__default.useRef(0);
10145
+ const counterpartyLookup = React23__default.useMemo(
9830
10146
  () => createFinancialCounterpartyLookup(isPayable ? "debit" : "credit"),
9831
10147
  [isPayable]
9832
10148
  );
9833
10149
  const contactCreate = useLookupCreate(counterpartyLookup, (created) => {
9834
10150
  setContact({ id: created.id, name: created.label });
9835
10151
  });
9836
- const initialItem = React22__default.useRef(newRecurringItem());
9837
- const [items, setItems] = React22__default.useState([initialItem.current]);
9838
- const [expandedItemId, setExpandedItemId] = React22__default.useState(initialItem.current._id);
9839
- const [addingItem, setAddingItem] = React22__default.useState(false);
9840
- const [frequency, setFrequency] = React22__default.useState("monthly");
9841
- const [dueDay, setDueDay] = React22__default.useState("1");
9842
- const [startsOn, setStartsOn] = React22__default.useState(today3());
9843
- const [endsOn, setEndsOn] = React22__default.useState("");
9844
- const [unitId, setUnitId] = React22__default.useState(locations[0]?.id ?? "");
9845
- const [availableLocations, setAvailableLocations] = React22__default.useState(locations);
9846
- const [notes, setNotes] = React22__default.useState("");
9847
- const [isActive, setIsActive] = React22__default.useState(true);
9848
- const [accounts, setAccounts] = React22__default.useState([]);
9849
- const [costCenters, setCostCenters] = React22__default.useState([]);
9850
- const [saving, setSaving] = React22__default.useState(false);
9851
- const [ending, setEnding] = React22__default.useState(false);
9852
- const [error, setError] = React22__default.useState(null);
9853
- React22__default.useEffect(() => {
10152
+ const initialItem = React23__default.useRef(newRecurringItem());
10153
+ const [items, setItems] = React23__default.useState([initialItem.current]);
10154
+ const [expandedItemId, setExpandedItemId] = React23__default.useState(initialItem.current._id);
10155
+ const [addingItem, setAddingItem] = React23__default.useState(false);
10156
+ const [frequency, setFrequency] = React23__default.useState("monthly");
10157
+ const [dueDay, setDueDay] = React23__default.useState("1");
10158
+ const [startsOn, setStartsOn] = React23__default.useState(today3());
10159
+ const [endsOn, setEndsOn] = React23__default.useState("");
10160
+ const [unitId, setUnitId] = React23__default.useState(locations[0]?.id ?? "");
10161
+ const [availableLocations, setAvailableLocations] = React23__default.useState(locations);
10162
+ const [notes, setNotes] = React23__default.useState("");
10163
+ const [isActive, setIsActive] = React23__default.useState(true);
10164
+ const [accounts, setAccounts] = React23__default.useState([]);
10165
+ const [costCenters, setCostCenters] = React23__default.useState([]);
10166
+ const [saving, setSaving] = React23__default.useState(false);
10167
+ const [ending, setEnding] = React23__default.useState(false);
10168
+ const [error, setError] = React23__default.useState(null);
10169
+ React23__default.useEffect(() => {
9854
10170
  if (locations.length > 0) {
9855
10171
  setAvailableLocations(locations);
9856
10172
  return;
@@ -9865,7 +10181,7 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
9865
10181
  cancelled = true;
9866
10182
  };
9867
10183
  }, [locations]);
9868
- React22__default.useEffect(() => {
10184
+ React23__default.useEffect(() => {
9869
10185
  let cancelled = false;
9870
10186
  void Promise.all([
9871
10187
  provider.getChartOfAccounts(),
@@ -9912,21 +10228,21 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
9912
10228
  cancelled = true;
9913
10229
  };
9914
10230
  }, [provider, direction, valueId]);
9915
- const offeredItemTypes = React22__default.useMemo(() => availableInvoiceItemTypes(
10231
+ const offeredItemTypes = React23__default.useMemo(() => availableInvoiceItemTypes(
9916
10232
  configuredItemTypes,
9917
10233
  Object.keys(entityLookups),
9918
10234
  configuredItemTypes.find((item) => item.value === "other")?.label ?? "Outro",
9919
10235
  direction
9920
10236
  ), [configuredItemTypes, entityLookups, direction]);
9921
10237
  const itemTypes = useStockedItemTypes(offeredItemTypes, entityLookups);
9922
- const selectableAccounts = React22__default.useMemo(() => {
10238
+ const selectableAccounts = React23__default.useMemo(() => {
9923
10239
  const postable = postableAccounts(accounts);
9924
10240
  if (!counterpartyAccounting.limitAccounts) return postable;
9925
10241
  const allowed = new Set(counterpartyAccounting.allowedAccountIds);
9926
10242
  return postable.filter((account) => allowed.has(account.id));
9927
10243
  }, [accounts, counterpartyAccounting]);
9928
10244
  const hasItemLookups = itemTypes.some((item) => entityLookups[item.value]);
9929
- const amount = React22__default.useMemo(() => items.reduce(
10245
+ const amount = React23__default.useMemo(() => items.reduce(
9930
10246
  (sum, item) => sum + item.quantity * item.unitPrice - (item.discount ?? 0) + (item.surcharge ?? 0),
9931
10247
  0
9932
10248
  ), [items]);
@@ -9934,8 +10250,8 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
9934
10250
  const numericDueDay = Math.trunc(Number(dueDay));
9935
10251
  const invalid = items.length === 0 ? "Adicione pelo menos um item." : items.some((item) => !item.description.trim()) ? "Informe a descri\xE7\xE3o de todos os itens." : !contact?.id ? `Selecione ${isPayable ? "quem recebe" : "quem paga"}.` : !(amount > 0) ? "Informe um valor maior que zero." : numericDueDay < 1 || numericDueDay > 31 ? "O dia do vencimento vai de 1 a 31." : !startsOn ? "Informe o in\xEDcio da recorr\xEAncia." : endsOn && endsOn < startsOn ? "O t\xE9rmino n\xE3o pode vir antes do in\xEDcio." : null;
9936
10252
  const fields = { items, contactId: contact?.id ?? "", frequency, dueDay, startsOn, endsOn, unitId, notes, isActive };
9937
- const snapshot = React22__default.useRef(null);
9938
- React22__default.useEffect(() => {
10253
+ const snapshot = React23__default.useRef(null);
10254
+ React23__default.useEffect(() => {
9939
10255
  if (loaded && snapshot.current === null) snapshot.current = JSON.stringify(fields);
9940
10256
  }, [loaded]);
9941
10257
  const dirty = snapshot.current !== null && JSON.stringify(fields) !== snapshot.current;
@@ -10792,7 +11108,7 @@ function BalanceHero() {
10792
11108
  const summary = useFinancialStore((s) => s.summary);
10793
11109
  const navigate = useDashboardNavigate();
10794
11110
  useEnsureSummary();
10795
- const [hidden, setHidden] = React22__default.useState(false);
11111
+ const [hidden, setHidden] = React23__default.useState(false);
10796
11112
  const money3 = (v) => hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022" : formatCurrency(v ?? 0, currency);
10797
11113
  const inflow = summary?.monthlyInflow ?? 0;
10798
11114
  const outflow = summary?.monthlyOutflow ?? 0;
@@ -10865,7 +11181,7 @@ function categorizeSpend(notes) {
10865
11181
  function SpendByCategoryChart() {
10866
11182
  const t2 = useTranslation();
10867
11183
  const provider = useFinancialProvider();
10868
- const [rows, setRows] = React22__default.useState([]);
11184
+ const [rows, setRows] = React23__default.useState([]);
10869
11185
  useEffect(() => {
10870
11186
  let alive = true;
10871
11187
  void (async () => {
@@ -10908,7 +11224,7 @@ function UpcomingBills() {
10908
11224
  const { currency } = useFinancialConfig();
10909
11225
  const provider = useFinancialProvider();
10910
11226
  const dueLabel = useDueLabel();
10911
- const [bills, setBills] = React22__default.useState([]);
11227
+ const [bills, setBills] = React23__default.useState([]);
10912
11228
  useEffect(() => {
10913
11229
  let alive = true;
10914
11230
  void (async () => {
@@ -11001,6 +11317,115 @@ function createFinancialDashboardWidgets(ctx2) {
11001
11317
  ];
11002
11318
  }
11003
11319
 
11320
+ // src/data/match-statement.ts
11321
+ var SUGGESTION_FLOOR = 0.62;
11322
+ var STOPWORDS = /* @__PURE__ */ new Set([
11323
+ "pix",
11324
+ "ted",
11325
+ "doc",
11326
+ "tev",
11327
+ "transferpix",
11328
+ "transferencia",
11329
+ "transferencias",
11330
+ "enviado",
11331
+ "enviada",
11332
+ "recebido",
11333
+ "recebida",
11334
+ "pagamento",
11335
+ "pagto",
11336
+ "pag",
11337
+ "boleto",
11338
+ "debito",
11339
+ "credito",
11340
+ "compra",
11341
+ "cartao",
11342
+ "saque",
11343
+ "deposito",
11344
+ "cobranca",
11345
+ "titulo",
11346
+ "conta",
11347
+ "fatura",
11348
+ "tarifa",
11349
+ "taxa",
11350
+ "liquidacao",
11351
+ "de",
11352
+ "da",
11353
+ "do",
11354
+ "das",
11355
+ "dos",
11356
+ "e",
11357
+ "em",
11358
+ "para",
11359
+ "por",
11360
+ "a",
11361
+ "o",
11362
+ "ao",
11363
+ "ltda",
11364
+ "me",
11365
+ "epp",
11366
+ "sa",
11367
+ "eireli",
11368
+ "mei",
11369
+ "cia"
11370
+ ]);
11371
+ function nameTokens(text2) {
11372
+ return (text2 ?? "").normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w.length >= 3 && !STOPWORDS.has(w) && !/^\d+$/.test(w));
11373
+ }
11374
+ function nameScore(descricao, nome) {
11375
+ const alvo = nameTokens(nome);
11376
+ if (alvo.length === 0) return 0;
11377
+ const texto = new Set(nameTokens(descricao));
11378
+ if (texto.size === 0) return 0;
11379
+ const achados = alvo.filter((w) => texto.has(w)).length;
11380
+ const razao = achados / alvo.length;
11381
+ if (achados === 1 && alvo.length > 2) return razao * 0.4;
11382
+ return razao;
11383
+ }
11384
+ function amountScore(lineAmount, movementAmount) {
11385
+ if (lineAmount <= 0) return 0;
11386
+ const diff = Math.abs(movementAmount - lineAmount);
11387
+ if (diff > lineAmount * 0.02 + 0.01) return 0;
11388
+ return Math.max(0, 1 - diff / (lineAmount * 0.02 + 0.01));
11389
+ }
11390
+ function dateScore(lineDate, refDate) {
11391
+ if (!lineDate || !refDate) return 0;
11392
+ const days = Math.abs(new Date(lineDate).getTime() - new Date(refDate).getTime()) / 864e5;
11393
+ return Math.max(0, 1 - days / 30);
11394
+ }
11395
+ function scorePair(line, movement, context = {}) {
11396
+ if (movement.direction !== line.direction) return 0;
11397
+ const valor = amountScore(line.amount, movement.amount);
11398
+ if (valor === 0) return 0;
11399
+ const texto = `${line.description ?? ""} ${line.counterpartyName ?? ""}`;
11400
+ const nome = Math.max(
11401
+ nameScore(texto, context.contactName),
11402
+ nameScore(texto, movement.notes)
11403
+ );
11404
+ const data = dateScore(line.date, movement.paymentDate ?? movement.dueDate);
11405
+ return Math.round((0.45 * valor + 0.35 * nome + 0.2 * data) * 100) / 100;
11406
+ }
11407
+ function matchStatementLines(lines, movements, context = /* @__PURE__ */ new Map(), limitPerLine = 3) {
11408
+ const pares = [];
11409
+ for (const line of lines) {
11410
+ if (line.reconciledAt) continue;
11411
+ for (const movement of movements) {
11412
+ const score = scorePair(line, movement, context.get(movement.id) ?? {});
11413
+ if (score >= SUGGESTION_FLOOR) pares.push({ lineId: line.id, movement, score });
11414
+ }
11415
+ }
11416
+ pares.sort((a, b) => b.score - a.score);
11417
+ const resultado = {};
11418
+ const usados = /* @__PURE__ */ new Set();
11419
+ for (const par of pares) {
11420
+ if (usados.has(par.movement.id)) continue;
11421
+ const lista = resultado[par.lineId] ?? (resultado[par.lineId] = []);
11422
+ if (lista.length >= limitPerLine) continue;
11423
+ lista.push({ movement: par.movement, score: par.score, invoice: context.get(par.movement.id)?.invoice });
11424
+ usados.add(par.movement.id);
11425
+ }
11426
+ return resultado;
11427
+ }
11428
+
11004
11429
  // src/data/mock.ts
11005
11430
  var nextId = 1;
11006
11431
  function uid() {
@@ -11268,6 +11693,7 @@ function createMockFinancialProvider(options) {
11268
11693
  if (!mov) return;
11269
11694
  if (data.dueDate !== void 0) mov.dueDate = data.dueDate;
11270
11695
  if (data.amount !== void 0) mov.amount = data.amount;
11696
+ if (data.metadata !== void 0) mov.metadata = data.metadata;
11271
11697
  },
11272
11698
  async replaceInvoiceItems(invoiceId, items) {
11273
11699
  const invoice = store6.invoices.find((i) => i.id === invoiceId);
@@ -11317,7 +11743,7 @@ function createMockFinancialProvider(options) {
11317
11743
  status: "pending",
11318
11744
  dueDate: inst.dueDate,
11319
11745
  installmentNumber: i + 1,
11320
- metadata: carried.get(i + 1),
11746
+ metadata: inst.metadata ?? carried.get(i + 1),
11321
11747
  tenantId,
11322
11748
  createdAt: now(),
11323
11749
  updatedAt: now()
@@ -11854,14 +12280,25 @@ function createMockFinancialProvider(options) {
11854
12280
  const amtScore = amount > 0 ? Math.max(0, 1 - Math.abs(m.amount - amount) / amount) : 0;
11855
12281
  const refDate = m.dueDate ?? m.paymentDate;
11856
12282
  const days = refDate ? Math.abs((new Date(line.date).getTime() - new Date(refDate).getTime()) / 864e5) : 30;
11857
- const dateScore = Math.max(0, 1 - days / 30);
12283
+ const dateScore2 = Math.max(0, 1 - days / 30);
11858
12284
  return {
11859
12285
  movement: m,
11860
12286
  invoice: m.invoiceId ? store6.invoices.find((i) => i.id === m.invoiceId) : void 0,
11861
- score: round25(0.7 * amtScore + 0.3 * dateScore)
12287
+ score: round25(0.7 * amtScore + 0.3 * dateScore2)
11862
12288
  };
11863
12289
  }).sort((a, b) => b.score - a.score).slice(0, 25);
11864
12290
  },
12291
+ // A mesma cabeça da conciliação de verdade (match-statement), sobre a
12292
+ // memória: valor, nome e data, e um lançamento por linha.
12293
+ async suggestReconciliationBatch(lines) {
12294
+ const contexto = /* @__PURE__ */ new Map();
12295
+ for (const movement of store6.movements) {
12296
+ const invoice = movement.invoiceId ? store6.invoices.find((i) => i.id === movement.invoiceId) : void 0;
12297
+ if (invoice) contexto.set(movement.id, { contactName: invoice.contactName, invoice });
12298
+ }
12299
+ const candidatos = store6.movements.filter((m) => !m.externalSource && !m.reconciledAt);
12300
+ return matchStatementLines(lines, candidatos, contexto);
12301
+ },
11865
12302
  async reconcileMovement(input) {
11866
12303
  if (input.mode === "settle" && input.matchedMovementId) {
11867
12304
  const line = store6.statementLines.find((l) => l.id === input.bankMovementId);
@@ -12545,6 +12982,7 @@ function createSupabaseFinancialProvider() {
12545
12982
  const row = {};
12546
12983
  if (data.dueDate !== void 0) row.due_date = data.dueDate;
12547
12984
  if (data.amount !== void 0) row.amount = data.amount;
12985
+ if (data.metadata !== void 0) row.metadata = data.metadata;
12548
12986
  if (Object.keys(row).length === 0) return;
12549
12987
  const { error } = await pub.from("plg_financial_invoice_installments").update(row).eq("id", id);
12550
12988
  if (error) throw error;
@@ -12604,7 +13042,11 @@ function createSupabaseFinancialProvider() {
12604
13042
  p_installments: installments.map((inst, i) => ({
12605
13043
  number: i + 1,
12606
13044
  due_date: inst.dueDate,
12607
- amount: inst.amount
13045
+ amount: inst.amount,
13046
+ // 111 faz `metadata = coalesce(v_meta, metadata)`: o que vai aqui
13047
+ // SUBSTITUI a metadata da parcela, então o chamador manda o conjunto
13048
+ // inteiro, não o delta.
13049
+ ...inst.metadata ? { metadata: inst.metadata } : {}
12608
13050
  })),
12609
13051
  p_idempotency_key: createIdempotencyKey("financial:invoice-reschedule")
12610
13052
  });
@@ -13375,7 +13817,11 @@ function createSupabaseFinancialProvider() {
13375
13817
  const { pub } = getClients();
13376
13818
  const direcao = query.direction === "debit" ? "payable" : "receivable";
13377
13819
  let qb = pub.from(T.recurringOccurrences).select(`id, seq, due_date, competence_date, amount, unit_id, recurring_bill_id,
13378
- ${T.recurringBills}!inner(id, description, direction, counterparty_person_id, is_active)`).eq("status", "provisioned").eq(`${T.recurringBills}.direction`, direcao).eq(`${T.recurringBills}.is_active`, true);
13820
+ ${T.recurringBills}!inner(id, description, notes, direction, counterparty_person_id,
13821
+ chart_account_id, cost_center_id, is_active)`).eq("status", "provisioned").eq(`${T.recurringBills}.direction`, direcao).eq(`${T.recurringBills}.is_active`, true);
13822
+ if (query.contactId) qb = qb.eq(`${T.recurringBills}.counterparty_person_id`, query.contactId);
13823
+ if (query.accountId) qb = qb.eq(`${T.recurringBills}.chart_account_id`, query.accountId);
13824
+ if (query.costCenterId) qb = qb.eq(`${T.recurringBills}.cost_center_id`, query.costCenterId);
13379
13825
  if (query.unitId) qb = qb.or(`unit_id.is.null,unit_id.eq.${query.unitId}`);
13380
13826
  if (query.dateRange) {
13381
13827
  qb = qb.gte("due_date", query.dateRange.from.slice(0, 10)).lte("due_date", query.dateRange.to.slice(0, 10));
@@ -13389,7 +13835,13 @@ function createSupabaseFinancialProvider() {
13389
13835
  const { data: people } = await pub.from("people").select("id,name").in("id", pessoaIds);
13390
13836
  for (const person of people ?? []) nomes.set(person.id, person.name);
13391
13837
  }
13392
- return linhas.map((row) => {
13838
+ const termo = query.search?.trim().toLocaleLowerCase("pt-BR");
13839
+ return linhas.filter((row) => {
13840
+ if (!termo) return true;
13841
+ const regua = row[T.recurringBills] ?? {};
13842
+ const nome = regua.counterparty_person_id ? nomes.get(regua.counterparty_person_id) : "";
13843
+ return [regua.description, regua.notes, nome].some((campo) => (campo ?? "").toLocaleLowerCase("pt-BR").includes(termo));
13844
+ }).map((row) => {
13393
13845
  const regua = row[T.recurringBills] ?? {};
13394
13846
  return {
13395
13847
  id: `occ:${row.id}`,
@@ -13988,10 +14440,10 @@ function createSupabaseFinancialProvider() {
13988
14440
  const amtScore = amount > 0 ? Math.max(0, 1 - amtDiff / amount) : 0;
13989
14441
  const refDate = r.due_date ?? r.payment_date;
13990
14442
  const days = lineDate && refDate ? Math.abs((new Date(lineDate).getTime() - new Date(refDate).getTime()) / 864e5) : 30;
13991
- const dateScore = Math.max(0, 1 - days / 30);
14443
+ const dateScore2 = Math.max(0, 1 - days / 30);
13992
14444
  return {
13993
14445
  movement: snakeToCamel(r),
13994
- score: round26(0.7 * amtScore + 0.3 * dateScore)
14446
+ score: round26(0.7 * amtScore + 0.3 * dateScore2)
13995
14447
  };
13996
14448
  }).sort((a, b) => b.score - a.score);
13997
14449
  const invoiceIds = [...new Set(candidates.map((c) => c.movement.invoiceId).filter(Boolean))];
@@ -14005,6 +14457,49 @@ function createSupabaseFinancialProvider() {
14005
14457
  }
14006
14458
  return candidates;
14007
14459
  },
14460
+ // ------------------------------------------------------------------
14461
+ // A SUGESTÃO QUE CHEGA SOZINHA, para a página inteira.
14462
+ //
14463
+ // DUAS perguntas ao banco para cinquenta linhas, e não duas por linha:
14464
+ //
14465
+ // 1. os lançamentos nativos em aberto cujo valor cabe em ALGUMA das
14466
+ // faixas de ±2% da página (um `or` só, com uma faixa por valor
14467
+ // distinto — o PostgREST aceita um `or` por consulta);
14468
+ // 2. o nome de quem, para os documentos desses lançamentos.
14469
+ //
14470
+ // O cruzamento é em memória (ver match-statement.ts): valor, nome e data,
14471
+ // e nenhum lançamento oferecido a duas linhas.
14472
+ // ------------------------------------------------------------------
14473
+ async suggestReconciliationBatch(lines) {
14474
+ const { core, pub } = getClients();
14475
+ const abertas = lines.filter((line) => !line.reconciledAt && (Number(line.amount) || 0) > 0);
14476
+ if (abertas.length === 0) return {};
14477
+ const faixas = [...new Set(abertas.map((line) => round26(line.amount)))].map((valor) => {
14478
+ const tol = Math.max(0.01, round26(valor * 0.02));
14479
+ return `and(amount.gte.${round26(valor - tol)},amount.lte.${round26(valor + tol)})`;
14480
+ });
14481
+ const direcoes = [...new Set(abertas.map((line) => line.direction))];
14482
+ const { data, error } = await pub.from(T.movements).select("*").is("external_source", null).is("reconciled_at", null).in("direction", direcoes).or(faixas.join(",")).limit(500);
14483
+ if (error) throw error;
14484
+ const movimentos = (data ?? []).map((r) => snakeToCamel(r));
14485
+ if (movimentos.length === 0) return {};
14486
+ const contexto = /* @__PURE__ */ new Map();
14487
+ const invoiceIds = [...new Set(movimentos.map((m) => m.invoiceId).filter(Boolean))];
14488
+ if (invoiceIds.length > 0) {
14489
+ const { data: orders } = await core.from("orders").select("id, reference_number, metadata").in("id", invoiceIds);
14490
+ const byId = new Map((orders ?? []).map((o) => [o.id, o]));
14491
+ for (const movimento of movimentos) {
14492
+ const order = movimento.invoiceId ? byId.get(movimento.invoiceId) : void 0;
14493
+ if (!order) continue;
14494
+ const contactName = order.metadata?.contactName;
14495
+ contexto.set(movimento.id, {
14496
+ contactName,
14497
+ invoice: { id: order.id, contactName, fiscalNumber: order.reference_number }
14498
+ });
14499
+ }
14500
+ }
14501
+ return matchStatementLines(abertas, movimentos, contexto);
14502
+ },
14008
14503
  async reconcileMovement(input) {
14009
14504
  if (input.mode === "settle" && input.matchedMovementId) {
14010
14505
  const line = await readStatementLine(input.bankMovementId);
@@ -14292,6 +14787,7 @@ function createFinancialStore(provider) {
14292
14787
  commissionsLoading: false,
14293
14788
  cashSessions: [],
14294
14789
  cashSessionsLoading: false,
14790
+ cashSessionModalOpen: false,
14295
14791
  paymentMethods: [],
14296
14792
  paymentMethodTypes: [],
14297
14793
  summary: null,
@@ -14500,6 +14996,9 @@ function createFinancialStore(provider) {
14500
14996
  toast.error("Failed to close session", { description: err?.message });
14501
14997
  throw err;
14502
14998
  }
14999
+ },
15000
+ setCashSessionModalOpen(open) {
15001
+ set({ cashSessionModalOpen: open });
14503
15002
  }
14504
15003
  }));
14505
15004
  }
@@ -14634,21 +15133,21 @@ function ChartOfAccountsTree({ entity, mockData, dataProvider, embedded }) {
14634
15133
  const canCreate = can("financial", "create");
14635
15134
  const canEdit = can("financial", "edit");
14636
15135
  const canDelete = can("financial", "delete");
14637
- const provider = React22__default.useMemo(
15136
+ const provider = React23__default.useMemo(
14638
15137
  () => dataProvider ?? resolveDataProvider(entity, mockData ?? []),
14639
15138
  [entity, mockData, dataProvider]
14640
15139
  );
14641
15140
  const table = entity.data?.table;
14642
- const [nodes, setNodes] = React22__default.useState(null);
14643
- const [loadError, setLoadError] = React22__default.useState(null);
14644
- const [expanded, setExpanded] = React22__default.useState(/* @__PURE__ */ new Set());
14645
- const [query, setQuery] = React22__default.useState("");
14646
- const [showInactive, setShowInactive] = React22__default.useState(true);
14647
- const [editing, setEditing] = React22__default.useState(null);
14648
- const [confirmDelete, setConfirmDelete] = React22__default.useState(null);
14649
- const [busy, setBusy] = React22__default.useState(false);
14650
- const initialized = React22__default.useRef(false);
14651
- const reload = React22__default.useCallback(async () => {
15141
+ const [nodes, setNodes] = React23__default.useState(null);
15142
+ const [loadError, setLoadError] = React23__default.useState(null);
15143
+ const [expanded, setExpanded] = React23__default.useState(/* @__PURE__ */ new Set());
15144
+ const [query, setQuery] = React23__default.useState("");
15145
+ const [showInactive, setShowInactive] = React23__default.useState(true);
15146
+ const [editing, setEditing] = React23__default.useState(null);
15147
+ const [confirmDelete, setConfirmDelete] = React23__default.useState(null);
15148
+ const [busy, setBusy] = React23__default.useState(false);
15149
+ const initialized = React23__default.useRef(false);
15150
+ const reload = React23__default.useCallback(async () => {
14652
15151
  try {
14653
15152
  const rows = await loadAll(provider);
14654
15153
  setNodes(rows);
@@ -14663,17 +15162,17 @@ function ChartOfAccountsTree({ entity, mockData, dataProvider, embedded }) {
14663
15162
  setNodes((prev) => prev ?? []);
14664
15163
  }
14665
15164
  }, [provider]);
14666
- React22__default.useEffect(() => {
15165
+ React23__default.useEffect(() => {
14667
15166
  void reload();
14668
15167
  }, [reload]);
14669
- const shown = React22__default.useMemo(
15168
+ const shown = React23__default.useMemo(
14670
15169
  () => (nodes ?? []).filter((n) => showInactive || n.isActive),
14671
15170
  [nodes, showInactive]
14672
15171
  );
14673
- const tree = React22__default.useMemo(() => buildChartTree(shown), [shown]);
14674
- const fullTree = React22__default.useMemo(() => buildChartTree(nodes ?? []), [nodes]);
14675
- const search = React22__default.useMemo(() => searchVisibleIds(tree, query), [tree, query]);
14676
- const groupIds = React22__default.useMemo(
15172
+ const tree = React23__default.useMemo(() => buildChartTree(shown), [shown]);
15173
+ const fullTree = React23__default.useMemo(() => buildChartTree(nodes ?? []), [nodes]);
15174
+ const search = React23__default.useMemo(() => searchVisibleIds(tree, query), [tree, query]);
15175
+ const groupIds = React23__default.useMemo(
14677
15176
  () => [...tree.byId.values()].filter((n) => isGroupNode(tree, n)).map((n) => n.id),
14678
15177
  [tree]
14679
15178
  );
@@ -14769,7 +15268,7 @@ function ChartOfAccountsTree({ entity, mockData, dataProvider, embedded }) {
14769
15268
  const ok = await run(() => provider.remove(node.id), t2("financial.chartTree.deleted", { name: node.name }));
14770
15269
  if (ok) setConfirmDelete(null);
14771
15270
  }
14772
- const parentOptions = React22__default.useCallback((q, selfId) => {
15271
+ const parentOptions = React23__default.useCallback((q, selfId) => {
14773
15272
  const blocked = selfId ? descendantIds(fullTree, selfId).add(selfId) : /* @__PURE__ */ new Set();
14774
15273
  const needle = q.trim().toLowerCase();
14775
15274
  return [...fullTree.byId.values()].filter((n) => !blocked.has(n.id) && isGroupNode(fullTree, n)).filter((n) => !needle || accountLabel(n).toLowerCase().includes(needle)).sort((a, b) => a.code.localeCompare(b.code, void 0, { numeric: true })).slice(0, 50).map((n) => ({ id: n.id, label: accountLabel(n) }));
@@ -15072,7 +15571,7 @@ var chartOfAccountsSeed = [
15072
15571
  { id: "coa-4.2", code: "4.2", name: "Utilities", nodeType: "leaf", accountType: "expense", parentId: "coa-4", isActive: true },
15073
15572
  { id: "coa-4.3", code: "4.3", name: "Payroll", nodeType: "leaf", accountType: "expense", parentId: "coa-4", isActive: true }
15074
15573
  ];
15075
- var ChartOfAccountsRegistryView = (props) => React22__default.createElement(ChartOfAccountsTree, { ...props, entity: chartOfAccountsEntity, mockData: chartOfAccountsSeed });
15574
+ var ChartOfAccountsRegistryView = (props) => React23__default.createElement(ChartOfAccountsTree, { ...props, entity: chartOfAccountsEntity, mockData: chartOfAccountsSeed });
15076
15575
  ChartOfAccountsRegistryView.displayName = "ChartOfAccountsRegistryView";
15077
15576
  var costCenterEntity = {
15078
15577
  name: "Cost Center",
@@ -16021,11 +16520,12 @@ function buildFinancialOnboarding() {
16021
16520
  function CashSessionHeaderControl({ config, provider, store: store6 }) {
16022
16521
  const sessions = useStore(store6, (s) => s.cashSessions);
16023
16522
  const fetchCashSessions = useStore(store6, (s) => s.fetchCashSessions);
16024
- const [open, setOpen] = React22__default.useState(false);
16025
- React22__default.useEffect(() => {
16523
+ const open = useStore(store6, (s) => s.cashSessionModalOpen);
16524
+ const setOpen = useStore(store6, (s) => s.setCashSessionModalOpen);
16525
+ React23__default.useEffect(() => {
16026
16526
  void fetchCashSessions?.();
16027
16527
  }, [fetchCashSessions]);
16028
- React22__default.useEffect(() => {
16528
+ React23__default.useEffect(() => {
16029
16529
  if (!open) return;
16030
16530
  const close = () => setOpen(false);
16031
16531
  window.addEventListener("hashchange", close);
@@ -16152,6 +16652,8 @@ var en = {
16152
16652
  "financial.invoiceForm.paidFrom": "Paid from",
16153
16653
  "financial.invoiceForm.selectMethod": "Select a method",
16154
16654
  "financial.invoiceForm.selectAccount": "Select an account",
16655
+ "financial.invoiceForm.selectMethodFirst": "Pick a method first",
16656
+ "financial.invoiceForm.scheduleSourceHint": "Method and account are set on each due date.",
16155
16657
  "financial.invoiceForm.paidNeedsMethod": "Tell us the payment method and the account",
16156
16658
  "financial.invoiceForm.willBePaid": "This installment will be settled",
16157
16659
  "financial.invoiceForm.clear": "Clear",
@@ -16532,6 +17034,7 @@ var en = {
16532
17034
  "financial.payment.selectAccount": "Select account...",
16533
17035
  "financial.payment.selectBrand": "Select brand...",
16534
17036
  "financial.payment.title": "Record Payment",
17037
+ "financial.payment.changeSource": "Change method and account",
16535
17038
  "financial.settings.allowPartial": "Allow partial payments",
16536
17039
  "financial.settings.allowPartialDesc": "Accept payments less than the full installment amount",
16537
17040
  "financial.settings.autoClose": "Auto-close at end of day",
@@ -16698,6 +17201,30 @@ var en = {
16698
17201
  "financial.cash.closeLabel": "Close",
16699
17202
  "financial.cash.diff": "Diff",
16700
17203
  "financial.cash.openAnother": "Open another register",
17204
+ "financial.cash.noOpenSessions": "No open cash sessions.",
17205
+ "financial.cash.tabOpen": "Open",
17206
+ "financial.cash.tabClosed": "Closed",
17207
+ "financial.cash.tabReview": "Review",
17208
+ "financial.cash.user": "User",
17209
+ "financial.cash.openingDate": "Opening Date",
17210
+ "financial.cash.accountLabel": "Cash Register Account",
17211
+ "financial.cash.review": "Review",
17212
+ "financial.cash.reviewModalTitle": "Review closing",
17213
+ "financial.cash.detailsModalTitle": "Session details",
17214
+ "financial.cash.summary": "Summary",
17215
+ "financial.cash.transactions": "Transactions",
17216
+ "financial.cash.noTransactions": "No transactions in this session.",
17217
+ "financial.cash.finalValue": "Final counted value",
17218
+ "financial.cash.movementCount": "Movements",
17219
+ "financial.cash.totalIn": "Total received",
17220
+ "financial.cash.totalOut": "Total withdrawn",
17221
+ "financial.cash.confirmAndApprove": "Confirm & approve",
17222
+ "financial.cash.details": "Details",
17223
+ "financial.cash.print": "Print",
17224
+ "financial.cash.verdictApproved": "Approved",
17225
+ "financial.cash.verdictRejected": "Rejected",
17226
+ "financial.cash.verdictPending": "Pending",
17227
+ "financial.cash.reviewedBy": "Reviewed by",
16701
17228
  "financial.invoice.statusOpen": "Open",
16702
17229
  "financial.invoice.statusPartial": "Partial",
16703
17230
  "financial.invoice.statusPaid": "Paid",
@@ -17014,6 +17541,8 @@ var ptBR = {
17014
17541
  "financial.invoiceForm.paidFrom": "Saiu de",
17015
17542
  "financial.invoiceForm.selectMethod": "Selecione a forma",
17016
17543
  "financial.invoiceForm.selectAccount": "Selecione a conta",
17544
+ "financial.invoiceForm.selectMethodFirst": "Escolha a forma primeiro",
17545
+ "financial.invoiceForm.scheduleSourceHint": "Forma e conta ficam configur\xE1veis em cada vencimento.",
17017
17546
  "financial.invoiceForm.paidNeedsMethod": "Diga a forma de pagamento e a conta",
17018
17547
  "financial.invoiceForm.willBePaid": "Esta parcela entra como paga",
17019
17548
  "financial.invoiceForm.clear": "Limpar",
@@ -17397,6 +17926,7 @@ var ptBR = {
17397
17926
  "financial.payment.selectAccount": "Selecionar conta...",
17398
17927
  "financial.payment.selectBrand": "Selecionar bandeira...",
17399
17928
  "financial.payment.title": "Registrar Pagamento",
17929
+ "financial.payment.changeSource": "Alterar forma e conta",
17400
17930
  "financial.settings.allowPartial": "Permitir pagamentos parciais",
17401
17931
  "financial.settings.allowPartialDesc": "Aceitar pagamentos menores que o valor total da parcela",
17402
17932
  "financial.settings.autoClose": "Fechamento autom\xE1tico no fim do dia",
@@ -17563,6 +18093,30 @@ var ptBR = {
17563
18093
  "financial.cash.closeLabel": "Fechamento",
17564
18094
  "financial.cash.diff": "Dif",
17565
18095
  "financial.cash.openAnother": "Abrir outro caixa",
18096
+ "financial.cash.noOpenSessions": "Nenhuma sess\xE3o de caixa aberta.",
18097
+ "financial.cash.tabOpen": "Abertos",
18098
+ "financial.cash.tabClosed": "Fechados",
18099
+ "financial.cash.tabReview": "Confer\xEAncia",
18100
+ "financial.cash.user": "Usu\xE1rio",
18101
+ "financial.cash.openingDate": "Data de Abertura",
18102
+ "financial.cash.accountLabel": "Conta do Caixa",
18103
+ "financial.cash.review": "Conferir",
18104
+ "financial.cash.reviewModalTitle": "Conferir fechamento",
18105
+ "financial.cash.detailsModalTitle": "Detalhes da sess\xE3o",
18106
+ "financial.cash.summary": "Resumo",
18107
+ "financial.cash.transactions": "Transa\xE7\xF5es",
18108
+ "financial.cash.noTransactions": "Nenhuma transa\xE7\xE3o nesta sess\xE3o.",
18109
+ "financial.cash.finalValue": "Valor final contado",
18110
+ "financial.cash.movementCount": "Movimenta\xE7\xF5es",
18111
+ "financial.cash.totalIn": "Total recebido",
18112
+ "financial.cash.totalOut": "Total retirado",
18113
+ "financial.cash.confirmAndApprove": "Confirmar e aprovar",
18114
+ "financial.cash.details": "Detalhes",
18115
+ "financial.cash.print": "Imprimir",
18116
+ "financial.cash.verdictApproved": "Aprovado",
18117
+ "financial.cash.verdictRejected": "Rejeitado",
18118
+ "financial.cash.verdictPending": "Pendente",
18119
+ "financial.cash.reviewedBy": "Conferido por",
17566
18120
  "financial.invoice.statusOpen": "Aberto",
17567
18121
  "financial.invoice.statusPartial": "Parcial",
17568
18122
  "financial.invoice.statusPaid": "Pago",
@@ -17886,14 +18440,14 @@ function CommissionRuleDialog({
17886
18440
  onClose,
17887
18441
  onSaved
17888
18442
  }) {
17889
- const [name, setName] = React22__default.useState(rule?.name ?? "");
17890
- const [description, setDescription] = React22__default.useState(rule?.description ?? "");
17891
- const [priority, setPriority] = React22__default.useState(String(rule?.priority ?? 1));
17892
- const [isActive, setIsActive] = React22__default.useState(rule?.isActive ?? true);
17893
- const [isDefault, setIsDefault] = React22__default.useState(rule?.isDefault ?? !hasDefault);
17894
- const [saving, setSaving] = React22__default.useState(false);
17895
- const [error, setError] = React22__default.useState(null);
17896
- const initial = React22__default.useMemo(() => {
18443
+ const [name, setName] = React23__default.useState(rule?.name ?? "");
18444
+ const [description, setDescription] = React23__default.useState(rule?.description ?? "");
18445
+ const [priority, setPriority] = React23__default.useState(String(rule?.priority ?? 1));
18446
+ const [isActive, setIsActive] = React23__default.useState(rule?.isActive ?? true);
18447
+ const [isDefault, setIsDefault] = React23__default.useState(rule?.isDefault ?? !hasDefault);
18448
+ const [saving, setSaving] = React23__default.useState(false);
18449
+ const [error, setError] = React23__default.useState(null);
18450
+ const initial = React23__default.useMemo(() => {
17897
18451
  const get = (t2, key = "ids") => rule?.criteria.find((c) => c.criteriaType === t2)?.criteriaValue?.[key] ?? [];
17898
18452
  return {
17899
18453
  services: get("service"),
@@ -17904,12 +18458,12 @@ function CommissionRuleDialog({
17904
18458
  ranges: get("time_range", "ranges")
17905
18459
  };
17906
18460
  }, [rule]);
17907
- const [services, setServices] = React22__default.useState(initial.services);
17908
- const [categories, setCategories] = React22__default.useState(initial.categories);
17909
- const [professionals, setProfessionals] = React22__default.useState(initial.professionals);
17910
- const [units, setUnits] = React22__default.useState(initial.units);
17911
- const [weekdays, setWeekdays] = React22__default.useState(initial.weekdays);
17912
- const [ranges, setRanges] = React22__default.useState(initial.ranges);
18461
+ const [services, setServices] = React23__default.useState(initial.services);
18462
+ const [categories, setCategories] = React23__default.useState(initial.categories);
18463
+ const [professionals, setProfessionals] = React23__default.useState(initial.professionals);
18464
+ const [units, setUnits] = React23__default.useState(initial.units);
18465
+ const [weekdays, setWeekdays] = React23__default.useState(initial.weekdays);
18466
+ const [ranges, setRanges] = React23__default.useState(initial.ranges);
17913
18467
  const criteriaCount = [services, categories, professionals, units].filter((a) => a.length > 0).length + (weekdays.length ? 1 : 0) + (ranges.length ? 1 : 0);
17914
18468
  async function handleSave() {
17915
18469
  if (!name.trim()) {
@@ -18152,11 +18706,11 @@ function blankFunction(order) {
18152
18706
  };
18153
18707
  }
18154
18708
  function CommissionFunctionsDialog({ rule, provider, onClose, onSaved }) {
18155
- const [functions, setFunctions] = React22__default.useState(
18709
+ const [functions, setFunctions] = React23__default.useState(
18156
18710
  rule.functions.length ? rule.functions : [blankFunction(0)]
18157
18711
  );
18158
- const [saving, setSaving] = React22__default.useState(false);
18159
- const [error, setError] = React22__default.useState(null);
18712
+ const [saving, setSaving] = React23__default.useState(false);
18713
+ const [error, setError] = React23__default.useState(null);
18160
18714
  function patch(i, changes) {
18161
18715
  setFunctions((fs) => fs.map((f, j) => j === i ? { ...f, ...changes } : f));
18162
18716
  }
@@ -18440,19 +18994,19 @@ function functionChip(fn) {
18440
18994
  return fn.valueType === "percentage" ? `${who}: ${fn.valueAmount}%` : `${who}: R$ ${fn.valueAmount.toFixed(2)}`;
18441
18995
  }
18442
18996
  function CommissionRulesView({ provider }) {
18443
- const [rules, setRules] = React22__default.useState(null);
18444
- const [catalogs, setCatalogs] = React22__default.useState({
18997
+ const [rules, setRules] = React23__default.useState(null);
18998
+ const [catalogs, setCatalogs] = React23__default.useState({
18445
18999
  services: [],
18446
19000
  categories: [],
18447
19001
  professionals: [],
18448
19002
  units: []
18449
19003
  });
18450
- const [error, setError] = React22__default.useState(null);
18451
- const [funnel, setFunnel] = React22__default.useState({});
18452
- const [editing, setEditing] = React22__default.useState(null);
18453
- const [functionsFor, setFunctionsFor] = React22__default.useState(null);
18454
- const [deleting, setDeleting] = React22__default.useState(null);
18455
- const load = React22__default.useCallback(async () => {
19004
+ const [error, setError] = React23__default.useState(null);
19005
+ const [funnel, setFunnel] = React23__default.useState({});
19006
+ const [editing, setEditing] = React23__default.useState(null);
19007
+ const [functionsFor, setFunctionsFor] = React23__default.useState(null);
19008
+ const [deleting, setDeleting] = React23__default.useState(null);
19009
+ const load = React23__default.useCallback(async () => {
18456
19010
  try {
18457
19011
  setError(null);
18458
19012
  const [r, c] = await Promise.all([
@@ -18466,17 +19020,17 @@ function CommissionRulesView({ provider }) {
18466
19020
  setRules([]);
18467
19021
  }
18468
19022
  }, [provider]);
18469
- React22__default.useEffect(() => {
19023
+ React23__default.useEffect(() => {
18470
19024
  void load();
18471
19025
  }, [load]);
18472
19026
  const funnelActive = Object.values(funnel).some(Boolean);
18473
- const visible = React22__default.useMemo(() => {
19027
+ const visible = React23__default.useMemo(() => {
18474
19028
  if (!rules) return [];
18475
19029
  const ranked = rankRules(rules);
18476
19030
  if (!funnelActive) return ranked;
18477
19031
  return ranked.filter((r) => r.isDefault || ruleMatches(r, funnel, catalogs));
18478
19032
  }, [rules, funnel, funnelActive, catalogs]);
18479
- const winner = React22__default.useMemo(() => {
19033
+ const winner = React23__default.useMemo(() => {
18480
19034
  if (!funnelActive) return null;
18481
19035
  return visible.find((r) => r.isActive && !r.isDefault) ?? visible.find((r) => r.isActive && r.isDefault) ?? null;
18482
19036
  }, [visible, funnelActive]);
@@ -18491,7 +19045,7 @@ function CommissionRulesView({ provider }) {
18491
19045
  await provider.duplicateCommissionRule?.(rule.id);
18492
19046
  await load();
18493
19047
  }
18494
- const headerActions = React22__default.useMemo(() => [{
19048
+ const headerActions = React23__default.useMemo(() => [{
18495
19049
  id: "new-rule",
18496
19050
  label: "Adicionar regra",
18497
19051
  icon: Plus,
@@ -19273,7 +19827,7 @@ function PersonStatementWidget({ person, provider, config }) {
19273
19827
  ] });
19274
19828
  }
19275
19829
  function SupplierFinancialSettings({ person, provider }) {
19276
- const initial = React22__default.useMemo(() => counterpartyAccountingPolicy(person), [person]);
19830
+ const initial = React23__default.useMemo(() => counterpartyAccountingPolicy(person), [person]);
19277
19831
  const [accounts, setAccounts] = useState([]);
19278
19832
  const [costCenters, setCostCenters] = useState([]);
19279
19833
  const [defaultAccountId, setDefaultAccountId] = useState(initial.defaultAccountId ?? "");
@@ -42473,8 +43027,8 @@ function ReceivePaymentModal({
42473
43027
  onClose,
42474
43028
  onPaid
42475
43029
  }) {
42476
- const provider = React22__default.useMemo(() => createSafeFinancialProvider(), []);
42477
- const store6 = React22__default.useMemo(() => createFinancialStore(provider), [provider]);
43030
+ const provider = React23__default.useMemo(() => createSafeFinancialProvider(), []);
43031
+ const store6 = React23__default.useMemo(() => createFinancialStore(provider), [provider]);
42478
43032
  return /* @__PURE__ */ jsx(FinancialContextProvider, { config: STANDALONE_CONFIG, provider, store: store6, children: /* @__PURE__ */ jsx(
42479
43033
  PaymentModal,
42480
43034
  {
@@ -42492,7 +43046,7 @@ function createFinancialSettingsPlugin(options) {
42492
43046
  ...financialRegistries.filter((r) => RECEIVING_REGISTRIES.includes(r.id)),
42493
43047
  ...options?.extraRegistries ?? []
42494
43048
  ];
42495
- const SettingsTab = () => React22__default.createElement(PluginSettingsPanel, {
43049
+ const SettingsTab = () => React23__default.createElement(PluginSettingsPanel, {
42496
43050
  title: "Pagamentos",
42497
43051
  registries,
42498
43052
  hostPluginId: "financial",
@@ -42549,25 +43103,25 @@ function ClientAccountPanel({
42549
43103
  currency = BRL
42550
43104
  }) {
42551
43105
  const provider = useFinancialProvider();
42552
- const [account, setAccount] = React22__default.useState(null);
42553
- const [busy, setBusy] = React22__default.useState(false);
42554
- const [viewInvoice, setViewInvoice] = React22__default.useState(null);
42555
- const [comprando, setComprando] = React22__default.useState(false);
42556
- const [buscaServico, setBuscaServico] = React22__default.useState("");
42557
- const [catalogo, setCatalogo] = React22__default.useState([]);
42558
- const [escolhidos, setEscolhidos] = React22__default.useState([]);
42559
- const [comprandoBusy, setComprandoBusy] = React22__default.useState(false);
42560
- const [erroCompra, setErroCompra] = React22__default.useState(null);
42561
- const load = React22__default.useCallback(async () => {
43106
+ const [account, setAccount] = React23__default.useState(null);
43107
+ const [busy, setBusy] = React23__default.useState(false);
43108
+ const [viewInvoice, setViewInvoice] = React23__default.useState(null);
43109
+ const [comprando, setComprando] = React23__default.useState(false);
43110
+ const [buscaServico, setBuscaServico] = React23__default.useState("");
43111
+ const [catalogo, setCatalogo] = React23__default.useState([]);
43112
+ const [escolhidos, setEscolhidos] = React23__default.useState([]);
43113
+ const [comprandoBusy, setComprandoBusy] = React23__default.useState(false);
43114
+ const [erroCompra, setErroCompra] = React23__default.useState(null);
43115
+ const load = React23__default.useCallback(async () => {
42562
43116
  if (!personId) return;
42563
43117
  setAccount(await provider.getClientAccount(personId));
42564
43118
  }, [personId, provider]);
42565
- React22__default.useEffect(() => {
43119
+ React23__default.useEffect(() => {
42566
43120
  void load();
42567
43121
  }, [load]);
42568
43122
  const forThis = account?.invoices.filter((i) => orderId && i.orderId === orderId) ?? [];
42569
43123
  const hasForThis = forThis.length > 0;
42570
- React22__default.useEffect(() => {
43124
+ React23__default.useEffect(() => {
42571
43125
  if (!comprando) return;
42572
43126
  let vivo = true;
42573
43127
  const t2 = setTimeout(async () => {
@@ -42828,8 +43382,8 @@ function ClientAccountPanel({
42828
43382
  ] });
42829
43383
  }
42830
43384
  function ClientAccountInline(props) {
42831
- const provider = React22__default.useMemo(() => createSafeFinancialProvider(), []);
42832
- const store6 = React22__default.useMemo(() => createFinancialStore(provider), [provider]);
43385
+ const provider = React23__default.useMemo(() => createSafeFinancialProvider(), []);
43386
+ const store6 = React23__default.useMemo(() => createFinancialStore(provider), [provider]);
42833
43387
  return /* @__PURE__ */ jsx(FinancialContextProvider, { config: STANDALONE_CONFIG, provider, store: store6, children: /* @__PURE__ */ jsx(ClientAccountPanel, { ...props }) });
42834
43388
  }
42835
43389
  function ClientAccountModal({
@@ -42951,11 +43505,11 @@ function createFinancialPlugin(options) {
42951
43505
  const settingsRegistries = registries.filter((r) => r.id !== "bank-accounts");
42952
43506
  const AccountsRegistryPage = createCrudPage(financialAccountEntity, { feature: "financial" });
42953
43507
  AccountsRegistryPage.displayName = "FinancialAccountsRegistryPage";
42954
- const PersonStatementBound = (props) => React22__default.createElement(PersonStatementWidget, { person: props.item, provider, config });
43508
+ const PersonStatementBound = (props) => React23__default.createElement(PersonStatementWidget, { person: props.item, provider, config });
42955
43509
  PersonStatementBound.displayName = "FinancialPersonStatement";
42956
- const SupplierFinancialSettingsBound = (props) => React22__default.createElement(SupplierFinancialSettings, { person: props.item, provider });
43510
+ const SupplierFinancialSettingsBound = (props) => React23__default.createElement(SupplierFinancialSettings, { person: props.item, provider });
42957
43511
  SupplierFinancialSettingsBound.displayName = "FinancialSupplierSettings";
42958
- const PageComponent = () => React22__default.createElement(FinancialPage, { config, provider, store: store6, registries });
43512
+ const PageComponent = () => React23__default.createElement(FinancialPage, { config, provider, store: store6, registries });
42959
43513
  const AgentMount = createFinancialAgentMount({ config, provider });
42960
43514
  return {
42961
43515
  id: "financial",
@@ -43013,7 +43567,7 @@ function createFinancialPlugin(options) {
43013
43567
  // tem — lá é um ícone no cabeçalho, de qualquer tela.
43014
43568
  headerControls: config.modules.cashRegisters ? [{
43015
43569
  id: "financial.cash-session",
43016
- component: (() => React22__default.createElement(CashSessionHeaderControl, { config, provider, store: store6 })),
43570
+ component: (() => React23__default.createElement(CashSessionHeaderControl, { config, provider, store: store6 })),
43017
43571
  order: 90,
43018
43572
  permission: { feature: "financial", action: "read" }
43019
43573
  }] : [],
@@ -43244,9 +43798,9 @@ function createFinancialPlugin(options) {
43244
43798
  // lado, e a corrida de "Plugins" não é partida ao meio.
43245
43799
  group: "Financeiro",
43246
43800
  component: (() => {
43247
- const FinancialSettingsTab = () => React22__default.createElement(PluginSettingsPanel, {
43801
+ const FinancialSettingsTab = () => React23__default.createElement(PluginSettingsPanel, {
43248
43802
  title: "Financial Settings",
43249
- generalSettings: React22__default.createElement(FinancialGeneralSettings, { config }),
43803
+ generalSettings: React23__default.createElement(FinancialGeneralSettings, { config }),
43250
43804
  registries: settingsRegistries,
43251
43805
  hostPluginId: "financial",
43252
43806
  routeBase: "/settings/financial"
@@ -43271,7 +43825,7 @@ function createFinancialPlugin(options) {
43271
43825
  icon: "HandCoins",
43272
43826
  group: "Financeiro",
43273
43827
  component: (() => {
43274
- const CommissionRulesTab = () => React22__default.createElement(CommissionRulesView, { provider });
43828
+ const CommissionRulesTab = () => React23__default.createElement(CommissionRulesView, { provider });
43275
43829
  CommissionRulesTab.displayName = "CommissionRulesTab";
43276
43830
  return CommissionRulesTab;
43277
43831
  })(),