@fayz-ai/plugin-financial 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/components/ReceiptPicker.d.ts +10 -0
- package/dist/components/ReceiptPicker.d.ts.map +1 -0
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/registries.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +545 -441
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts.d.ts +16 -0
- package/dist/lib/accounts.d.ts.map +1 -0
- package/dist/lib/settlements.d.ts +22 -0
- package/dist/lib/settlements.d.ts.map +1 -0
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +4 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/CommissionsView.d.ts +7 -0
- package/dist/views/CommissionsView.d.ts.map +1 -1
- package/dist/views/FinancialPage.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/InvoiceListView.d.ts.map +1 -1
- package/package.json +5 -4
- package/dist/components/ReceiptField.d.ts +0 -17
- package/dist/components/ReceiptField.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React17, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
|
2
2
|
import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, createSafeDataProvider, registerTranslations, registerMessageDefaults, useTranslation, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
|
|
3
|
-
import { toast, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, DataTable, PageTransition, ModulePage, Button, DashboardCanvas, SubpageHeader, Skeleton, PageHeaderActions, Card, FilterChip, DatePicker, Input, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, CurrencyInput, useSaveBar, SearchSelect, ListView, KpiCard, ChartWidget, CardHeader, CardTitle, CardContent, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
|
|
4
|
-
import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, PermissionGate, PersonLink, useLimitGuard, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, OrderDetailSkeleton, OrderDetailBlock, OrderDetailView, QuickActionsButton, invalidateLimit } from '@fayz-ai/admin';
|
|
5
|
-
import { Wallet, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, Plus, FileText, FileCheck2, ExternalLink, Send, Ban, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, ChevronDown, Clock, Paperclip, CheckCheck, X, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, CircleAlert, CircleEllipsis, Pencil, CalendarDays, LayoutList, Layers,
|
|
3
|
+
import { toast, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, DataTable, PageTransition, ModulePage, Button, DashboardCanvas, SubpageHeader, Skeleton, PageHeaderActions, Card, FilterChip, DatePicker, Input, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, CurrencyInput, useSaveBar, SearchSelect, ListView, ConfirmDialog, KpiCard, ChartWidget, CardHeader, CardTitle, CardContent, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
|
|
4
|
+
import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, PermissionGate, PersonLink, useLimitGuard, useLookupCreate, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, OrderDetailSkeleton, OrderDetailBlock, OrderDetailView, QuickActionsButton, invalidateLimit } from '@fayz-ai/admin';
|
|
5
|
+
import { Wallet, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, Plus, FileText, FileCheck2, ExternalLink, Send, Ban, CalendarClock, Users, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, ChevronDown, Clock, Paperclip, CheckCheck, X, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, Camera, CircleAlert, CircleEllipsis, Pencil, CalendarDays, LayoutList, Layers, Building2, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
import { createPortal } from 'react-dom';
|
|
8
8
|
import { createStore as createStore$1 } from 'zustand/vanilla';
|
|
@@ -104,22 +104,22 @@ function MobileSummary() {
|
|
|
104
104
|
}, [statement]);
|
|
105
105
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
106
106
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
107
|
-
/* @__PURE__ */ jsxs("div", { className: "col-span-2 rounded-
|
|
107
|
+
/* @__PURE__ */ jsxs("div", { className: "col-span-2 rounded-card border bg-card p-4", children: [
|
|
108
108
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.summary.totalBalance") }),
|
|
109
109
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-2xl font-semibold", children: fmt(summary?.totalBalance ?? 0) })
|
|
110
110
|
] }),
|
|
111
|
-
/* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
111
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card p-3", children: [
|
|
112
112
|
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.summary.income") }),
|
|
113
113
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-base font-semibold text-success", children: fmt(inflow) })
|
|
114
114
|
] }),
|
|
115
|
-
/* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
115
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card p-3", children: [
|
|
116
116
|
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.summary.expenses") }),
|
|
117
117
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-base font-semibold text-destructive", children: fmt(outflow) })
|
|
118
118
|
] })
|
|
119
119
|
] }),
|
|
120
120
|
groups.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-dashed py-12 text-center text-sm text-muted-foreground", children: t2("financial.summary.noTransactions") }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: groups.map(([date, rows]) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
121
121
|
/* @__PURE__ */ jsx("p", { className: "px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: dateLabel(date) }),
|
|
122
|
-
/* @__PURE__ */ jsx("div", { className: "divide-y overflow-hidden rounded-
|
|
122
|
+
/* @__PURE__ */ jsx("div", { className: "divide-y overflow-hidden rounded-card border bg-card", children: rows.map((e) => {
|
|
123
123
|
const isCredit = e.movement.direction === "credit";
|
|
124
124
|
const title = e.invoice?.contactName || e.movement.notes || t2("financial.summary.transactionsTitle");
|
|
125
125
|
const Icon = categoryIcon(title);
|
|
@@ -159,6 +159,15 @@ function MobileSummary() {
|
|
|
159
159
|
] }, date)) })
|
|
160
160
|
] });
|
|
161
161
|
}
|
|
162
|
+
|
|
163
|
+
// src/lib/accounts.ts
|
|
164
|
+
function postableAccounts(accounts) {
|
|
165
|
+
const parentIds = new Set(accounts.map((a) => a.parentId).filter(Boolean));
|
|
166
|
+
return accounts.filter((a) => a.nodeType !== "group" && !parentIds.has(a.id));
|
|
167
|
+
}
|
|
168
|
+
function accountLabel(account) {
|
|
169
|
+
return `${account.code} - ${account.name}`;
|
|
170
|
+
}
|
|
162
171
|
var STATUS_OPTIONS = [
|
|
163
172
|
{ value: "open", labelKey: "financial.invoice.statusOpen", color: "bg-info-soft text-info-soft-foreground", icon: CircleDashed },
|
|
164
173
|
{ value: "partial", labelKey: "financial.invoice.statusPartial", color: "bg-warning-soft text-warning-soft-foreground", icon: CircleEllipsis },
|
|
@@ -248,6 +257,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
248
257
|
const t2 = useTranslation();
|
|
249
258
|
const can = usePermissionOptional();
|
|
250
259
|
const canCreate = can("financial", "create");
|
|
260
|
+
const canEdit = can("financial", "edit");
|
|
251
261
|
const onNewGated = canCreate ? onNew : void 0;
|
|
252
262
|
const onNewReceiptGated = canCreate ? onNewReceipt : void 0;
|
|
253
263
|
const { currency } = useFinancialConfig();
|
|
@@ -256,11 +266,13 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
256
266
|
useFinancialStore((s) => s.invoicesTotal);
|
|
257
267
|
const invoicesLoading = useFinancialStore((s) => s.invoicesLoading);
|
|
258
268
|
const fetchInvoices = useFinancialStore((s) => s.fetchInvoices);
|
|
269
|
+
const fetchSummary = useFinancialStore((s) => s.fetchSummary);
|
|
259
270
|
const [search, setSearch] = useState("");
|
|
260
271
|
const [statusFilter, setStatusFilter] = useState([]);
|
|
261
272
|
const [accountId, setAccountId] = useState("");
|
|
262
273
|
const [costCenterId, setCostCenterId] = useState("");
|
|
263
274
|
const [accounts, setAccounts] = useState([]);
|
|
275
|
+
const postable = useMemo(() => postableAccounts(accounts), [accounts]);
|
|
264
276
|
const [costCenters, setCostCenters] = useState([]);
|
|
265
277
|
const [datePreset, setDatePreset] = useState("all");
|
|
266
278
|
const [dateFrom, setDateFrom] = useState("");
|
|
@@ -296,16 +308,17 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
296
308
|
active = false;
|
|
297
309
|
};
|
|
298
310
|
}, [provider]);
|
|
311
|
+
const query = useMemo(() => ({
|
|
312
|
+
direction,
|
|
313
|
+
status: statusFilter.length > 0 ? statusFilter : void 0,
|
|
314
|
+
search: search || void 0,
|
|
315
|
+
accountId: accountId || void 0,
|
|
316
|
+
costCenterId: costCenterId || void 0,
|
|
317
|
+
dateRange
|
|
318
|
+
}), [direction, statusFilter, search, accountId, costCenterId, dateRange]);
|
|
299
319
|
useEffect(() => {
|
|
300
|
-
fetchInvoices(
|
|
301
|
-
|
|
302
|
-
status: statusFilter.length > 0 ? statusFilter : void 0,
|
|
303
|
-
search: search || void 0,
|
|
304
|
-
accountId: accountId || void 0,
|
|
305
|
-
costCenterId: costCenterId || void 0,
|
|
306
|
-
dateRange
|
|
307
|
-
});
|
|
308
|
-
}, [direction, statusFilter, search, accountId, costCenterId, dateRange]);
|
|
320
|
+
fetchInvoices(query);
|
|
321
|
+
}, [query]);
|
|
309
322
|
const filtered = invoices.filter((inv) => inv.direction === direction);
|
|
310
323
|
const columns = useInvoiceColumns(currency);
|
|
311
324
|
const tags = useMemo(
|
|
@@ -324,6 +337,46 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
324
337
|
openAmount = totalAmount - paidAmount;
|
|
325
338
|
return { totalAmount, paidAmount, openAmount };
|
|
326
339
|
}, [filtered]);
|
|
340
|
+
const [cancelTarget, setCancelTarget] = useState(null);
|
|
341
|
+
const [bulkBusy, setBulkBusy] = useState(false);
|
|
342
|
+
const confirmBulkCancel = useCallback(async () => {
|
|
343
|
+
const targets = (cancelTarget ?? []).filter((inv) => inv.status !== "cancelled");
|
|
344
|
+
setBulkBusy(true);
|
|
345
|
+
let done = 0;
|
|
346
|
+
let failed = 0;
|
|
347
|
+
for (const invoice of targets) {
|
|
348
|
+
try {
|
|
349
|
+
await provider.cancelInvoice(invoice.id);
|
|
350
|
+
done++;
|
|
351
|
+
} catch (err) {
|
|
352
|
+
failed++;
|
|
353
|
+
console.warn("[financial] cancelInvoice failed", invoice.id, err);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
setBulkBusy(false);
|
|
357
|
+
setCancelTarget(null);
|
|
358
|
+
await Promise.all([fetchInvoices(query), fetchSummary()]);
|
|
359
|
+
if (done > 0) toast.success(t2("financial.invoice.bulkCancelDone", { count: String(done) }));
|
|
360
|
+
if (failed > 0) toast.error(t2("financial.invoice.bulkCancelFailed", { count: String(failed) }));
|
|
361
|
+
}, [cancelTarget, provider, fetchInvoices, fetchSummary, query, t2]);
|
|
362
|
+
const bulkActions = useMemo(() => {
|
|
363
|
+
if (!canEdit) return void 0;
|
|
364
|
+
return [{
|
|
365
|
+
id: "cancel",
|
|
366
|
+
label: t2("financial.invoice.bulkCancel"),
|
|
367
|
+
icon: Ban,
|
|
368
|
+
destructive: true,
|
|
369
|
+
disabled: bulkBusy,
|
|
370
|
+
onSelect: (rows) => {
|
|
371
|
+
const targets = rows.filter((inv) => inv.status !== "cancelled");
|
|
372
|
+
if (targets.length === 0) {
|
|
373
|
+
toast.error(t2("financial.invoice.bulkCancelNone"));
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
setCancelTarget(targets);
|
|
377
|
+
}
|
|
378
|
+
}];
|
|
379
|
+
}, [canEdit, bulkBusy, t2]);
|
|
327
380
|
const receivedLabel = direction === "debit" ? t2("financial.invoice.summaryPaid") : t2("financial.invoice.summaryReceived");
|
|
328
381
|
const pendingLabel = direction === "debit" ? t2("financial.invoice.summaryPayable") : t2("financial.invoice.summaryReceivable");
|
|
329
382
|
const exportRows = () => {
|
|
@@ -375,7 +428,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
375
428
|
children: t2("financial.invoice.filterClear")
|
|
376
429
|
}
|
|
377
430
|
),
|
|
378
|
-
|
|
431
|
+
postable.length > 0 && /* @__PURE__ */ jsx(
|
|
379
432
|
FilterChip,
|
|
380
433
|
{
|
|
381
434
|
icon: Landmark,
|
|
@@ -383,9 +436,9 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
383
436
|
allLabel: t2("financial.invoice.filterAllAccounts"),
|
|
384
437
|
value: accountId || void 0,
|
|
385
438
|
onChange: (next) => setAccountId(next ?? ""),
|
|
386
|
-
options:
|
|
439
|
+
options: postable.map((account) => ({
|
|
387
440
|
value: account.id,
|
|
388
|
-
label:
|
|
441
|
+
label: accountLabel(account)
|
|
389
442
|
}))
|
|
390
443
|
}
|
|
391
444
|
),
|
|
@@ -446,6 +499,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
446
499
|
onExport: filtered.length > 0 ? exportRows : void 0,
|
|
447
500
|
exportLabel: t2("crud.export.action"),
|
|
448
501
|
headerActions: newActions,
|
|
502
|
+
bulkActions,
|
|
449
503
|
onRowClick: (row) => onEdit?.(row.id),
|
|
450
504
|
emptyIcon: FileText,
|
|
451
505
|
emptyMessage: t2("financial.invoice.noInvoices"),
|
|
@@ -468,7 +522,21 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
468
522
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: pendingLabel }),
|
|
469
523
|
/* @__PURE__ */ jsx("span", { className: "font-semibold text-warning", children: formatCurrency(summary.openAmount, currency) })
|
|
470
524
|
] })
|
|
471
|
-
] })
|
|
525
|
+
] }),
|
|
526
|
+
/* @__PURE__ */ jsx(
|
|
527
|
+
ConfirmDialog,
|
|
528
|
+
{
|
|
529
|
+
open: cancelTarget !== null,
|
|
530
|
+
variant: "destructive",
|
|
531
|
+
title: t2("financial.invoice.bulkCancelTitle", { count: String(cancelTarget?.length ?? 0) }),
|
|
532
|
+
description: t2("financial.invoice.cancelDesc"),
|
|
533
|
+
confirmLabel: t2("financial.invoice.yesCancel"),
|
|
534
|
+
cancelLabel: t2("financial.invoice.keepInvoice"),
|
|
535
|
+
loading: bulkBusy,
|
|
536
|
+
onConfirm: () => void confirmBulkCancel(),
|
|
537
|
+
onCancel: () => setCancelTarget(null)
|
|
538
|
+
}
|
|
539
|
+
)
|
|
472
540
|
] });
|
|
473
541
|
}
|
|
474
542
|
function useReceiptPicker(onPick) {
|
|
@@ -501,96 +569,6 @@ function useReceiptPicker(onPick) {
|
|
|
501
569
|
);
|
|
502
570
|
return { open: () => ref.current?.click(), input };
|
|
503
571
|
}
|
|
504
|
-
function ReceiptField({
|
|
505
|
-
value,
|
|
506
|
-
onChange,
|
|
507
|
-
autoOpen = false
|
|
508
|
-
}) {
|
|
509
|
-
const t2 = useTranslation();
|
|
510
|
-
const fileInputRef = useRef(null);
|
|
511
|
-
useEffect(() => {
|
|
512
|
-
if (!autoOpen) return;
|
|
513
|
-
const id = window.setTimeout(() => fileInputRef.current?.click(), 250);
|
|
514
|
-
return () => window.clearTimeout(id);
|
|
515
|
-
}, [autoOpen]);
|
|
516
|
-
function onPick(e) {
|
|
517
|
-
const file = e.target.files?.[0];
|
|
518
|
-
e.target.value = "";
|
|
519
|
-
if (!file) return;
|
|
520
|
-
if (!file.type.startsWith("image/")) {
|
|
521
|
-
toast.error(t2("financial.quickTx.receiptInvalid"));
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
const reader = new FileReader();
|
|
525
|
-
reader.onload = () => {
|
|
526
|
-
onChange(typeof reader.result === "string" ? reader.result : void 0);
|
|
527
|
-
};
|
|
528
|
-
reader.onerror = () => toast.error(t2("financial.quickTx.receiptInvalid"));
|
|
529
|
-
reader.readAsDataURL(file);
|
|
530
|
-
}
|
|
531
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
532
|
-
/* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [
|
|
533
|
-
/* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }),
|
|
534
|
-
" ",
|
|
535
|
-
t2("financial.quickTx.receipt")
|
|
536
|
-
] }),
|
|
537
|
-
/* @__PURE__ */ jsx(
|
|
538
|
-
"input",
|
|
539
|
-
{
|
|
540
|
-
ref: fileInputRef,
|
|
541
|
-
type: "file",
|
|
542
|
-
accept: "image/*",
|
|
543
|
-
capture: "environment",
|
|
544
|
-
className: "hidden",
|
|
545
|
-
onChange: onPick
|
|
546
|
-
}
|
|
547
|
-
),
|
|
548
|
-
value ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-input border border-input bg-card p-2 shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)]", children: [
|
|
549
|
-
/* @__PURE__ */ jsx(
|
|
550
|
-
"img",
|
|
551
|
-
{
|
|
552
|
-
src: value,
|
|
553
|
-
alt: t2("financial.quickTx.receipt"),
|
|
554
|
-
className: "h-16 w-16 shrink-0 rounded-md border object-cover"
|
|
555
|
-
}
|
|
556
|
-
),
|
|
557
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
558
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: t2("financial.quickTx.receiptAttached") }),
|
|
559
|
-
/* @__PURE__ */ jsx(
|
|
560
|
-
"button",
|
|
561
|
-
{
|
|
562
|
-
type: "button",
|
|
563
|
-
onClick: () => fileInputRef.current?.click(),
|
|
564
|
-
className: "text-xs text-primary hover:underline",
|
|
565
|
-
children: t2("financial.quickTx.receiptReplace")
|
|
566
|
-
}
|
|
567
|
-
)
|
|
568
|
-
] }),
|
|
569
|
-
/* @__PURE__ */ jsx(
|
|
570
|
-
"button",
|
|
571
|
-
{
|
|
572
|
-
type: "button",
|
|
573
|
-
onClick: () => onChange(void 0),
|
|
574
|
-
"aria-label": t2("financial.quickTx.receiptRemove"),
|
|
575
|
-
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted",
|
|
576
|
-
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
577
|
-
}
|
|
578
|
-
)
|
|
579
|
-
] }) : /* @__PURE__ */ jsxs(
|
|
580
|
-
"button",
|
|
581
|
-
{
|
|
582
|
-
type: "button",
|
|
583
|
-
onClick: () => fileInputRef.current?.click(),
|
|
584
|
-
className: `flex w-full items-center justify-center gap-2 rounded-input border border-dashed px-3 py-3 text-sm font-medium transition-colors hover:bg-muted/30 ${autoOpen ? "border-primary text-primary ring-1 ring-primary/30" : "border-input text-muted-foreground"}`,
|
|
585
|
-
children: [
|
|
586
|
-
/* @__PURE__ */ jsx(Camera, { className: "h-4 w-4" }),
|
|
587
|
-
" ",
|
|
588
|
-
t2("financial.quickTx.receiptCapture")
|
|
589
|
-
]
|
|
590
|
-
}
|
|
591
|
-
)
|
|
592
|
-
] });
|
|
593
|
-
}
|
|
594
572
|
var ACCOUNT_TYPES_FOR_TX = {
|
|
595
573
|
cash: ["cash_register"],
|
|
596
574
|
credit_card: ["credit_card", "bank_account"],
|
|
@@ -756,7 +734,7 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
|
|
|
756
734
|
"button",
|
|
757
735
|
{
|
|
758
736
|
onClick: () => setExpandMethodPicker(true),
|
|
759
|
-
className: `flex items-center gap-2 mt-1.5 w-full rounded-
|
|
737
|
+
className: `flex items-center gap-2 mt-1.5 w-full rounded-button border px-3 py-2.5 text-left transition-colors hover:bg-muted/30 ${TYPE_COLORS[txType] ?? ""}`,
|
|
760
738
|
"data-active": "true",
|
|
761
739
|
children: [
|
|
762
740
|
SelectedIcon && /* @__PURE__ */ jsx(SelectedIcon, { className: `h-4 w-4 ${ICON_COLORS[txType] ?? ""}` }),
|
|
@@ -787,7 +765,7 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
|
|
|
787
765
|
{
|
|
788
766
|
"data-active": isActive,
|
|
789
767
|
onClick: () => handleSelectType(type.id),
|
|
790
|
-
className: `flex flex-col items-center gap-1.5 rounded-
|
|
768
|
+
className: `flex flex-col items-center gap-1.5 rounded-button border px-2 py-3 text-center transition-all ${TYPE_COLORS[tt] ?? TYPE_COLORS.cash}`,
|
|
791
769
|
children: [
|
|
792
770
|
/* @__PURE__ */ jsx(Icon, { className: `h-5 w-5 ${isActive ? ICON_COLORS[tt] ?? "" : "text-muted-foreground"}` }),
|
|
793
771
|
/* @__PURE__ */ jsx("span", { className: `text-[11px] font-medium leading-tight ${isActive ? "text-foreground" : "text-muted-foreground"}`, children: type.name })
|
|
@@ -995,6 +973,16 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
995
973
|
void fetchBankAccounts();
|
|
996
974
|
void fetchPaymentMethods();
|
|
997
975
|
}, [isEdit]);
|
|
976
|
+
useEffect(() => {
|
|
977
|
+
let cancelled = false;
|
|
978
|
+
void provider.getChartOfAccounts().then((rows) => {
|
|
979
|
+
if (!cancelled) setAccounts(rows);
|
|
980
|
+
}).catch(() => {
|
|
981
|
+
});
|
|
982
|
+
return () => {
|
|
983
|
+
cancelled = true;
|
|
984
|
+
};
|
|
985
|
+
}, [provider]);
|
|
998
986
|
const [loaded, setLoaded] = useState(!editId);
|
|
999
987
|
const [loading, setLoading] = useState(!!editId);
|
|
1000
988
|
const [contactName, setContactName] = useState("");
|
|
@@ -1004,11 +992,32 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1004
992
|
const [fiscalNumber, setFiscalNumber] = useState("");
|
|
1005
993
|
const [observations, setObservations] = useState("");
|
|
1006
994
|
const [items, setItems] = useState([]);
|
|
995
|
+
const [accounts, setAccounts] = useState([]);
|
|
996
|
+
const postable = useMemo(() => postableAccounts(accounts), [accounts]);
|
|
1007
997
|
const [expandedItemId, setExpandedItemId] = useState(null);
|
|
1008
998
|
const [addingItem, setAddingItem] = useState(false);
|
|
1009
999
|
const [installments, setInstallments] = useState([]);
|
|
1010
1000
|
const [savedMovements, setSavedMovements] = useState([]);
|
|
1011
1001
|
const [payingMovement, setPayingMovement] = useState(null);
|
|
1002
|
+
const [pendingReceipt, setPendingReceipt] = useState();
|
|
1003
|
+
const contactCreate = useLookupCreate(config.contactLookup, (created) => {
|
|
1004
|
+
setContactId(created.id);
|
|
1005
|
+
setContactName(created.label);
|
|
1006
|
+
});
|
|
1007
|
+
const [receiptTarget, setReceiptTarget] = useState(null);
|
|
1008
|
+
const receiptPicker = useReceiptPicker((dataUrl) => {
|
|
1009
|
+
const i = receiptTarget;
|
|
1010
|
+
setReceiptTarget(null);
|
|
1011
|
+
if (i == null) return;
|
|
1012
|
+
if (isEdit) {
|
|
1013
|
+
const saved = savedMovements[i];
|
|
1014
|
+
if (!saved) return;
|
|
1015
|
+
setPendingReceipt(dataUrl);
|
|
1016
|
+
setPayingMovement(saved);
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
setInstallments((prev) => prev.map((inst, idx) => idx === i ? { ...inst, receiptUrl: dataUrl } : inst));
|
|
1020
|
+
});
|
|
1012
1021
|
const [saving, setSaving] = useState(false);
|
|
1013
1022
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
1014
1023
|
const [confirmCancel, setConfirmCancel] = useState(false);
|
|
@@ -1055,7 +1064,9 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1055
1064
|
quantity: it.quantity,
|
|
1056
1065
|
unitPrice: it.unitPrice,
|
|
1057
1066
|
discount: it.discount,
|
|
1058
|
-
surcharge: it.surcharge
|
|
1067
|
+
surcharge: it.surcharge,
|
|
1068
|
+
accountId: it.accountId,
|
|
1069
|
+
costCenterId: it.costCenterId
|
|
1059
1070
|
})));
|
|
1060
1071
|
const movements = await provider.getMovements({ direction: invoice.direction });
|
|
1061
1072
|
const invoiceMovements = movements.data.filter((m) => m.invoiceId === editId && m.movementKind === "bill").sort((a, b) => (a.installmentNumber ?? 0) - (b.installmentNumber ?? 0));
|
|
@@ -1119,6 +1130,17 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1119
1130
|
if (isEdit || totalAmount <= 0 || installments.length > 0) return;
|
|
1120
1131
|
generateInstallments(1);
|
|
1121
1132
|
}, [isEdit, totalAmount, installments.length]);
|
|
1133
|
+
const receiptAutoOpened = useRef(false);
|
|
1134
|
+
useEffect(() => {
|
|
1135
|
+
if (!focusReceipt || isEdit || receiptAutoOpened.current) return;
|
|
1136
|
+
if (installments.length === 0 || installments[0]?.receiptUrl) return;
|
|
1137
|
+
receiptAutoOpened.current = true;
|
|
1138
|
+
const id = window.setTimeout(() => {
|
|
1139
|
+
setReceiptTarget(0);
|
|
1140
|
+
receiptPicker.open();
|
|
1141
|
+
}, 250);
|
|
1142
|
+
return () => window.clearTimeout(id);
|
|
1143
|
+
}, [focusReceipt, isEdit, installments.length]);
|
|
1122
1144
|
function generateInstallments(count) {
|
|
1123
1145
|
if (count < 1 || totalAmount <= 0) return;
|
|
1124
1146
|
const perInstallment = Math.floor(totalAmount * 100 / count) / 100;
|
|
@@ -1242,7 +1264,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1242
1264
|
});
|
|
1243
1265
|
const title = isEdit ? t2("financial.invoiceForm.editTitle") : t2("financial.invoiceForm.newTitle");
|
|
1244
1266
|
const subtitle = isEdit ? t2("financial.invoiceForm.editing") : t2("financial.invoiceForm.creating");
|
|
1245
|
-
if (loading) return /* @__PURE__ */ jsx(OrderFormSkeleton, {});
|
|
1267
|
+
if (loading) return /* @__PURE__ */ jsx(OrderFormSkeleton, { parentLabel: direction === "debit" ? t2("financial.nav.payables") : t2("financial.nav.receivables"), onBack: onSaved });
|
|
1246
1268
|
const money = (v) => formatCurrency(v, currency);
|
|
1247
1269
|
const actions = isEdit ? /* @__PURE__ */ jsxs("div", { className: "relative", ref: menuRef, children: [
|
|
1248
1270
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", onClick: () => setMenuOpen(!menuOpen), "aria-label": "More actions", children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" }) }),
|
|
@@ -1264,58 +1286,91 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1264
1286
|
] }) : null;
|
|
1265
1287
|
const paidSoFar = savedMovements.reduce((sum, m) => sum + (m.paidAmount ?? 0), 0);
|
|
1266
1288
|
const hasPayments = paidSoFar > 0;
|
|
1267
|
-
const
|
|
1268
|
-
/* @__PURE__ */ jsx("
|
|
1269
|
-
|
|
1270
|
-
{
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
n
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1289
|
+
const scheduleSection = /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1290
|
+
hasPayments ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.invoiceForm.scheduleLocked") }) : (
|
|
1291
|
+
// Atalhos de parcelamento — 1x, 2x, 3x, 6x, 12x cobrem quase tudo.
|
|
1292
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: [1, 2, 3, 6, 12].map((n) => /* @__PURE__ */ jsxs(
|
|
1293
|
+
"button",
|
|
1294
|
+
{
|
|
1295
|
+
type: "button",
|
|
1296
|
+
onClick: () => generateInstallments(n),
|
|
1297
|
+
disabled: totalAmount <= 0,
|
|
1298
|
+
className: `rounded-full px-2.5 py-1 text-[10px] font-medium transition-colors disabled:opacity-30 ${installments.length === n ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/80"}`,
|
|
1299
|
+
children: [
|
|
1300
|
+
n,
|
|
1301
|
+
"x"
|
|
1302
|
+
]
|
|
1303
|
+
},
|
|
1304
|
+
n
|
|
1305
|
+
)) })
|
|
1306
|
+
),
|
|
1307
|
+
installments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: totalAmount <= 0 ? t2("financial.invoiceForm.addItemsFirst") : t2("financial.invoiceForm.selectInstallments") }) : /* @__PURE__ */ jsx("div", { className: "divide-y rounded border", children: installments.map((inst, i) => {
|
|
1283
1308
|
const patch = (next) => {
|
|
1284
1309
|
const updated = [...installments];
|
|
1285
1310
|
updated[i] = { ...inst, ...next };
|
|
1286
1311
|
setInstallments(updated);
|
|
1287
1312
|
};
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1313
|
+
const saved = savedMovements[i];
|
|
1314
|
+
const isPaid = saved?.status === "paid";
|
|
1315
|
+
const isPartial = saved?.status === "partial";
|
|
1316
|
+
const StatusIcon = isPaid ? CircleCheckBig : CircleDashed;
|
|
1317
|
+
const canAttach = isEdit ? !!saved && !isPaid : true;
|
|
1318
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1319
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 px-3 py-2", children: [
|
|
1320
|
+
/* @__PURE__ */ jsxs("span", { className: "w-6 shrink-0 text-xs font-medium text-muted-foreground", children: [
|
|
1291
1321
|
"#",
|
|
1292
1322
|
i + 1
|
|
1293
1323
|
] }),
|
|
1294
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: money(inst.amount) })
|
|
1324
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums", children: money(inst.amount) }),
|
|
1325
|
+
saved && /* @__PURE__ */ jsxs("span", { className: `inline-flex shrink-0 items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${isPaid ? "bg-success-soft text-success-soft-foreground" : isPartial ? "bg-warning-soft text-warning-soft-foreground" : "bg-muted text-muted-foreground"}`, children: [
|
|
1326
|
+
/* @__PURE__ */ jsx(StatusIcon, { className: "h-2.5 w-2.5" }),
|
|
1327
|
+
t2(isPaid ? "financial.invoice.statusPaid" : isPartial ? "financial.invoice.statusPartial" : "financial.invoice.statusPending")
|
|
1328
|
+
] }),
|
|
1329
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [
|
|
1330
|
+
!hasPayments ? /* @__PURE__ */ jsx(
|
|
1331
|
+
DatePicker,
|
|
1332
|
+
{
|
|
1333
|
+
value: inst.dueDate,
|
|
1334
|
+
variant: "chip",
|
|
1335
|
+
onChange: (v) => patch({ dueDate: v })
|
|
1336
|
+
}
|
|
1337
|
+
) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: inst.dueDate }),
|
|
1338
|
+
canAttach && /* @__PURE__ */ jsx(
|
|
1339
|
+
"button",
|
|
1340
|
+
{
|
|
1341
|
+
type: "button",
|
|
1342
|
+
onClick: () => {
|
|
1343
|
+
setReceiptTarget(i);
|
|
1344
|
+
receiptPicker.open();
|
|
1345
|
+
},
|
|
1346
|
+
title: t2("financial.quickTx.receiptCapture"),
|
|
1347
|
+
"aria-label": t2("financial.quickTx.receiptCapture"),
|
|
1348
|
+
className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-md transition-colors ${inst.receiptUrl ? "text-success hover:bg-success-soft" : "text-muted-foreground hover:bg-muted hover:text-foreground"}`,
|
|
1349
|
+
children: inst.receiptUrl ? /* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Camera, { className: "h-3.5 w-3.5" })
|
|
1350
|
+
}
|
|
1351
|
+
),
|
|
1352
|
+
saved && !isPaid && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setPayingMovement(saved), children: [
|
|
1353
|
+
/* @__PURE__ */ jsx(DollarSign, { className: "mr-1 h-3.5 w-3.5" }),
|
|
1354
|
+
t2("financial.invoice.pay")
|
|
1355
|
+
] })
|
|
1356
|
+
] })
|
|
1295
1357
|
] }),
|
|
1296
|
-
/* @__PURE__ */
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
{
|
|
1306
|
-
value: inst.receiptUrl,
|
|
1307
|
-
onChange: (url) => patch({ receiptUrl: url }),
|
|
1308
|
-
autoOpen: focusReceipt && i === 0 && !inst.receiptUrl
|
|
1309
|
-
}
|
|
1310
|
-
),
|
|
1311
|
-
inst.receiptUrl && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 border-t pt-1.5", children: [
|
|
1312
|
-
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium text-success", children: t2("financial.invoiceForm.willBePaid") }),
|
|
1358
|
+
!isEdit && inst.receiptUrl && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t bg-muted/20 px-3 py-2", children: [
|
|
1359
|
+
/* @__PURE__ */ jsx(
|
|
1360
|
+
"img",
|
|
1361
|
+
{
|
|
1362
|
+
src: inst.receiptUrl,
|
|
1363
|
+
alt: t2("financial.quickTx.receipt"),
|
|
1364
|
+
className: "h-8 w-8 shrink-0 rounded border object-cover"
|
|
1365
|
+
}
|
|
1366
|
+
),
|
|
1367
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] font-medium text-success", children: t2("financial.invoiceForm.willBePaid") }),
|
|
1313
1368
|
/* @__PURE__ */ jsxs(
|
|
1314
1369
|
"select",
|
|
1315
1370
|
{
|
|
1316
1371
|
value: inst.payMethodTypeId ?? "",
|
|
1317
1372
|
onChange: (e) => patch({ payMethodTypeId: e.target.value }),
|
|
1318
|
-
className: "w-
|
|
1373
|
+
className: "min-w-[8rem] flex-1 rounded-input border border-input bg-card px-2 py-1 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1319
1374
|
children: [
|
|
1320
1375
|
/* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectMethod") }),
|
|
1321
1376
|
paymentMethodTypes.map((tp) => /* @__PURE__ */ jsx("option", { value: tp.id, children: tp.name }, tp.id))
|
|
@@ -1327,95 +1382,52 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1327
1382
|
{
|
|
1328
1383
|
value: inst.payAccountId ?? (bankAccounts.length === 1 ? bankAccounts[0].id : ""),
|
|
1329
1384
|
onChange: (e) => patch({ payAccountId: e.target.value }),
|
|
1330
|
-
className: "w-
|
|
1385
|
+
className: "min-w-[8rem] flex-1 rounded-input border border-input bg-card px-2 py-1 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1331
1386
|
children: [
|
|
1332
1387
|
/* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectAccount") }),
|
|
1333
1388
|
bankAccounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
|
|
1334
1389
|
]
|
|
1335
1390
|
}
|
|
1336
|
-
)
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
}) })
|
|
1340
|
-
] }) });
|
|
1341
|
-
const scheduleSection = isEdit ? /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1342
|
-
hasPayments && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.invoiceForm.scheduleLocked") }),
|
|
1343
|
-
!hasPayments && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: [1, 2, 3, 6, 12].map((n) => /* @__PURE__ */ jsxs(
|
|
1344
|
-
"button",
|
|
1345
|
-
{
|
|
1346
|
-
type: "button",
|
|
1347
|
-
onClick: () => generateInstallments(n),
|
|
1348
|
-
disabled: totalAmount <= 0,
|
|
1349
|
-
className: `rounded-full px-2.5 py-1 text-[10px] font-medium transition-colors disabled:opacity-30 ${installments.length === n ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/80"}`,
|
|
1350
|
-
children: [
|
|
1351
|
-
n,
|
|
1352
|
-
"x"
|
|
1353
|
-
]
|
|
1354
|
-
},
|
|
1355
|
-
n
|
|
1356
|
-
)) }),
|
|
1357
|
-
installments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: totalAmount <= 0 ? t2("financial.invoiceForm.addItemsFirst") : t2("financial.invoiceForm.selectInstallments") }) : /* @__PURE__ */ jsx("div", { className: "divide-y rounded border", children: installments.map((inst, i) => {
|
|
1358
|
-
const saved = savedMovements[i];
|
|
1359
|
-
const isPaid = saved?.status === "paid";
|
|
1360
|
-
const isPartial = saved?.status === "partial";
|
|
1361
|
-
const StatusIcon = isPaid ? CircleCheckBig : CircleDashed;
|
|
1362
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 px-3 py-2.5", children: [
|
|
1363
|
-
/* @__PURE__ */ jsxs("span", { className: "w-6 shrink-0 text-xs font-medium text-muted-foreground", children: [
|
|
1364
|
-
"#",
|
|
1365
|
-
i + 1
|
|
1366
|
-
] }),
|
|
1367
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums", children: money(inst.amount) }),
|
|
1368
|
-
saved && /* @__PURE__ */ jsxs("span", { className: `inline-flex shrink-0 items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${isPaid ? "bg-success-soft text-success-soft-foreground" : isPartial ? "bg-warning-soft text-warning-soft-foreground" : "bg-muted text-muted-foreground"}`, children: [
|
|
1369
|
-
/* @__PURE__ */ jsx(StatusIcon, { className: "h-2.5 w-2.5" }),
|
|
1370
|
-
t2(isPaid ? "financial.invoice.statusPaid" : isPartial ? "financial.invoice.statusPartial" : "financial.invoice.statusPending")
|
|
1371
|
-
] }),
|
|
1372
|
-
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
|
|
1373
|
-
!hasPayments ? /* @__PURE__ */ jsx(
|
|
1374
|
-
DatePicker,
|
|
1391
|
+
),
|
|
1392
|
+
/* @__PURE__ */ jsx(
|
|
1393
|
+
"button",
|
|
1375
1394
|
{
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
setInstallments(updated);
|
|
1382
|
-
}
|
|
1395
|
+
type: "button",
|
|
1396
|
+
onClick: () => patch({ receiptUrl: void 0, payMethodTypeId: void 0, payAccountId: void 0 }),
|
|
1397
|
+
"aria-label": t2("financial.quickTx.receiptRemove"),
|
|
1398
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
1399
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
1383
1400
|
}
|
|
1384
|
-
)
|
|
1385
|
-
saved && !isPaid && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setPayingMovement(saved), children: [
|
|
1386
|
-
/* @__PURE__ */ jsx(DollarSign, { className: "mr-1 h-3.5 w-3.5" }),
|
|
1387
|
-
t2("financial.invoice.pay")
|
|
1388
|
-
] })
|
|
1401
|
+
)
|
|
1389
1402
|
] })
|
|
1390
1403
|
] }, inst._id);
|
|
1391
1404
|
}) })
|
|
1392
|
-
] }) })
|
|
1405
|
+
] }) });
|
|
1393
1406
|
const sideSections = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1394
|
-
/* @__PURE__ */
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
) }),
|
|
1407
|
+
/* @__PURE__ */ jsxs(OrderFormBlock, { title: direction === "debit" ? t2("financial.invoiceForm.payTo") : t2("financial.invoiceForm.receiveFrom"), children: [
|
|
1408
|
+
/* @__PURE__ */ jsx(
|
|
1409
|
+
SearchSelect,
|
|
1410
|
+
{
|
|
1411
|
+
required: true,
|
|
1412
|
+
value: contactId ?? "",
|
|
1413
|
+
displayValue: contactName,
|
|
1414
|
+
onChange: (id, opt) => {
|
|
1415
|
+
setContactId(id || void 0);
|
|
1416
|
+
setContactName(opt?.label ?? "");
|
|
1417
|
+
},
|
|
1418
|
+
onSearch: async (q) => {
|
|
1419
|
+
if (!config.contactLookup) return [];
|
|
1420
|
+
const results = await config.contactLookup.search(q);
|
|
1421
|
+
return results.map((r) => ({ id: r.id, label: r.label, subtitle: r.subtitle, group: r.group, data: r }));
|
|
1422
|
+
},
|
|
1423
|
+
createLabel: t2("financial.invoiceForm.createContact"),
|
|
1424
|
+
clearLabel: t2("financial.invoiceForm.clear"),
|
|
1425
|
+
onCreate: contactCreate.onCreate,
|
|
1426
|
+
placeholder: t2("financial.invoiceForm.searchType")
|
|
1427
|
+
}
|
|
1428
|
+
),
|
|
1429
|
+
contactCreate.modal
|
|
1430
|
+
] }),
|
|
1419
1431
|
/* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.details"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1420
1432
|
hasLocations && /* @__PURE__ */ jsx(LocationSelector, { locations, value: unitId, onChange: setUnitId }),
|
|
1421
1433
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -1436,7 +1448,6 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1436
1448
|
)
|
|
1437
1449
|
] })
|
|
1438
1450
|
] }) }),
|
|
1439
|
-
!isEdit && installmentsEditor,
|
|
1440
1451
|
/* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoiceForm.observations"), children: /* @__PURE__ */ jsx(
|
|
1441
1452
|
"textarea",
|
|
1442
1453
|
{
|
|
@@ -1502,6 +1513,21 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1502
1513
|
removeItem(item._id);
|
|
1503
1514
|
if (expandedItemId === item._id) setExpandedItemId(null);
|
|
1504
1515
|
},
|
|
1516
|
+
extraFields: postable.length > 0 && /* @__PURE__ */ jsx("div", { className: "fz-fields fz-fields-2", children: /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
1517
|
+
/* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium text-muted-foreground", children: t2("financial.invoiceForm.account") }),
|
|
1518
|
+
/* @__PURE__ */ jsxs(
|
|
1519
|
+
"select",
|
|
1520
|
+
{
|
|
1521
|
+
value: item.accountId ?? "",
|
|
1522
|
+
onChange: (e) => updateItem(item._id, { accountId: e.target.value || void 0 }),
|
|
1523
|
+
className: "mt-0.5 w-full rounded-input border border-input bg-card px-2.5 py-1.5 text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1524
|
+
children: [
|
|
1525
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.accountNone") }),
|
|
1526
|
+
postable.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: accountLabel(a) }, a.id))
|
|
1527
|
+
]
|
|
1528
|
+
}
|
|
1529
|
+
)
|
|
1530
|
+
] }) }),
|
|
1505
1531
|
entityLookups: config.entityLookups,
|
|
1506
1532
|
showTypeSelector: hasItemLookups,
|
|
1507
1533
|
allowSurcharge: true,
|
|
@@ -1527,13 +1553,19 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1527
1553
|
]
|
|
1528
1554
|
}
|
|
1529
1555
|
),
|
|
1556
|
+
receiptPicker.input,
|
|
1530
1557
|
payingMovement && /* @__PURE__ */ jsx(
|
|
1531
1558
|
PaymentModal,
|
|
1532
1559
|
{
|
|
1533
1560
|
movement: payingMovement,
|
|
1534
|
-
|
|
1561
|
+
receiptUrl: pendingReceipt,
|
|
1562
|
+
onClose: () => {
|
|
1563
|
+
setPayingMovement(null);
|
|
1564
|
+
setPendingReceipt(void 0);
|
|
1565
|
+
},
|
|
1535
1566
|
onPaid: () => {
|
|
1536
1567
|
setPayingMovement(null);
|
|
1568
|
+
setPendingReceipt(void 0);
|
|
1537
1569
|
void (async () => {
|
|
1538
1570
|
if (!editId) return;
|
|
1539
1571
|
const movements = await provider.getMovements({ direction });
|
|
@@ -1572,6 +1604,27 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
|
1572
1604
|
` })
|
|
1573
1605
|
] });
|
|
1574
1606
|
}
|
|
1607
|
+
|
|
1608
|
+
// src/lib/settlements.ts
|
|
1609
|
+
function groupPaymentsByBill(bills, payments) {
|
|
1610
|
+
const byBillId = /* @__PURE__ */ new Map();
|
|
1611
|
+
for (const bill of bills) byBillId.set(bill.id, []);
|
|
1612
|
+
const unattributed = [];
|
|
1613
|
+
for (const payment of payments) {
|
|
1614
|
+
if (payment.settlesMovementId) {
|
|
1615
|
+
const bucket = byBillId.get(payment.settlesMovementId);
|
|
1616
|
+
if (bucket) bucket.push(payment);
|
|
1617
|
+
else unattributed.push(payment);
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
const derived = bills.find(
|
|
1621
|
+
(bill) => (bill.installmentNumber ?? null) === (payment.installmentNumber ?? null)
|
|
1622
|
+
);
|
|
1623
|
+
if (derived) byBillId.get(derived.id).push(payment);
|
|
1624
|
+
else unattributed.push(payment);
|
|
1625
|
+
}
|
|
1626
|
+
return { byBillId, unattributed };
|
|
1627
|
+
}
|
|
1575
1628
|
var STATUS_COLORS = {
|
|
1576
1629
|
open: { bg: "bg-info-soft text-info-soft-foreground", icon: CircleDashed, labelKey: "financial.invoice.statusOpen", tone: "info" },
|
|
1577
1630
|
pending: { bg: "bg-warning-soft text-warning-soft-foreground", icon: CircleDashed, labelKey: "financial.invoice.statusPending", tone: "warning" },
|
|
@@ -1668,7 +1721,7 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
|
|
|
1668
1721
|
}
|
|
1669
1722
|
}
|
|
1670
1723
|
if (loading) {
|
|
1671
|
-
return /* @__PURE__ */ jsx(OrderDetailSkeleton, {});
|
|
1724
|
+
return /* @__PURE__ */ jsx(OrderDetailSkeleton, { parentLabel, onBack });
|
|
1672
1725
|
}
|
|
1673
1726
|
if (!invoice) {
|
|
1674
1727
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
@@ -1676,6 +1729,7 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
|
|
|
1676
1729
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t2("financial.invoice.notFoundDesc") })
|
|
1677
1730
|
] });
|
|
1678
1731
|
}
|
|
1732
|
+
const settlements = groupPaymentsByBill(movements, payments);
|
|
1679
1733
|
const statusConfig = STATUS_COLORS[invoice.status] ?? STATUS_COLORS.pending;
|
|
1680
1734
|
statusConfig.icon;
|
|
1681
1735
|
const totalPaid = movements.reduce((sum, m) => sum + m.paidAmount, 0);
|
|
@@ -1750,7 +1804,6 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
|
|
|
1750
1804
|
{
|
|
1751
1805
|
title: invoice.fiscalNumber ? `#${invoice.fiscalNumber}` : invoice.contactName ?? invoice.invoiceDate,
|
|
1752
1806
|
badges: [{ label: t2(statusConfig.labelKey), tone: statusConfig.tone }],
|
|
1753
|
-
subtitle: invoice.invoiceDate,
|
|
1754
1807
|
onBack,
|
|
1755
1808
|
backLabel: parentLabel,
|
|
1756
1809
|
actions,
|
|
@@ -1769,153 +1822,166 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
|
|
|
1769
1822
|
mainSections: (
|
|
1770
1823
|
// O cronograma no MESMO bloco que os itens e o pagamento — antes o título
|
|
1771
1824
|
// ficava fora do cartão, e a seção lia como se fosse de outra tela.
|
|
1772
|
-
/* @__PURE__ */ jsx(OrderDetailBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
/* @__PURE__ */ jsxs(
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
"
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1793
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1794
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium tabular-nums", children: formatCurrency(mov.amount, currency) }),
|
|
1795
|
-
/* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-0.5 rounded-full px-1.5 py-0.5 text-[9px] font-medium capitalize ${movStatus.bg}`, children: [
|
|
1796
|
-
/* @__PURE__ */ jsx(MovIcon, { className: "h-2 w-2" }),
|
|
1797
|
-
" ",
|
|
1798
|
-
t2(movStatus.labelKey)
|
|
1799
|
-
] })
|
|
1825
|
+
/* @__PURE__ */ jsx(OrderDetailBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "-mx-4 -mb-4", children: [
|
|
1826
|
+
movements.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-4 text-center text-sm text-muted-foreground", children: t2("financial.invoice.noInstallments") }) : /* @__PURE__ */ jsx("div", { className: "divide-y border-t", children: movements.map((mov) => {
|
|
1827
|
+
const remaining = mov.amount - mov.paidAmount;
|
|
1828
|
+
const isPaid = mov.status === "paid";
|
|
1829
|
+
const isPartial = mov.status === "partial";
|
|
1830
|
+
const hasPaidDetails = isPaid || isPartial;
|
|
1831
|
+
const isExpanded = expandedMovId === mov.id;
|
|
1832
|
+
const movStatus = STATUS_COLORS[mov.status] ?? STATUS_COLORS.pending;
|
|
1833
|
+
const MovIcon = movStatus.icon;
|
|
1834
|
+
const instPayments = settlements.byBillId.get(mov.id) ?? [];
|
|
1835
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1836
|
+
/* @__PURE__ */ jsxs(
|
|
1837
|
+
"div",
|
|
1838
|
+
{
|
|
1839
|
+
className: `flex items-center gap-3 px-4 py-3 ${hasPaidDetails ? "cursor-pointer hover:bg-muted/20 transition-colors" : ""}`,
|
|
1840
|
+
onClick: hasPaidDetails ? () => handleExpandMov(mov.id) : void 0,
|
|
1841
|
+
children: [
|
|
1842
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-muted-foreground w-6 shrink-0", children: [
|
|
1843
|
+
"#",
|
|
1844
|
+
mov.installmentNumber
|
|
1800
1845
|
] }),
|
|
1801
|
-
/* @__PURE__ */ jsxs("
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
onClick: (e) => {
|
|
1813
|
-
e.stopPropagation();
|
|
1814
|
-
setReceiptFor(mov);
|
|
1815
|
-
receiptPicker.open();
|
|
1816
|
-
},
|
|
1817
|
-
title: t2("financial.quickTx.receiptCapture"),
|
|
1818
|
-
"aria-label": t2("financial.quickTx.receiptCapture"),
|
|
1819
|
-
className: "inline-flex shrink-0 items-center gap-1 rounded-lg border bg-card px-2.5 py-1.5 text-xs font-medium shadow-button transition-colors hover:bg-muted active:shadow-button-inset",
|
|
1820
|
-
children: /* @__PURE__ */ jsx(Camera, { className: "h-3 w-3" })
|
|
1821
|
-
}
|
|
1822
|
-
),
|
|
1823
|
-
!isPaid && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
|
|
1824
|
-
"button",
|
|
1825
|
-
{
|
|
1826
|
-
onClick: (e) => {
|
|
1827
|
-
e.stopPropagation();
|
|
1828
|
-
setPayingMovement(mov);
|
|
1829
|
-
},
|
|
1830
|
-
className: "inline-flex items-center gap-1 rounded-lg bg-primary border border-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors shrink-0",
|
|
1831
|
-
children: [
|
|
1832
|
-
/* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
|
|
1833
|
-
t2("financial.invoice.pay"),
|
|
1834
|
-
" ",
|
|
1835
|
-
remaining < mov.amount ? formatCurrency(remaining, currency) : ""
|
|
1836
|
-
]
|
|
1837
|
-
}
|
|
1838
|
-
),
|
|
1839
|
-
isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
|
|
1840
|
-
"button",
|
|
1841
|
-
{
|
|
1842
|
-
onClick: (e) => {
|
|
1843
|
-
e.stopPropagation();
|
|
1844
|
-
setPayingMovement(mov);
|
|
1845
|
-
},
|
|
1846
|
-
className: "inline-flex items-center gap-1 rounded-lg border px-3 py-1.5 text-xs font-medium hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors shrink-0",
|
|
1847
|
-
children: [
|
|
1848
|
-
/* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
|
|
1849
|
-
t2("financial.invoice.pay"),
|
|
1846
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1847
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1848
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium tabular-nums", children: formatCurrency(mov.amount, currency) }),
|
|
1849
|
+
/* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-0.5 rounded-full px-1.5 py-0.5 text-[9px] font-medium capitalize ${movStatus.bg}`, children: [
|
|
1850
|
+
/* @__PURE__ */ jsx(MovIcon, { className: "h-2 w-2" }),
|
|
1851
|
+
" ",
|
|
1852
|
+
t2(movStatus.labelKey)
|
|
1853
|
+
] })
|
|
1854
|
+
] }),
|
|
1855
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
|
|
1856
|
+
t2("financial.invoice.due"),
|
|
1850
1857
|
" ",
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
),
|
|
1855
|
-
hasPaidDetails && /* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 text-muted-foreground shrink-0 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })
|
|
1856
|
-
]
|
|
1857
|
-
}
|
|
1858
|
-
),
|
|
1859
|
-
/* @__PURE__ */ jsx(
|
|
1860
|
-
"div",
|
|
1861
|
-
{
|
|
1862
|
-
className: "grid transition-all duration-200 ease-out",
|
|
1863
|
-
style: { gridTemplateRows: isExpanded ? "1fr" : "0fr" },
|
|
1864
|
-
children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-2.5 pl-14", children: detailsLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1865
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-20 rounded bg-muted/40 animate-pulse" }),
|
|
1866
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-16 rounded bg-muted/30 animate-pulse" }),
|
|
1867
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-24 rounded bg-muted/40 animate-pulse" })
|
|
1868
|
-
] }) : instPayments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-[11px] text-muted-foreground", children: t2("financial.invoice.noDetails") }) : /* @__PURE__ */ jsx("div", { className: "space-y-1", children: instPayments.map((p) => {
|
|
1869
|
-
const pMethod = paymentMethods.find((m) => m.id === p.paymentMethodId);
|
|
1870
|
-
const pType = paymentMethodTypes.find((tp) => tp.id === (pMethod?.paymentMethodTypeId ?? p.paymentMethodTypeId));
|
|
1871
|
-
const pAccount = bankAccounts.find((a) => a.id === p.bankAccountId);
|
|
1872
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-1 gap-y-0.5 text-[11px] text-muted-foreground", children: [
|
|
1873
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
|
|
1874
|
-
pType && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1875
|
-
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1876
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: pType.name }),
|
|
1877
|
-
pMethod && /* @__PURE__ */ jsxs("span", { children: [
|
|
1878
|
-
"(",
|
|
1879
|
-
pMethod.name,
|
|
1880
|
-
")"
|
|
1881
|
-
] })
|
|
1882
|
-
] }),
|
|
1883
|
-
p.cardBrand && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1884
|
-
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1885
|
-
/* @__PURE__ */ jsx(CreditCard, { className: "h-3 w-3 shrink-0" }),
|
|
1886
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: p.cardBrand }),
|
|
1887
|
-
p.cardInstallments && /* @__PURE__ */ jsxs("span", { children: [
|
|
1888
|
-
p.cardInstallments,
|
|
1889
|
-
"x"
|
|
1858
|
+
mov.dueDate,
|
|
1859
|
+
mov.paidAmount > 0 && !isPaid && ` \xB7 ${t2("financial.invoice.paidLabel")} ${formatCurrency(mov.paidAmount, currency)}`,
|
|
1860
|
+
mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
|
|
1890
1861
|
] })
|
|
1891
1862
|
] }),
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
t2("financial.
|
|
1901
|
-
" ",
|
|
1902
|
-
|
|
1863
|
+
!isPaid && invoice.status !== "cancelled" && /* @__PURE__ */ jsx(
|
|
1864
|
+
"button",
|
|
1865
|
+
{
|
|
1866
|
+
onClick: (e) => {
|
|
1867
|
+
e.stopPropagation();
|
|
1868
|
+
setReceiptFor(mov);
|
|
1869
|
+
receiptPicker.open();
|
|
1870
|
+
},
|
|
1871
|
+
title: t2("financial.quickTx.receiptCapture"),
|
|
1872
|
+
"aria-label": t2("financial.quickTx.receiptCapture"),
|
|
1873
|
+
className: "inline-flex shrink-0 items-center gap-1 rounded-lg border bg-card px-2.5 py-1.5 text-xs font-medium shadow-button transition-colors hover:bg-muted active:shadow-button-inset",
|
|
1874
|
+
children: /* @__PURE__ */ jsx(Camera, { className: "h-3 w-3" })
|
|
1875
|
+
}
|
|
1876
|
+
),
|
|
1877
|
+
!isPaid && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
|
|
1878
|
+
"button",
|
|
1879
|
+
{
|
|
1880
|
+
onClick: (e) => {
|
|
1881
|
+
e.stopPropagation();
|
|
1882
|
+
setPayingMovement(mov);
|
|
1883
|
+
},
|
|
1884
|
+
className: "inline-flex items-center gap-1 rounded-button bg-primary border border-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors shrink-0",
|
|
1885
|
+
children: [
|
|
1886
|
+
/* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
|
|
1887
|
+
t2("financial.invoice.pay"),
|
|
1888
|
+
" ",
|
|
1889
|
+
remaining < mov.amount ? formatCurrency(remaining, currency) : ""
|
|
1890
|
+
]
|
|
1891
|
+
}
|
|
1892
|
+
),
|
|
1893
|
+
isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
|
|
1894
|
+
"button",
|
|
1895
|
+
{
|
|
1896
|
+
onClick: (e) => {
|
|
1897
|
+
e.stopPropagation();
|
|
1898
|
+
setPayingMovement(mov);
|
|
1899
|
+
},
|
|
1900
|
+
className: "inline-flex items-center gap-1 rounded-lg border px-3 py-1.5 text-xs font-medium hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors shrink-0",
|
|
1901
|
+
children: [
|
|
1902
|
+
/* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
|
|
1903
|
+
t2("financial.invoice.pay"),
|
|
1904
|
+
" ",
|
|
1905
|
+
formatCurrency(remaining, currency)
|
|
1906
|
+
]
|
|
1907
|
+
}
|
|
1908
|
+
),
|
|
1909
|
+
hasPaidDetails && /* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 text-muted-foreground shrink-0 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })
|
|
1910
|
+
]
|
|
1911
|
+
}
|
|
1912
|
+
),
|
|
1913
|
+
/* @__PURE__ */ jsx(
|
|
1914
|
+
"div",
|
|
1915
|
+
{
|
|
1916
|
+
className: "grid transition-all duration-200 ease-out",
|
|
1917
|
+
style: { gridTemplateRows: isExpanded ? "1fr" : "0fr" },
|
|
1918
|
+
children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-2.5 pl-14", children: detailsLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1919
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-20 rounded bg-muted/40 animate-pulse" }),
|
|
1920
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-16 rounded bg-muted/30 animate-pulse" }),
|
|
1921
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-24 rounded bg-muted/40 animate-pulse" })
|
|
1922
|
+
] }) : instPayments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-[11px] text-muted-foreground", children: t2("financial.invoice.noDetails") }) : /* @__PURE__ */ jsx("div", { className: "space-y-1", children: instPayments.map((p) => {
|
|
1923
|
+
const pMethod = paymentMethods.find((m) => m.id === p.paymentMethodId);
|
|
1924
|
+
const pType = paymentMethodTypes.find((tp) => tp.id === (pMethod?.paymentMethodTypeId ?? p.paymentMethodTypeId));
|
|
1925
|
+
const pAccount = bankAccounts.find((a) => a.id === p.bankAccountId);
|
|
1926
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-1 gap-y-0.5 text-[11px] text-muted-foreground", children: [
|
|
1927
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
|
|
1928
|
+
pType && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1929
|
+
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1930
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: pType.name }),
|
|
1931
|
+
pMethod && /* @__PURE__ */ jsxs("span", { children: [
|
|
1932
|
+
"(",
|
|
1933
|
+
pMethod.name,
|
|
1934
|
+
")"
|
|
1935
|
+
] })
|
|
1936
|
+
] }),
|
|
1937
|
+
p.cardBrand && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1938
|
+
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1939
|
+
/* @__PURE__ */ jsx(CreditCard, { className: "h-3 w-3 shrink-0" }),
|
|
1940
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: p.cardBrand }),
|
|
1941
|
+
p.cardInstallments && /* @__PURE__ */ jsxs("span", { children: [
|
|
1942
|
+
p.cardInstallments,
|
|
1943
|
+
"x"
|
|
1944
|
+
] })
|
|
1945
|
+
] }),
|
|
1946
|
+
pAccount && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1947
|
+
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1948
|
+
/* @__PURE__ */ jsx(Building2, { className: "h-3 w-3 shrink-0" }),
|
|
1949
|
+
/* @__PURE__ */ jsx("span", { children: pAccount.name })
|
|
1950
|
+
] }),
|
|
1951
|
+
(p.feeAmount ?? 0) > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1952
|
+
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1953
|
+
/* @__PURE__ */ jsxs("span", { className: "text-warning", children: [
|
|
1954
|
+
t2("financial.statements.columnFee"),
|
|
1955
|
+
" ",
|
|
1956
|
+
formatCurrency(p.feeAmount ?? 0, currency)
|
|
1957
|
+
] })
|
|
1958
|
+
] }),
|
|
1959
|
+
p.paymentDate && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1960
|
+
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1961
|
+
/* @__PURE__ */ jsx("span", { children: p.paymentDate })
|
|
1962
|
+
] }),
|
|
1963
|
+
p.notes && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1964
|
+
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1965
|
+
/* @__PURE__ */ jsx("span", { className: "italic truncate max-w-[200px]", children: p.notes })
|
|
1903
1966
|
] })
|
|
1904
|
-
] })
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1967
|
+
] }, p.id);
|
|
1968
|
+
}) }) }) })
|
|
1969
|
+
}
|
|
1970
|
+
)
|
|
1971
|
+
] }, mov.id);
|
|
1972
|
+
}) }),
|
|
1973
|
+
settlements.unattributed.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t bg-warning-soft/40 px-4 py-2.5", children: [
|
|
1974
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-foreground", children: t2("financial.invoice.unlinkedPayments") }),
|
|
1975
|
+
/* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] text-muted-foreground", children: t2("financial.invoice.unlinkedPaymentsDesc") }),
|
|
1976
|
+
settlements.unattributed.map((p) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
|
|
1977
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-success", children: formatCurrency(p.paidAmount, currency) }),
|
|
1978
|
+
p.paymentDate && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1979
|
+
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
1980
|
+
/* @__PURE__ */ jsx("span", { children: p.paymentDate })
|
|
1981
|
+
] })
|
|
1982
|
+
] }, p.id))
|
|
1983
|
+
] })
|
|
1984
|
+
] }) })
|
|
1919
1985
|
),
|
|
1920
1986
|
extraSections: sideBlocks
|
|
1921
1987
|
}
|
|
@@ -2176,7 +2242,7 @@ function CashOpeningPanel({
|
|
|
2176
2242
|
{
|
|
2177
2243
|
onClick: handleConfirm,
|
|
2178
2244
|
disabled: submitting,
|
|
2179
|
-
className: "inline-flex items-center gap-1.5 rounded-
|
|
2245
|
+
className: "inline-flex items-center gap-1.5 rounded-button bg-primary border border-primary px-4 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors disabled:opacity-50",
|
|
2180
2246
|
children: [
|
|
2181
2247
|
/* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
|
|
2182
2248
|
" ",
|
|
@@ -2277,7 +2343,7 @@ function CashOpeningPanel({
|
|
|
2277
2343
|
{
|
|
2278
2344
|
onClick: () => setConfirming(true),
|
|
2279
2345
|
disabled: effectiveBalance <= 0,
|
|
2280
|
-
className: "inline-flex items-center gap-1.5 rounded-
|
|
2346
|
+
className: "inline-flex items-center gap-1.5 rounded-button bg-primary border border-primary px-4 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors disabled:opacity-50",
|
|
2281
2347
|
children: [
|
|
2282
2348
|
/* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }),
|
|
2283
2349
|
" ",
|
|
@@ -2420,7 +2486,7 @@ function CashRegistersView() {
|
|
|
2420
2486
|
}
|
|
2421
2487
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
2422
2488
|
/* @__PURE__ */ jsx(SubpageHeader, { title: labels.cashRegisters ?? t2("financial.cash.title"), subtitle: t2("financial.cash.subtitle") }),
|
|
2423
|
-
/* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
2489
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm", children: [
|
|
2424
2490
|
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-4 py-3 border-b", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2425
2491
|
/* @__PURE__ */ jsx(Landmark, { className: "h-4 w-4 text-info" }),
|
|
2426
2492
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.openSessions") }),
|
|
@@ -2457,7 +2523,7 @@ function CashRegistersView() {
|
|
|
2457
2523
|
] }, session.id)) })
|
|
2458
2524
|
] }),
|
|
2459
2525
|
openSessions.length > 0 && cashAccounts.length > openSessions.length && /* @__PURE__ */ jsx(OpenAnotherCollapsible, { cashAccounts, openSessions, currency, onOpen: handleOpen }),
|
|
2460
|
-
/* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
2526
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm", children: [
|
|
2461
2527
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
|
|
2462
2528
|
/* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-muted-foreground" }),
|
|
2463
2529
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.cash.sessionHistory") })
|
|
@@ -2511,7 +2577,7 @@ function OpenAnotherCollapsible({
|
|
|
2511
2577
|
const [expanded, setExpanded] = useState(false);
|
|
2512
2578
|
const availableAccounts = cashAccounts.filter((a) => !openSessions.some((s) => s.bankAccountId === a.id));
|
|
2513
2579
|
if (availableAccounts.length === 0) return null;
|
|
2514
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
2580
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm", children: [
|
|
2515
2581
|
/* @__PURE__ */ jsxs(
|
|
2516
2582
|
"button",
|
|
2517
2583
|
{
|
|
@@ -2738,7 +2804,7 @@ function StatementsView({ onNavigate }) {
|
|
|
2738
2804
|
const e = row.original;
|
|
2739
2805
|
const isTransfer = e.entryKind !== "movement";
|
|
2740
2806
|
const memo = e.movement.notes;
|
|
2741
|
-
const title = e.invoice?.contactName || (isTransfer ? t2("financial.statements.transfer") : memo || "
|
|
2807
|
+
const title = e.invoice?.contactName || (isTransfer ? t2("financial.statements.transfer") : memo || t2("financial.statements.untitledEntry"));
|
|
2742
2808
|
const memoLine = memo && memo !== title ? memo : void 0;
|
|
2743
2809
|
const doc = e.invoice?.fiscalNumber;
|
|
2744
2810
|
const inst = e.movement.installmentNumber;
|
|
@@ -2948,7 +3014,12 @@ function CommissionsView() {
|
|
|
2948
3014
|
const { labels } = useFinancialConfig();
|
|
2949
3015
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
2950
3016
|
/* @__PURE__ */ jsx(SubpageHeader, { title: labels.commissions, subtitle: t2("financial.commissions.subtitle") }),
|
|
2951
|
-
/* @__PURE__ */
|
|
3017
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-muted px-6 py-12 text-center", children: [
|
|
3018
|
+
/* @__PURE__ */ jsx("div", { className: "mb-3 flex h-12 w-12 items-center justify-center rounded-xl bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(Users, { className: "h-6 w-6" }) }),
|
|
3019
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t2("financial.commissions.emptyTitle") }),
|
|
3020
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-xs text-muted-foreground", children: t2("financial.commissions.emptyDesc") }),
|
|
3021
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 max-w-sm text-xs text-muted-foreground", children: t2("financial.commissions.emptyRulesHint") })
|
|
3022
|
+
] })
|
|
2952
3023
|
] });
|
|
2953
3024
|
}
|
|
2954
3025
|
function cardMath(acc) {
|
|
@@ -2990,7 +3061,7 @@ function CardsView({ onNavigate }) {
|
|
|
2990
3061
|
}
|
|
2991
3062
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
2992
3063
|
/* @__PURE__ */ jsx(SubpageHeader, { title: t2("financial.cards.title"), subtitle: t2("financial.cards.subtitle") }),
|
|
2993
|
-
bankAccountsLoading && cards.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-sm text-muted-foreground", children: "
|
|
3064
|
+
bankAccountsLoading && cards.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-sm text-muted-foreground", children: t2("financial.cards.loading") }) : cards.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center rounded-2xl border-2 border-dashed border-muted", children: [
|
|
2994
3065
|
/* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-amber-300 to-yellow-600 mb-3 text-white shadow-sm", children: /* @__PURE__ */ jsx(CreditCard, { className: "h-6 w-6" }) }),
|
|
2995
3066
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t2("financial.cards.noCards") }),
|
|
2996
3067
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 max-w-xs", children: t2("financial.cards.noCardsHint") })
|
|
@@ -3152,12 +3223,12 @@ function ReconciliationView() {
|
|
|
3152
3223
|
] })
|
|
3153
3224
|
] })
|
|
3154
3225
|
] }),
|
|
3155
|
-
lines.length === 0 && !loading ? /* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
3226
|
+
lines.length === 0 && !loading ? /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm py-16 text-center", children: [
|
|
3156
3227
|
/* @__PURE__ */ jsx(Check, { className: "h-8 w-8 text-success/40 mx-auto mb-2" }),
|
|
3157
3228
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t2("financial.reconciliation.empty") }),
|
|
3158
3229
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: t2("financial.reconciliation.emptyHint") })
|
|
3159
3230
|
] }) : /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
|
|
3160
|
-
/* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
3231
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm overflow-hidden", children: [
|
|
3161
3232
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
|
|
3162
3233
|
/* @__PURE__ */ jsx(Landmark, { className: "h-4 w-4 text-muted-foreground" }),
|
|
3163
3234
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.reconciliation.bankLines") })
|
|
@@ -3188,7 +3259,7 @@ function ReconciliationView() {
|
|
|
3188
3259
|
);
|
|
3189
3260
|
}) })
|
|
3190
3261
|
] }),
|
|
3191
|
-
/* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
3262
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card shadow-sm overflow-hidden", children: [
|
|
3192
3263
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b", children: [
|
|
3193
3264
|
/* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 text-muted-foreground" }),
|
|
3194
3265
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t2("financial.reconciliation.candidates") })
|
|
@@ -3537,10 +3608,8 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
3537
3608
|
id: "commissions",
|
|
3538
3609
|
label: t2("financial.nav.commissions"),
|
|
3539
3610
|
icon: "Users",
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
{ id: "commissions-rules", label: t2("financial.nav.rules"), active: activeView === "commissions", onClick: () => navigate("commissions") }
|
|
3543
|
-
]
|
|
3611
|
+
active: activeView === "commissions",
|
|
3612
|
+
onClick: () => navigate("commissions")
|
|
3544
3613
|
});
|
|
3545
3614
|
}
|
|
3546
3615
|
if (config.modules.cards) {
|
|
@@ -3548,10 +3617,8 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
3548
3617
|
id: "cards",
|
|
3549
3618
|
label: t2("financial.nav.cards"),
|
|
3550
3619
|
icon: "CreditCard",
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
{ id: "cards-reconciliation", label: t2("financial.nav.reconciliation"), active: activeView === "cards", onClick: () => navigate("cards") }
|
|
3554
|
-
]
|
|
3620
|
+
active: activeView === "cards",
|
|
3621
|
+
onClick: () => navigate("cards")
|
|
3555
3622
|
});
|
|
3556
3623
|
}
|
|
3557
3624
|
for (const item of items) {
|
|
@@ -3646,7 +3713,6 @@ function FinancialPage({ config, provider, store: store6, registries }) {
|
|
|
3646
3713
|
PluginSettingsPanel,
|
|
3647
3714
|
{
|
|
3648
3715
|
title: t2("financial.settingsPage.title"),
|
|
3649
|
-
subtitle: t2("financial.settingsPage.subtitle"),
|
|
3650
3716
|
generalSettings: /* @__PURE__ */ jsx(FinancialGeneralSettings, {}),
|
|
3651
3717
|
registries,
|
|
3652
3718
|
hostPluginId: "financial",
|
|
@@ -4321,6 +4387,7 @@ function createMockFinancialProvider(options) {
|
|
|
4321
4387
|
paidAmount: input.amount,
|
|
4322
4388
|
feeAmount: thisFee,
|
|
4323
4389
|
status: "paid",
|
|
4390
|
+
settlesMovementId: bill.id,
|
|
4324
4391
|
dueDate: bill.dueDate,
|
|
4325
4392
|
paymentDate: input.paymentDate,
|
|
4326
4393
|
installmentNumber: bill.installmentNumber,
|
|
@@ -5018,6 +5085,11 @@ function createSupabaseFinancialProvider() {
|
|
|
5018
5085
|
party_id: input.contactId,
|
|
5019
5086
|
notes: input.observations,
|
|
5020
5087
|
currency: "BRL",
|
|
5088
|
+
// The form has an invoice-date picker and this used to ignore it, so
|
|
5089
|
+
// every invoice was dated the day it was typed. `invoiceDate` is read
|
|
5090
|
+
// back off created_at (see mapping below) and is what the date filters
|
|
5091
|
+
// and the payables history sort by, so it has to be written here.
|
|
5092
|
+
...input.invoiceDate ? { created_at: (/* @__PURE__ */ new Date(`${input.invoiceDate}T12:00:00Z`)).toISOString() } : {},
|
|
5021
5093
|
metadata: { itemsSummary, contactName: input.contactName, installmentCount: input.installments.length || 1 }
|
|
5022
5094
|
}).select().single();
|
|
5023
5095
|
if (order && input.items.length > 0) {
|
|
@@ -5060,7 +5132,7 @@ function createSupabaseFinancialProvider() {
|
|
|
5060
5132
|
return {
|
|
5061
5133
|
id: order.id,
|
|
5062
5134
|
direction: input.direction,
|
|
5063
|
-
invoiceDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
5135
|
+
invoiceDate: input.invoiceDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
5064
5136
|
fiscalNumber: referenceNumber,
|
|
5065
5137
|
totalAmount,
|
|
5066
5138
|
paidAmount: 0,
|
|
@@ -5132,11 +5204,17 @@ function createSupabaseFinancialProvider() {
|
|
|
5132
5204
|
card_brand: input.cardBrand ?? null,
|
|
5133
5205
|
card_installments: input.cardInstallments ?? null,
|
|
5134
5206
|
notes: input.notes ?? null,
|
|
5135
|
-
metadata: input.metadata ?? null
|
|
5207
|
+
metadata: input.metadata ?? null,
|
|
5208
|
+
// Which bill this settles. Without it the pairing has to be re-derived
|
|
5209
|
+
// from (invoice_id, installment_number), which is ambiguous the moment
|
|
5210
|
+
// one installment takes two payments. See migration 014.
|
|
5211
|
+
settles_movement_id: mov.id
|
|
5136
5212
|
};
|
|
5137
5213
|
let { data, error } = await pub.from(T.movements).insert(paymentRow).select().single();
|
|
5138
|
-
if (error && /fee_amount/i.test(error.message ?? "")) {
|
|
5139
|
-
const {
|
|
5214
|
+
if (error && /fee_amount|settles_movement_id/i.test(error.message ?? "")) {
|
|
5215
|
+
const rest = { ...paymentRow };
|
|
5216
|
+
if (/fee_amount/i.test(error.message ?? "")) delete rest.fee_amount;
|
|
5217
|
+
if (/settles_movement_id/i.test(error.message ?? "")) delete rest.settles_movement_id;
|
|
5140
5218
|
({ data, error } = await pub.from(T.movements).insert(rest).select().single());
|
|
5141
5219
|
}
|
|
5142
5220
|
if (error) throw error;
|
|
@@ -5929,7 +6007,17 @@ var chartOfAccountsEntity = {
|
|
|
5929
6007
|
fields: [
|
|
5930
6008
|
{ key: "code", label: "Code", type: "text", required: true, showInTable: true },
|
|
5931
6009
|
{ key: "name", label: "Name", type: "text", required: true, showInTable: true },
|
|
5932
|
-
{ key: "nodeType", label: "
|
|
6010
|
+
{ key: "nodeType", label: "Structure", type: "select", options: ["group", "leaf"], showInTable: true, required: true, defaultValue: "leaf" },
|
|
6011
|
+
// What the account IS, as opposed to where it sits in the tree (nodeType).
|
|
6012
|
+
// Migration 013 leaves it unconstrained; the picker offers the five the
|
|
6013
|
+
// module reasons about.
|
|
6014
|
+
{ key: "accountType", label: "Nature", type: "select", options: [
|
|
6015
|
+
{ label: "Revenue", value: "revenue" },
|
|
6016
|
+
{ label: "Expense", value: "expense" },
|
|
6017
|
+
{ label: "Asset", value: "asset" },
|
|
6018
|
+
{ label: "Liability", value: "liability" },
|
|
6019
|
+
{ label: "Equity", value: "equity" }
|
|
6020
|
+
], showInTable: true },
|
|
5933
6021
|
{ key: "parentId", label: "Parent", type: "text", showInTable: false },
|
|
5934
6022
|
{ key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
|
|
5935
6023
|
],
|
|
@@ -6051,20 +6139,20 @@ var financialRegistries = [
|
|
|
6051
6139
|
description: "Hierarchical chart of accounts for categorizing transactions",
|
|
6052
6140
|
display: "tree",
|
|
6053
6141
|
seedData: [
|
|
6054
|
-
{ id: "coa-1", code: "1", name: "Revenue", nodeType: "group", isActive: true },
|
|
6055
|
-
{ id: "coa-1.1", code: "1.1", name: "Sales Revenue", nodeType: "group", parentId: "coa-1", isActive: true },
|
|
6056
|
-
{ id: "coa-1.1.01", code: "1.1.01", name: "Services", nodeType: "leaf", parentId: "coa-1.1", isActive: true },
|
|
6057
|
-
{ id: "coa-1.1.02", code: "1.1.02", name: "Products", nodeType: "leaf", parentId: "coa-1.1", isActive: true },
|
|
6058
|
-
{ id: "coa-2", code: "2", name: "Other Revenue", nodeType: "group", isActive: true },
|
|
6059
|
-
{ id: "coa-2.1", code: "2.1", name: "Financial Income", nodeType: "leaf", parentId: "coa-2", isActive: true },
|
|
6060
|
-
{ id: "coa-3", code: "3", name: "Operational Costs", nodeType: "group", isActive: true },
|
|
6061
|
-
{ id: "coa-3.1", code: "3.1", name: "Materials & Supplies", nodeType: "group", parentId: "coa-3", isActive: true },
|
|
6062
|
-
{ id: "coa-3.1.01", code: "3.1.01", name: "Raw Materials", nodeType: "leaf", parentId: "coa-3.1", isActive: true },
|
|
6063
|
-
{ id: "coa-3.1.02", code: "3.1.02", name: "Consumables", nodeType: "leaf", parentId: "coa-3.1", isActive: true },
|
|
6064
|
-
{ id: "coa-4", code: "4", name: "Expenses", nodeType: "group", isActive: true },
|
|
6065
|
-
{ id: "coa-4.1", code: "4.1", name: "Rent", nodeType: "leaf", parentId: "coa-4", isActive: true },
|
|
6066
|
-
{ id: "coa-4.2", code: "4.2", name: "Utilities", nodeType: "leaf", parentId: "coa-4", isActive: true },
|
|
6067
|
-
{ id: "coa-4.3", code: "4.3", name: "Payroll", nodeType: "leaf", parentId: "coa-4", isActive: true }
|
|
6142
|
+
{ id: "coa-1", code: "1", name: "Revenue", nodeType: "group", accountType: "revenue", isActive: true },
|
|
6143
|
+
{ id: "coa-1.1", code: "1.1", name: "Sales Revenue", nodeType: "group", accountType: "revenue", parentId: "coa-1", isActive: true },
|
|
6144
|
+
{ id: "coa-1.1.01", code: "1.1.01", name: "Services", nodeType: "leaf", accountType: "revenue", parentId: "coa-1.1", isActive: true },
|
|
6145
|
+
{ id: "coa-1.1.02", code: "1.1.02", name: "Products", nodeType: "leaf", accountType: "revenue", parentId: "coa-1.1", isActive: true },
|
|
6146
|
+
{ id: "coa-2", code: "2", name: "Other Revenue", nodeType: "group", accountType: "revenue", isActive: true },
|
|
6147
|
+
{ id: "coa-2.1", code: "2.1", name: "Financial Income", nodeType: "leaf", accountType: "revenue", parentId: "coa-2", isActive: true },
|
|
6148
|
+
{ id: "coa-3", code: "3", name: "Operational Costs", nodeType: "group", accountType: "expense", isActive: true },
|
|
6149
|
+
{ id: "coa-3.1", code: "3.1", name: "Materials & Supplies", nodeType: "group", accountType: "expense", parentId: "coa-3", isActive: true },
|
|
6150
|
+
{ id: "coa-3.1.01", code: "3.1.01", name: "Raw Materials", nodeType: "leaf", accountType: "expense", parentId: "coa-3.1", isActive: true },
|
|
6151
|
+
{ id: "coa-3.1.02", code: "3.1.02", name: "Consumables", nodeType: "leaf", accountType: "expense", parentId: "coa-3.1", isActive: true },
|
|
6152
|
+
{ id: "coa-4", code: "4", name: "Expenses", nodeType: "group", accountType: "expense", isActive: true },
|
|
6153
|
+
{ id: "coa-4.1", code: "4.1", name: "Rent", nodeType: "leaf", accountType: "expense", parentId: "coa-4", isActive: true },
|
|
6154
|
+
{ id: "coa-4.2", code: "4.2", name: "Utilities", nodeType: "leaf", accountType: "expense", parentId: "coa-4", isActive: true },
|
|
6155
|
+
{ id: "coa-4.3", code: "4.3", name: "Payroll", nodeType: "leaf", accountType: "expense", parentId: "coa-4", isActive: true }
|
|
6068
6156
|
]
|
|
6069
6157
|
},
|
|
6070
6158
|
{
|
|
@@ -6152,6 +6240,7 @@ var en = {
|
|
|
6152
6240
|
"financial.cards.currentBill": "Current bill",
|
|
6153
6241
|
"financial.cards.dueOn": "Due on the {{day}}",
|
|
6154
6242
|
"financial.cards.installment": "Installment {{current}}/{{total}}",
|
|
6243
|
+
"financial.cards.loading": "Loading...",
|
|
6155
6244
|
"financial.cards.noCards": "No cards yet",
|
|
6156
6245
|
"financial.cards.noCardsHint": "Add a credit card in settings to track your bills here",
|
|
6157
6246
|
"financial.cards.noTransactions": "No card transactions",
|
|
@@ -6193,10 +6282,17 @@ var en = {
|
|
|
6193
6282
|
"financial.cashRegisters.register": "Register",
|
|
6194
6283
|
"financial.cashRegisters.sessionHistory": "Session History",
|
|
6195
6284
|
"financial.cashRegisters.subtitle": "Open, count and close cash sessions",
|
|
6196
|
-
"financial.commissions.
|
|
6285
|
+
"financial.commissions.emptyTitle": "No commissions calculated",
|
|
6286
|
+
"financial.commissions.emptyDesc": "This module tracks commission rules, but nothing here calculates a commission from your sales yet.",
|
|
6287
|
+
"financial.commissions.emptyRulesHint": "Commission rules are managed under Settings \u203A Financial \u203A Properties.",
|
|
6197
6288
|
"financial.commissions.subtitle": "Team commissions",
|
|
6198
6289
|
"financial.invoice.accountsPayable": "Accounts Payable",
|
|
6199
6290
|
"financial.invoice.accountsReceivable": "Accounts Receivable",
|
|
6291
|
+
"financial.invoice.bulkCancel": "Cancel",
|
|
6292
|
+
"financial.invoice.bulkCancelDone": "{{count}} invoice(s) cancelled",
|
|
6293
|
+
"financial.invoice.bulkCancelFailed": "{{count}} invoice(s) could not be cancelled",
|
|
6294
|
+
"financial.invoice.bulkCancelNone": "None of the selected invoices can be cancelled.",
|
|
6295
|
+
"financial.invoice.bulkCancelTitle": "Cancel {{count}} invoice(s)?",
|
|
6200
6296
|
"financial.invoice.cancelConfirm": "This action cannot be undone.",
|
|
6201
6297
|
"financial.invoice.cancelDesc": "The invoice will be marked as cancelled. All pending installments will also be cancelled. This invoice will no longer count towards totals.",
|
|
6202
6298
|
"financial.invoice.cancelInvoice": "Cancel Invoice",
|
|
@@ -6226,6 +6322,8 @@ var en = {
|
|
|
6226
6322
|
"financial.invoiceForm.clear": "Clear",
|
|
6227
6323
|
"financial.invoice.newManualHint": "Open the full invoice form",
|
|
6228
6324
|
"financial.invoice.noDetails": "No additional details recorded",
|
|
6325
|
+
"financial.invoice.unlinkedPayments": "Payments not linked to an installment",
|
|
6326
|
+
"financial.invoice.unlinkedPaymentsDesc": "Money recorded against this invoice that does not name one of the installments above.",
|
|
6229
6327
|
"financial.invoice.noInstallments": "No installments",
|
|
6230
6328
|
"financial.invoice.noInvoices": "No invoices yet",
|
|
6231
6329
|
"financial.invoice.summaryTotal": "Total",
|
|
@@ -6530,6 +6628,8 @@ var en = {
|
|
|
6530
6628
|
"financial.invoiceForm.cancelling": "Cancelling...",
|
|
6531
6629
|
"financial.invoiceForm.removeAdjustments": "Remove adjustments",
|
|
6532
6630
|
"financial.invoiceForm.discount": "Discount",
|
|
6631
|
+
"financial.invoiceForm.account": "Chart account",
|
|
6632
|
+
"financial.invoiceForm.accountNone": "Not assigned",
|
|
6533
6633
|
"financial.invoiceForm.surcharge": "Surcharge",
|
|
6534
6634
|
"financial.invoiceForm.total": "Total:",
|
|
6535
6635
|
"financial.invoiceForm.itemCount": "{{count}} item{{plural}}",
|
|
@@ -6603,6 +6703,7 @@ var en = {
|
|
|
6603
6703
|
"financial.statements.totalFees": "Fees",
|
|
6604
6704
|
"financial.statements.net": "Net",
|
|
6605
6705
|
"financial.statements.transfer": "Transfer",
|
|
6706
|
+
"financial.statements.untitledEntry": "Transaction",
|
|
6606
6707
|
"financial.statements.reconciled": "Reconciled with the bank statement",
|
|
6607
6708
|
"financial.statements.imported": "Imported from the bank \xB7 not reconciled",
|
|
6608
6709
|
"financial.statements.reconciledWith": "Reconciled with {{ref}}",
|
|
@@ -6664,6 +6765,7 @@ var ptBR = {
|
|
|
6664
6765
|
"financial.cards.currentBill": "Fatura atual",
|
|
6665
6766
|
"financial.cards.dueOn": "Vence dia {{day}}",
|
|
6666
6767
|
"financial.cards.installment": "Parcela {{current}}/{{total}}",
|
|
6768
|
+
"financial.cards.loading": "Carregando...",
|
|
6667
6769
|
"financial.cards.noCards": "Nenhum cart\xE3o cadastrado",
|
|
6668
6770
|
"financial.cards.noCardsHint": "Adicione um cart\xE3o de cr\xE9dito nas configura\xE7\xF5es para acompanhar suas faturas aqui",
|
|
6669
6771
|
"financial.cards.noTransactions": "Nenhuma transa\xE7\xE3o com cart\xE3o",
|
|
@@ -6705,10 +6807,17 @@ var ptBR = {
|
|
|
6705
6807
|
"financial.cashRegisters.register": "Caixa",
|
|
6706
6808
|
"financial.cashRegisters.sessionHistory": "Hist\xF3rico de Sess\xF5es",
|
|
6707
6809
|
"financial.cashRegisters.subtitle": "Abrir, contar e fechar sess\xF5es de caixa",
|
|
6708
|
-
"financial.commissions.
|
|
6810
|
+
"financial.commissions.emptyTitle": "Nenhuma comiss\xE3o calculada",
|
|
6811
|
+
"financial.commissions.emptyDesc": "Este m\xF3dulo guarda as regras de comiss\xE3o, mas ainda n\xE3o h\xE1 nada que calcule comiss\xE3o a partir das suas vendas.",
|
|
6812
|
+
"financial.commissions.emptyRulesHint": "As regras de comiss\xE3o ficam em Configura\xE7\xF5es \u203A Financeiro \u203A Propriedades.",
|
|
6709
6813
|
"financial.commissions.subtitle": "Comiss\xF5es da equipe",
|
|
6710
6814
|
"financial.invoice.accountsPayable": "Contas a Pagar",
|
|
6711
6815
|
"financial.invoice.accountsReceivable": "Contas a Receber",
|
|
6816
|
+
"financial.invoice.bulkCancel": "Cancelar",
|
|
6817
|
+
"financial.invoice.bulkCancelDone": "{{count}} fatura(s) cancelada(s)",
|
|
6818
|
+
"financial.invoice.bulkCancelFailed": "{{count}} fatura(s) n\xE3o puderam ser canceladas",
|
|
6819
|
+
"financial.invoice.bulkCancelNone": "Nenhuma das faturas selecionadas pode ser cancelada.",
|
|
6820
|
+
"financial.invoice.bulkCancelTitle": "Cancelar {{count}} fatura(s)?",
|
|
6712
6821
|
"financial.invoice.cancelConfirm": "Esta a\xE7\xE3o n\xE3o pode ser desfeita.",
|
|
6713
6822
|
"financial.invoice.cancelDesc": "A fatura ser\xE1 marcada como cancelada. Todas as parcelas pendentes tamb\xE9m ser\xE3o canceladas. Esta fatura n\xE3o contar\xE1 mais nos totais.",
|
|
6714
6823
|
"financial.invoice.cancelInvoice": "Cancelar Fatura",
|
|
@@ -6738,6 +6847,8 @@ var ptBR = {
|
|
|
6738
6847
|
"financial.invoiceForm.clear": "Limpar",
|
|
6739
6848
|
"financial.invoice.newManualHint": "Preencher os campos da conta",
|
|
6740
6849
|
"financial.invoice.noDetails": "Nenhum detalhe adicional registrado",
|
|
6850
|
+
"financial.invoice.unlinkedPayments": "Pagamentos sem parcela vinculada",
|
|
6851
|
+
"financial.invoice.unlinkedPaymentsDesc": "Dinheiro registrado nesta fatura que n\xE3o aponta para nenhuma das parcelas acima.",
|
|
6741
6852
|
"financial.invoice.noInstallments": "Sem parcelas",
|
|
6742
6853
|
"financial.invoice.noInvoices": "Nenhuma fatura ainda",
|
|
6743
6854
|
"financial.invoice.summaryTotal": "Total",
|
|
@@ -7045,6 +7156,8 @@ var ptBR = {
|
|
|
7045
7156
|
"financial.invoiceForm.cancelling": "Cancelando...",
|
|
7046
7157
|
"financial.invoiceForm.removeAdjustments": "Remover ajustes",
|
|
7047
7158
|
"financial.invoiceForm.discount": "Desconto",
|
|
7159
|
+
"financial.invoiceForm.account": "Conta cont\xE1bil",
|
|
7160
|
+
"financial.invoiceForm.accountNone": "N\xE3o atribu\xEDda",
|
|
7048
7161
|
"financial.invoiceForm.surcharge": "Acr\xE9scimo",
|
|
7049
7162
|
"financial.invoiceForm.total": "Total:",
|
|
7050
7163
|
"financial.invoiceForm.itemCount": "{{count}} item{{plural}}",
|
|
@@ -7118,6 +7231,7 @@ var ptBR = {
|
|
|
7118
7231
|
"financial.statements.totalFees": "Taxas",
|
|
7119
7232
|
"financial.statements.net": "L\xEDquido",
|
|
7120
7233
|
"financial.statements.transfer": "Transfer\xEAncia",
|
|
7234
|
+
"financial.statements.untitledEntry": "Lan\xE7amento",
|
|
7121
7235
|
"financial.statements.reconciled": "Conciliado com o extrato banc\xE1rio",
|
|
7122
7236
|
"financial.statements.imported": "Importado do banco \xB7 n\xE3o conciliado",
|
|
7123
7237
|
"financial.statements.reconciledWith": "Conciliado com {{ref}}",
|
|
@@ -7773,7 +7887,10 @@ function resolveConfig(options) {
|
|
|
7773
7887
|
receivables: options?.modules?.receivables !== false,
|
|
7774
7888
|
cashRegisters: options?.modules?.cashRegisters !== false,
|
|
7775
7889
|
statements: options?.modules?.statements !== false,
|
|
7776
|
-
|
|
7890
|
+
// Opt-in: commission RULES are a settings registry, but nothing computes a
|
|
7891
|
+
// commission. Defaulting it on advertised a nav item that could only ever
|
|
7892
|
+
// land on an empty screen.
|
|
7893
|
+
commissions: options?.modules?.commissions === true,
|
|
7777
7894
|
cards: options?.modules?.cards !== false,
|
|
7778
7895
|
// Opt-in: only shown when an app explicitly enables reconciliation (bank connector present).
|
|
7779
7896
|
reconciliation: options?.modules?.reconciliation === true,
|
|
@@ -7809,6 +7926,7 @@ function createFinancialPlugin(options) {
|
|
|
7809
7926
|
const PageComponent = () => React17.createElement(FinancialPage, { config, provider, store: store6, registries });
|
|
7810
7927
|
return {
|
|
7811
7928
|
id: "financial",
|
|
7929
|
+
defaultAgentRole: "finance",
|
|
7812
7930
|
name: config.labels.pageTitle,
|
|
7813
7931
|
icon: "DollarSign",
|
|
7814
7932
|
version: "1.0.0",
|
|
@@ -7935,28 +8053,15 @@ function createFinancialPlugin(options) {
|
|
|
7935
8053
|
{ label: "Compare this week's revenue to last week" }
|
|
7936
8054
|
]
|
|
7937
8055
|
},
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
type: "object",
|
|
7948
|
-
properties: {
|
|
7949
|
-
contact: { type: "string", description: "Contact/customer name" },
|
|
7950
|
-
amount: { type: "number", description: "Invoice amount" },
|
|
7951
|
-
description: { type: "string", description: "Invoice description" }
|
|
7952
|
-
},
|
|
7953
|
-
required: ["contact", "amount"]
|
|
7954
|
-
},
|
|
7955
|
-
suggestions: [
|
|
7956
|
-
{ label: "Create an invoice for a new client" }
|
|
7957
|
-
],
|
|
7958
|
-
permission: { feature: "financial", action: "create" }
|
|
7959
|
-
},
|
|
8056
|
+
// (removed) `createInvoice`. It declared mode:'persist' with no handler, no
|
|
8057
|
+
// dataTools entry and no RPC, so buildAgentToolset filtered it out — a
|
|
8058
|
+
// capability advertised to nobody. It was not given an RPC because its
|
|
8059
|
+
// three parameters (contact, amount, description) cannot express what
|
|
8060
|
+
// CreateInvoiceInput requires: `direction` (payable vs receivable),
|
|
8061
|
+
// `invoiceDate`, and `installments[]` with their due dates. An RPC would
|
|
8062
|
+
// have to invent all three, and a bill with a guessed due date is worse
|
|
8063
|
+
// than no bill. Creating an invoice stays the form's job — /financial
|
|
8064
|
+
// #payables-new, which the agent can navigate to.
|
|
7960
8065
|
{
|
|
7961
8066
|
id: "financial.mark-payment-received",
|
|
7962
8067
|
name: "markPaymentReceived",
|
|
@@ -8014,7 +8119,6 @@ function createFinancialPlugin(options) {
|
|
|
8014
8119
|
component: (() => {
|
|
8015
8120
|
const FinancialSettingsTab = () => React17.createElement(PluginSettingsPanel, {
|
|
8016
8121
|
title: "Financial Settings",
|
|
8017
|
-
subtitle: "Payment methods, accounts, and configurations",
|
|
8018
8122
|
generalSettings: React17.createElement(FinancialGeneralSettings),
|
|
8019
8123
|
registries,
|
|
8020
8124
|
hostPluginId: "financial",
|