@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/components/CashSessionHeaderControl.d.ts.map +1 -1
- package/dist/components/CashSessionReviewModal.d.ts +11 -0
- package/dist/components/CashSessionReviewModal.d.ts.map +1 -0
- package/dist/components/InstallmentSource.d.ts +21 -0
- package/dist/components/InstallmentSource.d.ts.map +1 -0
- package/dist/components/PaymentFields.d.ts +28 -1
- package/dist/components/PaymentFields.d.ts.map +1 -1
- package/dist/components/PaymentModal.d.ts.map +1 -1
- package/dist/components/ReconcileFlow.d.ts.map +1 -1
- package/dist/data/match-statement.d.ts +32 -0
- package/dist/data/match-statement.d.ts.map +1 -0
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/types.d.ts +12 -0
- package/dist/data/types.d.ts.map +1 -1
- package/dist/index.js +1385 -831
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/store.d.ts +5 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/views/CashRegistersView.d.ts.map +1 -1
- package/dist/views/InvoiceDetailView.d.ts.map +1 -1
- package/dist/views/InvoiceFormView.d.ts.map +1 -1
- package/dist/views/ReconciliationView.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
841
|
+
const live = React23.useRef(onOpenDraft);
|
|
842
842
|
live.current = onOpenDraft;
|
|
843
|
-
const contribution =
|
|
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] =
|
|
899
|
-
const [bankAccountId, setBankAccountId] =
|
|
900
|
-
const [draft, setDraft] =
|
|
901
|
-
const [saving, setSaving] =
|
|
902
|
-
const [saveError, setSaveError] =
|
|
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 =
|
|
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 =
|
|
2906
|
+
const live = React23.useRef(draft);
|
|
2907
2907
|
live.current = draft;
|
|
2908
|
-
const lookup =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
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 =
|
|
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
|
-
|
|
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] =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
4252
|
-
|
|
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
|
-
|
|
4281
|
-
|
|
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: "
|
|
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(
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
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: "
|
|
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] =
|
|
5182
|
-
|
|
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__ */
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
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__ */
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
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 [
|
|
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
|
|
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
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
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
|
-
|
|
5945
|
-
/* @__PURE__ */
|
|
5946
|
-
|
|
5947
|
-
"
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
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
|
-
|
|
6229
|
+
CurrencyInput,
|
|
5992
6230
|
{
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
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", {
|
|
6000
|
-
/* @__PURE__ */ jsx("
|
|
6001
|
-
/* @__PURE__ */ jsx(
|
|
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: () =>
|
|
6033
|
-
disabled:
|
|
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(
|
|
6260
|
+
/* @__PURE__ */ jsx(Inbox, { className: "h-3 w-3" }),
|
|
6037
6261
|
" ",
|
|
6038
|
-
t2("financial.cash.
|
|
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({
|
|
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 [
|
|
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(
|
|
6237
|
-
/* @__PURE__ */
|
|
6238
|
-
|
|
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
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
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
|
-
|
|
6341
|
-
|
|
6342
|
-
inPanel ? /* @__PURE__ */ jsxs(
|
|
6343
|
-
"a",
|
|
6522
|
+
expandedClosing === session.id ? /* @__PURE__ */ jsx(CashClosingPanel, { session, currency, onClose: handleClose }) : /* @__PURE__ */ jsxs(
|
|
6523
|
+
"button",
|
|
6344
6524
|
{
|
|
6345
|
-
|
|
6346
|
-
|
|
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(
|
|
6349
|
-
|
|
6350
|
-
|
|
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
|
-
)
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
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
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
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
|
-
|
|
6371
|
-
t2("financial.cash.
|
|
6372
|
-
" ",
|
|
6373
|
-
session.
|
|
6558
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
6559
|
+
t2("financial.cash.closeLabel"),
|
|
6560
|
+
": ",
|
|
6561
|
+
formatCurrency(session.closingBalance ?? 0, currency)
|
|
6374
6562
|
] })
|
|
6375
6563
|
] }),
|
|
6376
|
-
/* @__PURE__ */ jsxs("
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
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 =
|
|
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] =
|
|
7022
|
-
const [to, setTo] =
|
|
7023
|
-
const [preset, setPreset] =
|
|
7024
|
-
const [running, setRunning] =
|
|
7025
|
-
const [lastRun, setLastRun] =
|
|
7026
|
-
const [generation, setGeneration] =
|
|
7027
|
-
const [busy, setBusy] =
|
|
7028
|
-
const [error, setError] =
|
|
7029
|
-
const loadGeneration =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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] =
|
|
7543
|
-
const [unitId, setUnitId] =
|
|
7544
|
-
const [year, setYear] =
|
|
7545
|
-
const [rows, setRows] =
|
|
7546
|
-
const [editing, setEditing] =
|
|
7547
|
-
const [error, setError] =
|
|
7548
|
-
|
|
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 =
|
|
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
|
-
|
|
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] =
|
|
7699
|
-
const [sup, setSup] =
|
|
7700
|
-
const [hyper, setHyper] =
|
|
7701
|
-
const [saving, setSaving] =
|
|
7702
|
-
const [error, setError] =
|
|
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] =
|
|
7794
|
-
const [cards, setCards] =
|
|
7795
|
-
const [error, setError] =
|
|
7796
|
-
const [issuing, setIssuing] =
|
|
7797
|
-
const [issued, setIssued] =
|
|
7798
|
-
const [activating, setActivating] =
|
|
7799
|
-
const [search, setSearch] =
|
|
7800
|
-
const [state, setState] =
|
|
7801
|
-
const [minValue, setMinValue] =
|
|
7802
|
-
const [maxValue, setMaxValue] =
|
|
7803
|
-
const [from, setFrom] =
|
|
7804
|
-
const [to, setTo] =
|
|
7805
|
-
const money3 =
|
|
7806
|
-
const seq =
|
|
7807
|
-
const load =
|
|
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
|
-
|
|
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
|
-
|
|
8091
|
+
React23__default.useEffect(() => {
|
|
7838
8092
|
if (enabled) void load();
|
|
7839
8093
|
}, [enabled, load]);
|
|
7840
|
-
const columns =
|
|
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] =
|
|
8022
|
-
const [amount, setAmount] =
|
|
8023
|
-
const [expiresAt, setExpiresAt] =
|
|
8024
|
-
const [note, setNote] =
|
|
8025
|
-
const [saving, setSaving] =
|
|
8026
|
-
const [error, setError] =
|
|
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-
|
|
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:
|
|
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
|
|
9519
|
+
setApplied({ accountId, dateFrom, dateTo, direction, status });
|
|
9243
9520
|
}
|
|
9244
9521
|
const filtrosPendentes = useMemo(
|
|
9245
|
-
() => JSON.stringify({ accountId, dateFrom, dateTo, direction, status
|
|
9246
|
-
[accountId, dateFrom, dateTo, direction, status,
|
|
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 =
|
|
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 =
|
|
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")
|
|
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
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
/* @__PURE__ */ jsx(
|
|
9531
|
-
t2("financial.reconciliation.
|
|
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
|
-
|
|
9534
|
-
isCredit ?
|
|
9535
|
-
|
|
9536
|
-
|
|
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: "
|
|
9540
|
-
/* @__PURE__ */ jsxs("
|
|
9541
|
-
|
|
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
|
-
|
|
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", {
|
|
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
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
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, {
|
|
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.
|
|
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__ */
|
|
9607
|
-
|
|
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
|
-
|
|
9613
|
-
|
|
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(
|
|
9616
|
-
t2("financial.reconciliation.
|
|
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(
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
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] =
|
|
9675
|
-
const [search, setSearch] =
|
|
9676
|
-
const [status, setStatus] =
|
|
9677
|
-
const [changing, setChanging] =
|
|
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 =
|
|
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
|
-
|
|
10005
|
+
React23__default.useEffect(() => {
|
|
9690
10006
|
void load();
|
|
9691
10007
|
}, [load]);
|
|
9692
|
-
const filtered =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
9821
|
-
const [loaded, setLoaded] =
|
|
9822
|
-
const [contact, setContact] =
|
|
9823
|
-
const [counterpartyAccounting, setCounterpartyAccounting] =
|
|
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 =
|
|
9829
|
-
const counterpartyLookup =
|
|
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 =
|
|
9837
|
-
const [items, setItems] =
|
|
9838
|
-
const [expandedItemId, setExpandedItemId] =
|
|
9839
|
-
const [addingItem, setAddingItem] =
|
|
9840
|
-
const [frequency, setFrequency] =
|
|
9841
|
-
const [dueDay, setDueDay] =
|
|
9842
|
-
const [startsOn, setStartsOn] =
|
|
9843
|
-
const [endsOn, setEndsOn] =
|
|
9844
|
-
const [unitId, setUnitId] =
|
|
9845
|
-
const [availableLocations, setAvailableLocations] =
|
|
9846
|
-
const [notes, setNotes] =
|
|
9847
|
-
const [isActive, setIsActive] =
|
|
9848
|
-
const [accounts, setAccounts] =
|
|
9849
|
-
const [costCenters, setCostCenters] =
|
|
9850
|
-
const [saving, setSaving] =
|
|
9851
|
-
const [ending, setEnding] =
|
|
9852
|
-
const [error, setError] =
|
|
9853
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
9938
|
-
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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
|
|
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 *
|
|
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,
|
|
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
|
-
|
|
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
|
|
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 *
|
|
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 =
|
|
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] =
|
|
14643
|
-
const [loadError, setLoadError] =
|
|
14644
|
-
const [expanded, setExpanded] =
|
|
14645
|
-
const [query, setQuery] =
|
|
14646
|
-
const [showInactive, setShowInactive] =
|
|
14647
|
-
const [editing, setEditing] =
|
|
14648
|
-
const [confirmDelete, setConfirmDelete] =
|
|
14649
|
-
const [busy, setBusy] =
|
|
14650
|
-
const initialized =
|
|
14651
|
-
const reload =
|
|
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
|
-
|
|
15165
|
+
React23__default.useEffect(() => {
|
|
14667
15166
|
void reload();
|
|
14668
15167
|
}, [reload]);
|
|
14669
|
-
const shown =
|
|
15168
|
+
const shown = React23__default.useMemo(
|
|
14670
15169
|
() => (nodes ?? []).filter((n) => showInactive || n.isActive),
|
|
14671
15170
|
[nodes, showInactive]
|
|
14672
15171
|
);
|
|
14673
|
-
const tree =
|
|
14674
|
-
const fullTree =
|
|
14675
|
-
const search =
|
|
14676
|
-
const groupIds =
|
|
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 =
|
|
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) =>
|
|
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
|
|
16025
|
-
|
|
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
|
-
|
|
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] =
|
|
17890
|
-
const [description, setDescription] =
|
|
17891
|
-
const [priority, setPriority] =
|
|
17892
|
-
const [isActive, setIsActive] =
|
|
17893
|
-
const [isDefault, setIsDefault] =
|
|
17894
|
-
const [saving, setSaving] =
|
|
17895
|
-
const [error, setError] =
|
|
17896
|
-
const initial =
|
|
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] =
|
|
17908
|
-
const [categories, setCategories] =
|
|
17909
|
-
const [professionals, setProfessionals] =
|
|
17910
|
-
const [units, setUnits] =
|
|
17911
|
-
const [weekdays, setWeekdays] =
|
|
17912
|
-
const [ranges, setRanges] =
|
|
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] =
|
|
18709
|
+
const [functions, setFunctions] = React23__default.useState(
|
|
18156
18710
|
rule.functions.length ? rule.functions : [blankFunction(0)]
|
|
18157
18711
|
);
|
|
18158
|
-
const [saving, setSaving] =
|
|
18159
|
-
const [error, setError] =
|
|
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] =
|
|
18444
|
-
const [catalogs, setCatalogs] =
|
|
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] =
|
|
18451
|
-
const [funnel, setFunnel] =
|
|
18452
|
-
const [editing, setEditing] =
|
|
18453
|
-
const [functionsFor, setFunctionsFor] =
|
|
18454
|
-
const [deleting, setDeleting] =
|
|
18455
|
-
const load =
|
|
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
|
-
|
|
19023
|
+
React23__default.useEffect(() => {
|
|
18470
19024
|
void load();
|
|
18471
19025
|
}, [load]);
|
|
18472
19026
|
const funnelActive = Object.values(funnel).some(Boolean);
|
|
18473
|
-
const visible =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
42477
|
-
const store6 =
|
|
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 = () =>
|
|
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] =
|
|
42553
|
-
const [busy, setBusy] =
|
|
42554
|
-
const [viewInvoice, setViewInvoice] =
|
|
42555
|
-
const [comprando, setComprando] =
|
|
42556
|
-
const [buscaServico, setBuscaServico] =
|
|
42557
|
-
const [catalogo, setCatalogo] =
|
|
42558
|
-
const [escolhidos, setEscolhidos] =
|
|
42559
|
-
const [comprandoBusy, setComprandoBusy] =
|
|
42560
|
-
const [erroCompra, setErroCompra] =
|
|
42561
|
-
const load =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
42832
|
-
const store6 =
|
|
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) =>
|
|
43508
|
+
const PersonStatementBound = (props) => React23__default.createElement(PersonStatementWidget, { person: props.item, provider, config });
|
|
42955
43509
|
PersonStatementBound.displayName = "FinancialPersonStatement";
|
|
42956
|
-
const SupplierFinancialSettingsBound = (props) =>
|
|
43510
|
+
const SupplierFinancialSettingsBound = (props) => React23__default.createElement(SupplierFinancialSettings, { person: props.item, provider });
|
|
42957
43511
|
SupplierFinancialSettingsBound.displayName = "FinancialSupplierSettings";
|
|
42958
|
-
const PageComponent = () =>
|
|
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: (() =>
|
|
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 = () =>
|
|
43801
|
+
const FinancialSettingsTab = () => React23__default.createElement(PluginSettingsPanel, {
|
|
43248
43802
|
title: "Financial Settings",
|
|
43249
|
-
generalSettings:
|
|
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 = () =>
|
|
43828
|
+
const CommissionRulesTab = () => React23__default.createElement(CommissionRulesView, { provider });
|
|
43275
43829
|
CommissionRulesTab.displayName = "CommissionRulesTab";
|
|
43276
43830
|
return CommissionRulesTab;
|
|
43277
43831
|
})(),
|