@fayz-ai/plugin-financial 0.9.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/PaymentModal.d.ts +3 -1
- package/dist/components/PaymentModal.d.ts.map +1 -1
- package/dist/components/ReceiptField.d.ts +17 -0
- package/dist/components/ReceiptField.d.ts.map +1 -0
- package/dist/context.d.ts +0 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +740 -762
- package/dist/index.js.map +1 -1
- package/dist/integrations/acquirers/shared.d.ts +15 -0
- package/dist/integrations/acquirers/shared.d.ts.map +1 -0
- package/dist/integrations/cielo/connectorDef.d.ts +6 -0
- package/dist/integrations/cielo/connectorDef.d.ts.map +1 -0
- package/dist/integrations/mercadopago/connectorDef.d.ts +5 -0
- package/dist/integrations/mercadopago/connectorDef.d.ts.map +1 -0
- package/dist/integrations/pagbank/connectorDef.d.ts +5 -0
- package/dist/integrations/pagbank/connectorDef.d.ts.map +1 -0
- package/dist/integrations/stone/connectorDef.d.ts +6 -0
- package/dist/integrations/stone/connectorDef.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/store.d.ts +1 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/types.d.ts +6 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/views/CardsView.d.ts +3 -1
- package/dist/views/CardsView.d.ts.map +1 -1
- package/dist/views/FinancialPage.d.ts +2 -0
- package/dist/views/FinancialPage.d.ts.map +1 -1
- package/dist/views/InvoiceDetailView.d.ts.map +1 -1
- package/dist/views/InvoiceFormView.d.ts +3 -1
- package/dist/views/InvoiceFormView.d.ts.map +1 -1
- package/dist/views/InvoiceListView.d.ts +3 -1
- package/dist/views/InvoiceListView.d.ts.map +1 -1
- package/dist/views/PayablesView.d.ts.map +1 -1
- package/dist/views/ReceivablesView.d.ts.map +1 -1
- package/dist/views/StatementsView.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/data/capability.test.d.ts +0 -2
- package/dist/data/capability.test.d.ts.map +0 -1
- package/dist/lib/quick-add.d.ts +0 -13
- package/dist/lib/quick-add.d.ts.map +0 -1
- package/dist/views/QuickTransactionForm.d.ts +0 -16
- package/dist/views/QuickTransactionForm.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { Wallet, Plus, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, X, CalendarDays, Paperclip, Camera, Tag, Repeat, ChevronDown, FileText, FileCheck2, ExternalLink, Send, Ban, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, Clock, ArrowLeftRight, CheckCheck, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, CircleAlert, CircleEllipsis, Pencil, LayoutList, Layers, Building2, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
|
|
1
|
+
import React17, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
|
2
|
+
import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, createSafeDataProvider, registerTranslations, registerMessageDefaults, useTranslation, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
|
|
3
|
+
import { toast, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, DataTable, PageTransition, ModulePage, Button, DashboardCanvas, SubpageHeader, Skeleton, PageHeaderActions, Card, FilterChip, DatePicker, Input, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, CurrencyInput, useSaveBar, SearchSelect, ListView, KpiCard, ChartWidget, CardHeader, CardTitle, CardContent, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
|
|
4
|
+
import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, PermissionGate, PersonLink, useLimitGuard, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, OrderDetailSkeleton, OrderDetailBlock, OrderDetailView, QuickActionsButton, invalidateLimit } from '@fayz-ai/admin';
|
|
5
|
+
import { Wallet, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, Plus, FileText, FileCheck2, ExternalLink, Send, Ban, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, ChevronDown, Clock, Paperclip, CheckCheck, X, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, CircleAlert, CircleEllipsis, Pencil, CalendarDays, LayoutList, Layers, Camera, Building2, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
|
|
7
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
7
|
import { createPortal } from 'react-dom';
|
|
9
8
|
import { createStore as createStore$1 } from 'zustand/vanilla';
|
|
@@ -160,489 +159,6 @@ function MobileSummary() {
|
|
|
160
159
|
] }, date)) })
|
|
161
160
|
] });
|
|
162
161
|
}
|
|
163
|
-
function typeTheme(type) {
|
|
164
|
-
switch (type) {
|
|
165
|
-
case "income":
|
|
166
|
-
return { header: "bg-success text-success-foreground", ring: "ring-success", icon: ArrowDownCircle };
|
|
167
|
-
case "transfer":
|
|
168
|
-
return { header: "bg-muted text-foreground", ring: "ring-ring", icon: ArrowLeftRight };
|
|
169
|
-
case "expense":
|
|
170
|
-
default:
|
|
171
|
-
return { header: "bg-destructive text-destructive-foreground", ring: "ring-destructive", icon: ArrowUpCircle };
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function ChipSelect({ icon: Icon, label, value, options, onChange, emptyLabel }) {
|
|
175
|
-
const [open, setOpen] = useState(false);
|
|
176
|
-
const ref = useRef(null);
|
|
177
|
-
useEffect(() => {
|
|
178
|
-
if (!open) return;
|
|
179
|
-
function onDoc(e) {
|
|
180
|
-
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
181
|
-
}
|
|
182
|
-
document.addEventListener("mousedown", onDoc);
|
|
183
|
-
return () => document.removeEventListener("mousedown", onDoc);
|
|
184
|
-
}, [open]);
|
|
185
|
-
const selected = options.find((o) => o.id === value);
|
|
186
|
-
return /* @__PURE__ */ jsxs("div", { ref, className: "relative", children: [
|
|
187
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: label }),
|
|
188
|
-
/* @__PURE__ */ jsxs(
|
|
189
|
-
"button",
|
|
190
|
-
{
|
|
191
|
-
type: "button",
|
|
192
|
-
onClick: () => setOpen((v) => !v),
|
|
193
|
-
className: "mt-1 flex w-full items-center gap-2 rounded-input border border-input bg-card px-3 py-2.5 text-left text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] transition-colors hover:bg-muted/30",
|
|
194
|
-
children: [
|
|
195
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
196
|
-
/* @__PURE__ */ jsx("span", { className: `flex-1 truncate ${selected ? "" : "text-muted-foreground"}`, children: selected?.name ?? emptyLabel }),
|
|
197
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })
|
|
198
|
-
]
|
|
199
|
-
}
|
|
200
|
-
),
|
|
201
|
-
open && /* @__PURE__ */ jsx("div", { className: "fayz-glass-surface absolute bottom-full left-0 right-0 z-50 mb-1 max-h-56 overflow-auto rounded-lg border bg-popover py-1 shadow-lg", children: options.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: emptyLabel }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
202
|
-
value != null && /* @__PURE__ */ jsxs(
|
|
203
|
-
"button",
|
|
204
|
-
{
|
|
205
|
-
onClick: () => {
|
|
206
|
-
onChange(void 0);
|
|
207
|
-
setOpen(false);
|
|
208
|
-
},
|
|
209
|
-
className: "flex w-full items-center gap-2 px-3 py-2.5 text-left text-xs text-muted-foreground hover:bg-muted/50",
|
|
210
|
-
children: [
|
|
211
|
-
/* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" }),
|
|
212
|
-
" ",
|
|
213
|
-
emptyLabel
|
|
214
|
-
]
|
|
215
|
-
}
|
|
216
|
-
),
|
|
217
|
-
options.map((o) => /* @__PURE__ */ jsxs(
|
|
218
|
-
"button",
|
|
219
|
-
{
|
|
220
|
-
onClick: () => {
|
|
221
|
-
onChange(o.id);
|
|
222
|
-
setOpen(false);
|
|
223
|
-
},
|
|
224
|
-
className: `flex w-full items-center gap-2 px-3 py-2.5 text-left text-sm hover:bg-muted/50 ${o.id === value ? "bg-muted/30" : ""}`,
|
|
225
|
-
children: [
|
|
226
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.name }),
|
|
227
|
-
o.hint && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: o.hint }),
|
|
228
|
-
o.id === value && /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-primary" })
|
|
229
|
-
]
|
|
230
|
-
},
|
|
231
|
-
o.id
|
|
232
|
-
))
|
|
233
|
-
] }) })
|
|
234
|
-
] });
|
|
235
|
-
}
|
|
236
|
-
function TogglePill({ icon: Icon, label, checked, onChange, accent }) {
|
|
237
|
-
return /* @__PURE__ */ jsxs(
|
|
238
|
-
"button",
|
|
239
|
-
{
|
|
240
|
-
type: "button",
|
|
241
|
-
onClick: () => onChange(!checked),
|
|
242
|
-
className: `flex items-center gap-2 rounded-full border px-3 py-2 text-xs font-medium transition-colors ${checked ? `border-transparent ${accent ?? "bg-primary text-primary-foreground"}` : "border-input bg-card text-muted-foreground hover:bg-muted/40"}`,
|
|
243
|
-
children: [
|
|
244
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
|
|
245
|
-
label
|
|
246
|
-
]
|
|
247
|
-
}
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
function QuickTransactionForm({ open, onOpenChange, defaultType = "expense", defaultAccountId, focusReceipt = false }) {
|
|
251
|
-
const t2 = useTranslation();
|
|
252
|
-
const { currency } = useFinancialConfig();
|
|
253
|
-
const provider = useFinancialProvider();
|
|
254
|
-
const createQuickTransaction = useFinancialStore((s) => s.createQuickTransaction);
|
|
255
|
-
const guardMovements = useLimitGuard("movements_month");
|
|
256
|
-
const can = usePermissionOptional();
|
|
257
|
-
const [type, setType] = useState(defaultType);
|
|
258
|
-
const [amount, setAmount] = useState(0);
|
|
259
|
-
const [amountStr, setAmountStr] = useState("");
|
|
260
|
-
const [paid, setPaid] = useState(true);
|
|
261
|
-
const [date, setDate] = useState(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
262
|
-
const [description, setDescription] = useState("");
|
|
263
|
-
const [categoryId, setCategoryId] = useState();
|
|
264
|
-
const [accountId, setAccountId] = useState();
|
|
265
|
-
const [toAccountId, setToAccountId] = useState();
|
|
266
|
-
const [recurring, setRecurring] = useState(false);
|
|
267
|
-
const [saving, setSaving] = useState(false);
|
|
268
|
-
const [receiptUrl, setReceiptUrl] = useState();
|
|
269
|
-
const fileInputRef = useRef(null);
|
|
270
|
-
const [accounts, setAccounts] = useState([]);
|
|
271
|
-
const [categories, setCategories] = useState([]);
|
|
272
|
-
useEffect(() => {
|
|
273
|
-
if (!open) return;
|
|
274
|
-
setType(defaultType);
|
|
275
|
-
setAmount(0);
|
|
276
|
-
setAmountStr("");
|
|
277
|
-
setPaid(true);
|
|
278
|
-
setDate((/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
279
|
-
setDescription("");
|
|
280
|
-
setCategoryId(void 0);
|
|
281
|
-
setAccountId(defaultAccountId);
|
|
282
|
-
setToAccountId(void 0);
|
|
283
|
-
setRecurring(false);
|
|
284
|
-
setReceiptUrl(void 0);
|
|
285
|
-
}, [open, defaultType, defaultAccountId]);
|
|
286
|
-
useEffect(() => {
|
|
287
|
-
if (open && focusReceipt) {
|
|
288
|
-
const id = window.setTimeout(() => fileInputRef.current?.click(), 250);
|
|
289
|
-
return () => window.clearTimeout(id);
|
|
290
|
-
}
|
|
291
|
-
}, [open, focusReceipt]);
|
|
292
|
-
function onPickReceipt(e) {
|
|
293
|
-
const file = e.target.files?.[0];
|
|
294
|
-
e.target.value = "";
|
|
295
|
-
if (!file) return;
|
|
296
|
-
if (!file.type.startsWith("image/")) {
|
|
297
|
-
toast.error(t2("financial.quickTx.receiptInvalid"));
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
const reader = new FileReader();
|
|
301
|
-
reader.onload = () => {
|
|
302
|
-
setReceiptUrl(typeof reader.result === "string" ? reader.result : void 0);
|
|
303
|
-
};
|
|
304
|
-
reader.onerror = () => toast.error(t2("financial.quickTx.receiptInvalid"));
|
|
305
|
-
reader.readAsDataURL(file);
|
|
306
|
-
}
|
|
307
|
-
useEffect(() => {
|
|
308
|
-
if (!open) return;
|
|
309
|
-
let cancelled = false;
|
|
310
|
-
void (async () => {
|
|
311
|
-
const [accs, coa] = await Promise.all([
|
|
312
|
-
provider.getBankAccounts(),
|
|
313
|
-
provider.getChartOfAccounts().catch(() => [])
|
|
314
|
-
]);
|
|
315
|
-
if (cancelled) return;
|
|
316
|
-
setAccounts(accs);
|
|
317
|
-
setCategories(coa);
|
|
318
|
-
setAccountId((prev) => prev ?? accs[0]?.id);
|
|
319
|
-
})();
|
|
320
|
-
return () => {
|
|
321
|
-
cancelled = true;
|
|
322
|
-
};
|
|
323
|
-
}, [open, provider]);
|
|
324
|
-
const theme = typeTheme(type);
|
|
325
|
-
const HeaderIcon = theme.icon;
|
|
326
|
-
const accountOptions = useMemo(
|
|
327
|
-
() => accounts.map((a) => ({ id: a.id, name: a.name, hint: a.accountType === "credit_card" ? t2("financial.quickTx.card") : void 0 })),
|
|
328
|
-
[accounts, t2]
|
|
329
|
-
);
|
|
330
|
-
const categoryOptions = useMemo(
|
|
331
|
-
() => categories.filter((c) => c.isActive).map((c) => ({ id: c.id, name: c.name, hint: c.code })),
|
|
332
|
-
[categories]
|
|
333
|
-
);
|
|
334
|
-
function onAmountChange(raw) {
|
|
335
|
-
const cleaned = raw.replace(/[^\d.,]/g, "").replace(",", ".");
|
|
336
|
-
setAmountStr(raw.replace(/[^\d.,]/g, ""));
|
|
337
|
-
const n = Number.parseFloat(cleaned);
|
|
338
|
-
setAmount(Number.isFinite(n) ? n : 0);
|
|
339
|
-
}
|
|
340
|
-
const canSave = amount > 0 && !saving && (type === "transfer" ? !!accountId && !!toAccountId && accountId !== toAccountId : true);
|
|
341
|
-
async function handleSave() {
|
|
342
|
-
if (!can("financial", "create")) return;
|
|
343
|
-
if (!canSave) {
|
|
344
|
-
if (type === "transfer" && accountId && accountId === toAccountId) {
|
|
345
|
-
toast.error(t2("financial.transfer.sameAccount"));
|
|
346
|
-
}
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
if (await guardMovements(type === "transfer" ? 2 : 1) === "blocked") return;
|
|
350
|
-
setSaving(true);
|
|
351
|
-
try {
|
|
352
|
-
await createQuickTransaction({
|
|
353
|
-
type,
|
|
354
|
-
amount,
|
|
355
|
-
date,
|
|
356
|
-
paid: type === "transfer" ? true : paid,
|
|
357
|
-
description: description || void 0,
|
|
358
|
-
categoryId: type === "transfer" ? void 0 : categoryId,
|
|
359
|
-
bankAccountId: accountId,
|
|
360
|
-
toAccountId: type === "transfer" ? toAccountId : void 0,
|
|
361
|
-
recurring: type === "transfer" ? false : recurring,
|
|
362
|
-
receiptUrl: type === "transfer" ? void 0 : receiptUrl
|
|
363
|
-
});
|
|
364
|
-
invalidateLimit("movements_month");
|
|
365
|
-
onOpenChange(false);
|
|
366
|
-
} catch {
|
|
367
|
-
} finally {
|
|
368
|
-
setSaving(false);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
const TYPES = [
|
|
372
|
-
{ id: "expense", label: t2("financial.quickTx.expense") },
|
|
373
|
-
{ id: "income", label: t2("financial.quickTx.income") },
|
|
374
|
-
{ id: "transfer", label: t2("financial.quickTx.transfer") }
|
|
375
|
-
];
|
|
376
|
-
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange, children: /* @__PURE__ */ jsxs(ModalContent, { size: "md", noPadding: true, hideClose: true, className: "overflow-hidden", children: [
|
|
377
|
-
/* @__PURE__ */ jsxs("div", { className: `${theme.header} shrink-0 px-5 pb-5 pt-5`, children: [
|
|
378
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
379
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
380
|
-
/* @__PURE__ */ jsx(HeaderIcon, { className: "h-5 w-5" }),
|
|
381
|
-
/* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: t2("financial.quickTx.title") })
|
|
382
|
-
] }),
|
|
383
|
-
/* @__PURE__ */ jsx(
|
|
384
|
-
"button",
|
|
385
|
-
{
|
|
386
|
-
onClick: () => onOpenChange(false),
|
|
387
|
-
className: "flex h-7 w-7 items-center justify-center rounded-full opacity-80 transition-colors hover:bg-black/10 hover:opacity-100",
|
|
388
|
-
"aria-label": t2("financial.quickTx.close"),
|
|
389
|
-
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
390
|
-
}
|
|
391
|
-
)
|
|
392
|
-
] }),
|
|
393
|
-
/* @__PURE__ */ jsx("div", { className: "mt-4 flex gap-1 rounded-full bg-black/15 p-1", children: TYPES.map((ty) => /* @__PURE__ */ jsx(
|
|
394
|
-
"button",
|
|
395
|
-
{
|
|
396
|
-
onClick: () => setType(ty.id),
|
|
397
|
-
className: `flex-1 rounded-full px-2 py-1.5 text-xs font-medium transition-colors ${type === ty.id ? "bg-white/90 text-foreground shadow-sm" : "opacity-90 hover:bg-white/10"}`,
|
|
398
|
-
children: ty.label
|
|
399
|
-
},
|
|
400
|
-
ty.id
|
|
401
|
-
)) }),
|
|
402
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-5 flex items-baseline justify-center gap-2", children: [
|
|
403
|
-
/* @__PURE__ */ jsx("span", { className: "text-xl font-medium opacity-90", children: currency.symbol }),
|
|
404
|
-
/* @__PURE__ */ jsx(
|
|
405
|
-
"input",
|
|
406
|
-
{
|
|
407
|
-
type: "text",
|
|
408
|
-
inputMode: "decimal",
|
|
409
|
-
autoFocus: true,
|
|
410
|
-
value: amountStr,
|
|
411
|
-
onChange: (e) => onAmountChange(e.target.value),
|
|
412
|
-
onKeyDown: (e) => {
|
|
413
|
-
if (e.key === "Enter") void handleSave();
|
|
414
|
-
},
|
|
415
|
-
placeholder: "0,00",
|
|
416
|
-
className: "w-full max-w-[220px] bg-transparent text-center text-4xl font-bold tracking-tight outline-none placeholder:opacity-40"
|
|
417
|
-
}
|
|
418
|
-
)
|
|
419
|
-
] })
|
|
420
|
-
] }),
|
|
421
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto px-5 py-5", children: [
|
|
422
|
-
type !== "transfer" && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-input border border-input bg-card px-3 py-2.5 shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)]", children: [
|
|
423
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: type === "income" ? t2("financial.quickTx.received") : t2("financial.quickTx.paid") }),
|
|
424
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
425
|
-
/* @__PURE__ */ jsx(
|
|
426
|
-
"button",
|
|
427
|
-
{
|
|
428
|
-
onClick: () => setPaid(true),
|
|
429
|
-
className: `rounded-full px-3 py-1 text-xs font-medium transition-colors ${paid ? "bg-success text-success-foreground" : "bg-muted text-muted-foreground"}`,
|
|
430
|
-
children: t2("financial.quickTx.yes")
|
|
431
|
-
}
|
|
432
|
-
),
|
|
433
|
-
/* @__PURE__ */ jsx(
|
|
434
|
-
"button",
|
|
435
|
-
{
|
|
436
|
-
onClick: () => setPaid(false),
|
|
437
|
-
className: `rounded-full px-3 py-1 text-xs font-medium transition-colors ${!paid ? "bg-warning text-warning-foreground" : "bg-muted text-muted-foreground"}`,
|
|
438
|
-
children: t2("financial.quickTx.no")
|
|
439
|
-
}
|
|
440
|
-
)
|
|
441
|
-
] })
|
|
442
|
-
] }),
|
|
443
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
444
|
-
/* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [
|
|
445
|
-
/* @__PURE__ */ jsx(CalendarDays, { className: "h-3.5 w-3.5" }),
|
|
446
|
-
" ",
|
|
447
|
-
t2("financial.invoiceForm.date")
|
|
448
|
-
] }),
|
|
449
|
-
/* @__PURE__ */ jsx(DatePicker, { value: date, onChange: setDate })
|
|
450
|
-
] }),
|
|
451
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
452
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: t2("financial.invoiceForm.description") }),
|
|
453
|
-
/* @__PURE__ */ jsx(
|
|
454
|
-
"input",
|
|
455
|
-
{
|
|
456
|
-
type: "text",
|
|
457
|
-
value: description,
|
|
458
|
-
onChange: (e) => setDescription(e.target.value),
|
|
459
|
-
onKeyDown: (e) => {
|
|
460
|
-
if (e.key === "Enter") void handleSave();
|
|
461
|
-
},
|
|
462
|
-
placeholder: t2("financial.quickTx.descriptionPlaceholder"),
|
|
463
|
-
className: "mt-1 w-full rounded-input border border-input bg-card px-3 py-2.5 text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] outline-none focus:ring-2 focus:ring-ring"
|
|
464
|
-
}
|
|
465
|
-
)
|
|
466
|
-
] }),
|
|
467
|
-
type !== "transfer" && /* @__PURE__ */ jsxs("div", { children: [
|
|
468
|
-
/* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [
|
|
469
|
-
/* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }),
|
|
470
|
-
" ",
|
|
471
|
-
t2("financial.quickTx.receipt")
|
|
472
|
-
] }),
|
|
473
|
-
/* @__PURE__ */ jsx(
|
|
474
|
-
"input",
|
|
475
|
-
{
|
|
476
|
-
ref: fileInputRef,
|
|
477
|
-
type: "file",
|
|
478
|
-
accept: "image/*",
|
|
479
|
-
capture: "environment",
|
|
480
|
-
className: "hidden",
|
|
481
|
-
onChange: onPickReceipt
|
|
482
|
-
}
|
|
483
|
-
),
|
|
484
|
-
receiptUrl ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-input border border-input bg-card p-2 shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)]", children: [
|
|
485
|
-
/* @__PURE__ */ jsx(
|
|
486
|
-
"img",
|
|
487
|
-
{
|
|
488
|
-
src: receiptUrl,
|
|
489
|
-
alt: t2("financial.quickTx.receipt"),
|
|
490
|
-
className: "h-16 w-16 shrink-0 rounded-md border object-cover"
|
|
491
|
-
}
|
|
492
|
-
),
|
|
493
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
494
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: t2("financial.quickTx.receiptAttached") }),
|
|
495
|
-
/* @__PURE__ */ jsx(
|
|
496
|
-
"button",
|
|
497
|
-
{
|
|
498
|
-
type: "button",
|
|
499
|
-
onClick: () => fileInputRef.current?.click(),
|
|
500
|
-
className: "text-xs text-primary hover:underline",
|
|
501
|
-
children: t2("financial.quickTx.receiptReplace")
|
|
502
|
-
}
|
|
503
|
-
)
|
|
504
|
-
] }),
|
|
505
|
-
/* @__PURE__ */ jsx(
|
|
506
|
-
"button",
|
|
507
|
-
{
|
|
508
|
-
type: "button",
|
|
509
|
-
onClick: () => setReceiptUrl(void 0),
|
|
510
|
-
"aria-label": t2("financial.quickTx.receiptRemove"),
|
|
511
|
-
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted",
|
|
512
|
-
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
513
|
-
}
|
|
514
|
-
)
|
|
515
|
-
] }) : /* @__PURE__ */ jsxs(
|
|
516
|
-
"button",
|
|
517
|
-
{
|
|
518
|
-
type: "button",
|
|
519
|
-
onClick: () => fileInputRef.current?.click(),
|
|
520
|
-
className: `flex w-full items-center justify-center gap-2 rounded-input border border-dashed px-3 py-3 text-sm font-medium transition-colors hover:bg-muted/30 ${focusReceipt ? "border-primary text-primary ring-1 ring-primary/30" : "border-input text-muted-foreground"}`,
|
|
521
|
-
children: [
|
|
522
|
-
/* @__PURE__ */ jsx(Camera, { className: "h-4 w-4" }),
|
|
523
|
-
" ",
|
|
524
|
-
t2("financial.quickTx.receiptCapture")
|
|
525
|
-
]
|
|
526
|
-
}
|
|
527
|
-
)
|
|
528
|
-
] }),
|
|
529
|
-
type === "transfer" ? /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3", children: [
|
|
530
|
-
/* @__PURE__ */ jsx(
|
|
531
|
-
ChipSelect,
|
|
532
|
-
{
|
|
533
|
-
icon: Wallet,
|
|
534
|
-
label: t2("financial.quickTx.fromAccount"),
|
|
535
|
-
value: accountId,
|
|
536
|
-
options: accountOptions,
|
|
537
|
-
onChange: setAccountId,
|
|
538
|
-
emptyLabel: t2("financial.quickTx.selectAccount")
|
|
539
|
-
}
|
|
540
|
-
),
|
|
541
|
-
/* @__PURE__ */ jsx(
|
|
542
|
-
ChipSelect,
|
|
543
|
-
{
|
|
544
|
-
icon: Wallet,
|
|
545
|
-
label: t2("financial.quickTx.toAccount"),
|
|
546
|
-
value: toAccountId,
|
|
547
|
-
options: accountOptions.filter((o) => o.id !== accountId),
|
|
548
|
-
onChange: setToAccountId,
|
|
549
|
-
emptyLabel: t2("financial.quickTx.selectAccount")
|
|
550
|
-
}
|
|
551
|
-
)
|
|
552
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3", children: [
|
|
553
|
-
/* @__PURE__ */ jsx(
|
|
554
|
-
ChipSelect,
|
|
555
|
-
{
|
|
556
|
-
icon: Tag,
|
|
557
|
-
label: t2("financial.quickTx.category"),
|
|
558
|
-
value: categoryId,
|
|
559
|
-
options: categoryOptions,
|
|
560
|
-
onChange: setCategoryId,
|
|
561
|
-
emptyLabel: t2("financial.quickTx.selectCategory")
|
|
562
|
-
}
|
|
563
|
-
),
|
|
564
|
-
/* @__PURE__ */ jsx(
|
|
565
|
-
ChipSelect,
|
|
566
|
-
{
|
|
567
|
-
icon: Wallet,
|
|
568
|
-
label: t2("financial.quickTx.account"),
|
|
569
|
-
value: accountId,
|
|
570
|
-
options: accountOptions,
|
|
571
|
-
onChange: setAccountId,
|
|
572
|
-
emptyLabel: t2("financial.quickTx.selectAccount")
|
|
573
|
-
}
|
|
574
|
-
)
|
|
575
|
-
] }),
|
|
576
|
-
type !== "transfer" && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
577
|
-
/* @__PURE__ */ jsx(
|
|
578
|
-
TogglePill,
|
|
579
|
-
{
|
|
580
|
-
icon: Repeat,
|
|
581
|
-
label: t2("financial.quickTx.recurring"),
|
|
582
|
-
checked: recurring,
|
|
583
|
-
onChange: setRecurring
|
|
584
|
-
}
|
|
585
|
-
),
|
|
586
|
-
/* @__PURE__ */ jsxs(
|
|
587
|
-
"button",
|
|
588
|
-
{
|
|
589
|
-
type: "button",
|
|
590
|
-
onClick: () => fileInputRef.current?.click(),
|
|
591
|
-
title: t2("financial.quickTx.attachment"),
|
|
592
|
-
className: `flex items-center gap-2 rounded-full border px-3 py-2 text-xs font-medium transition-colors ${receiptUrl ? "border-transparent bg-primary text-primary-foreground" : "border-input bg-card text-muted-foreground hover:bg-muted/40"}`,
|
|
593
|
-
children: [
|
|
594
|
-
/* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5" }),
|
|
595
|
-
" ",
|
|
596
|
-
t2("financial.quickTx.attachment"),
|
|
597
|
-
receiptUrl && /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" })
|
|
598
|
-
]
|
|
599
|
-
}
|
|
600
|
-
)
|
|
601
|
-
] })
|
|
602
|
-
] }),
|
|
603
|
-
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-3 border-t px-5 py-3", children: [
|
|
604
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: amount > 0 ? formatCurrency(amount, currency) : t2("financial.quickTx.enterAmount") }),
|
|
605
|
-
/* @__PURE__ */ jsxs(
|
|
606
|
-
Button,
|
|
607
|
-
{
|
|
608
|
-
className: "ml-auto",
|
|
609
|
-
onClick: () => void handleSave(),
|
|
610
|
-
disabled: !canSave,
|
|
611
|
-
children: [
|
|
612
|
-
/* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }),
|
|
613
|
-
saving ? t2("financial.quickTx.saving") : t2("financial.quickTx.save")
|
|
614
|
-
]
|
|
615
|
-
}
|
|
616
|
-
)
|
|
617
|
-
] })
|
|
618
|
-
] }) });
|
|
619
|
-
}
|
|
620
|
-
var pending = null;
|
|
621
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
622
|
-
function emit() {
|
|
623
|
-
for (const l of listeners) l();
|
|
624
|
-
}
|
|
625
|
-
function openQuickAdd(type = "expense", receipt = false) {
|
|
626
|
-
pending = { type, receipt };
|
|
627
|
-
emit();
|
|
628
|
-
}
|
|
629
|
-
function consumeQuickAdd() {
|
|
630
|
-
const req = pending;
|
|
631
|
-
pending = null;
|
|
632
|
-
return req;
|
|
633
|
-
}
|
|
634
|
-
function subscribe(cb) {
|
|
635
|
-
listeners.add(cb);
|
|
636
|
-
return () => {
|
|
637
|
-
listeners.delete(cb);
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
function getSnapshot() {
|
|
641
|
-
return pending;
|
|
642
|
-
}
|
|
643
|
-
function usePendingQuickAdd() {
|
|
644
|
-
return React18.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
645
|
-
}
|
|
646
162
|
var STATUS_OPTIONS = [
|
|
647
163
|
{ value: "open", labelKey: "financial.invoice.statusOpen", color: "bg-info-soft text-info-soft-foreground", icon: CircleDashed },
|
|
648
164
|
{ value: "partial", labelKey: "financial.invoice.statusPartial", color: "bg-warning-soft text-warning-soft-foreground", icon: CircleEllipsis },
|
|
@@ -728,10 +244,12 @@ function useInvoiceColumns(currency) {
|
|
|
728
244
|
}
|
|
729
245
|
], [currency, t2]);
|
|
730
246
|
}
|
|
731
|
-
function InvoiceListView({ direction, onNew, onEdit }) {
|
|
247
|
+
function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
732
248
|
const t2 = useTranslation();
|
|
733
249
|
const can = usePermissionOptional();
|
|
734
|
-
const
|
|
250
|
+
const canCreate = can("financial", "create");
|
|
251
|
+
const onNewGated = canCreate ? onNew : void 0;
|
|
252
|
+
const onNewReceiptGated = canCreate ? onNewReceipt : void 0;
|
|
735
253
|
const { currency } = useFinancialConfig();
|
|
736
254
|
const provider = useFinancialProvider();
|
|
737
255
|
const invoices = useFinancialStore((s) => s.invoices);
|
|
@@ -886,6 +404,29 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
886
404
|
}
|
|
887
405
|
)
|
|
888
406
|
] });
|
|
407
|
+
const newActions = onNewGated ? /* @__PURE__ */ jsx(
|
|
408
|
+
QuickActionsButton,
|
|
409
|
+
{
|
|
410
|
+
variant: "default",
|
|
411
|
+
label: t2("financial.invoice.new"),
|
|
412
|
+
actions: [
|
|
413
|
+
{
|
|
414
|
+
id: "manual",
|
|
415
|
+
label: t2("financial.invoice.newManual"),
|
|
416
|
+
description: t2("financial.invoice.newManualHint"),
|
|
417
|
+
icon: "FileText",
|
|
418
|
+
action: onNewGated
|
|
419
|
+
},
|
|
420
|
+
...onNewReceiptGated ? [{
|
|
421
|
+
id: "receipt",
|
|
422
|
+
label: t2("financial.quickTx.sendReceipt"),
|
|
423
|
+
description: t2("financial.quickTx.receiptCapture"),
|
|
424
|
+
icon: "Camera",
|
|
425
|
+
action: onNewReceiptGated
|
|
426
|
+
}] : []
|
|
427
|
+
]
|
|
428
|
+
}
|
|
429
|
+
) : void 0;
|
|
889
430
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
890
431
|
/* @__PURE__ */ jsx(
|
|
891
432
|
ListView,
|
|
@@ -904,8 +445,7 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
904
445
|
filters: listFilters,
|
|
905
446
|
onExport: filtered.length > 0 ? exportRows : void 0,
|
|
906
447
|
exportLabel: t2("crud.export.action"),
|
|
907
|
-
|
|
908
|
-
onNew: onNewGated,
|
|
448
|
+
headerActions: newActions,
|
|
909
449
|
onRowClick: (row) => onEdit?.(row.id),
|
|
910
450
|
emptyIcon: FileText,
|
|
911
451
|
emptyMessage: t2("financial.invoice.noInvoices"),
|
|
@@ -931,6 +471,126 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
931
471
|
] })
|
|
932
472
|
] });
|
|
933
473
|
}
|
|
474
|
+
function useReceiptPicker(onPick) {
|
|
475
|
+
const t2 = useTranslation();
|
|
476
|
+
const ref = useRef(null);
|
|
477
|
+
const input = /* @__PURE__ */ jsx(
|
|
478
|
+
"input",
|
|
479
|
+
{
|
|
480
|
+
ref,
|
|
481
|
+
type: "file",
|
|
482
|
+
accept: "image/*",
|
|
483
|
+
capture: "environment",
|
|
484
|
+
className: "hidden",
|
|
485
|
+
onChange: (e) => {
|
|
486
|
+
const file = e.target.files?.[0];
|
|
487
|
+
e.target.value = "";
|
|
488
|
+
if (!file) return;
|
|
489
|
+
if (!file.type.startsWith("image/")) {
|
|
490
|
+
toast.error(t2("financial.quickTx.receiptInvalid"));
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const reader = new FileReader();
|
|
494
|
+
reader.onload = () => {
|
|
495
|
+
if (typeof reader.result === "string") onPick(reader.result);
|
|
496
|
+
};
|
|
497
|
+
reader.onerror = () => toast.error(t2("financial.quickTx.receiptInvalid"));
|
|
498
|
+
reader.readAsDataURL(file);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
);
|
|
502
|
+
return { open: () => ref.current?.click(), input };
|
|
503
|
+
}
|
|
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
|
+
}
|
|
934
594
|
var ACCOUNT_TYPES_FOR_TX = {
|
|
935
595
|
cash: ["cash_register"],
|
|
936
596
|
credit_card: ["credit_card", "bank_account"],
|
|
@@ -964,7 +624,7 @@ var ICON_COLORS = {
|
|
|
964
624
|
bank_transfer: "text-warning",
|
|
965
625
|
check: "text-muted-foreground"
|
|
966
626
|
};
|
|
967
|
-
function PaymentModal({ movement, onClose, onPaid }) {
|
|
627
|
+
function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
|
|
968
628
|
const t2 = useTranslation();
|
|
969
629
|
const { currency } = useFinancialConfig();
|
|
970
630
|
const payMovement = useFinancialStore((s) => s.payMovement);
|
|
@@ -1035,6 +695,8 @@ function PaymentModal({ movement, onClose, onPaid }) {
|
|
|
1035
695
|
setSaving(true);
|
|
1036
696
|
try {
|
|
1037
697
|
await payMovement({
|
|
698
|
+
// O comprovante viaja com o pagamento — é dele que ele é prova.
|
|
699
|
+
metadata: receiptUrl ? { receiptUrl } : void 0,
|
|
1038
700
|
movementId: movement.id,
|
|
1039
701
|
amount,
|
|
1040
702
|
paymentDate,
|
|
@@ -1311,18 +973,28 @@ var formId = 1;
|
|
|
1311
973
|
function fid() {
|
|
1312
974
|
return `f${formId++}`;
|
|
1313
975
|
}
|
|
1314
|
-
function InvoiceFormView({ direction, editId, onSaved }) {
|
|
976
|
+
function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false }) {
|
|
1315
977
|
const t2 = useTranslation();
|
|
1316
978
|
const config = useFinancialConfig();
|
|
1317
979
|
const { currency, itemTypes, locations } = config;
|
|
1318
980
|
const provider = useFinancialProvider();
|
|
1319
981
|
const hasItemLookups = itemTypes.some((tp) => config.entityLookups[tp.value]);
|
|
1320
982
|
const defaultItemKind = itemTypes.find((tp) => tp.value === "other")?.value ?? itemTypes.find((tp) => !config.entityLookups[tp.value])?.value ?? itemTypes[0]?.value ?? "other";
|
|
983
|
+
const bankAccounts = useFinancialStore((s) => s.bankAccounts);
|
|
984
|
+
const paymentMethodTypes = useFinancialStore((s) => s.paymentMethodTypes);
|
|
985
|
+
const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
|
|
986
|
+
const fetchPaymentMethods = useFinancialStore((s) => s.fetchPaymentMethods);
|
|
987
|
+
const payMovement = useFinancialStore((s) => s.payMovement);
|
|
1321
988
|
const createInvoice = useFinancialStore((s) => s.createInvoice);
|
|
1322
989
|
const guardMovements = useLimitGuard("movements_month");
|
|
1323
990
|
const can = usePermissionOptional();
|
|
1324
991
|
const hasLocations = locations.length > 0;
|
|
1325
992
|
const isEdit = !!editId;
|
|
993
|
+
useEffect(() => {
|
|
994
|
+
if (isEdit) return;
|
|
995
|
+
void fetchBankAccounts();
|
|
996
|
+
void fetchPaymentMethods();
|
|
997
|
+
}, [isEdit]);
|
|
1326
998
|
const [loaded, setLoaded] = useState(!editId);
|
|
1327
999
|
const [loading, setLoading] = useState(!!editId);
|
|
1328
1000
|
const [contactName, setContactName] = useState("");
|
|
@@ -1443,6 +1115,10 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1443
1115
|
function removeItem(id) {
|
|
1444
1116
|
setItems(items.filter((it) => it._id !== id));
|
|
1445
1117
|
}
|
|
1118
|
+
useEffect(() => {
|
|
1119
|
+
if (isEdit || totalAmount <= 0 || installments.length > 0) return;
|
|
1120
|
+
generateInstallments(1);
|
|
1121
|
+
}, [isEdit, totalAmount, installments.length]);
|
|
1446
1122
|
function generateInstallments(count) {
|
|
1447
1123
|
if (count < 1 || totalAmount <= 0) return;
|
|
1448
1124
|
const perInstallment = Math.floor(totalAmount * 100 / count) / 100;
|
|
@@ -1466,6 +1142,10 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1466
1142
|
toast.error(t2("common.formIncomplete"));
|
|
1467
1143
|
return;
|
|
1468
1144
|
}
|
|
1145
|
+
if (!isEdit && installments.some((i) => i.receiptUrl && (!i.payMethodTypeId || !i.payAccountId))) {
|
|
1146
|
+
toast.error(t2("financial.invoiceForm.paidNeedsMethod"));
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1469
1149
|
if (!isEdit && await guardMovements(Math.max(1, installments.length)) === "blocked") return;
|
|
1470
1150
|
setSaving(true);
|
|
1471
1151
|
try {
|
|
@@ -1485,6 +1165,11 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1485
1165
|
installmentNumber: 1
|
|
1486
1166
|
});
|
|
1487
1167
|
}
|
|
1168
|
+
installments.forEach((inst, i) => {
|
|
1169
|
+
if (inst.receiptUrl && movInputs[i]) {
|
|
1170
|
+
movInputs[i] = { ...movInputs[i], metadata: { ...movInputs[i].metadata ?? {}, receiptUrl: inst.receiptUrl } };
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1488
1173
|
if (isEdit && editId) {
|
|
1489
1174
|
await provider.updateInvoice(editId, {
|
|
1490
1175
|
invoiceDate,
|
|
@@ -1509,6 +1194,22 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1509
1194
|
installments: movInputs
|
|
1510
1195
|
});
|
|
1511
1196
|
invalidateLimit("movements_month");
|
|
1197
|
+
const paidOnes = installments.map((inst, i) => ({ inst, number: i + 1 })).filter(({ inst }) => inst.receiptUrl);
|
|
1198
|
+
if (paidOnes.length > 0) {
|
|
1199
|
+
const movements = await provider.getMovements({ direction, status: "pending" });
|
|
1200
|
+
const bills = movements.data.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
|
|
1201
|
+
for (const { inst, number } of paidOnes) {
|
|
1202
|
+
const bill = bills.find((m) => (m.installmentNumber ?? 1) === number);
|
|
1203
|
+
if (!bill) continue;
|
|
1204
|
+
await payMovement({
|
|
1205
|
+
movementId: bill.id,
|
|
1206
|
+
amount: bill.amount,
|
|
1207
|
+
paymentDate: inst.dueDate,
|
|
1208
|
+
paymentMethodTypeId: inst.payMethodTypeId || void 0,
|
|
1209
|
+
bankAccountId: inst.payAccountId || void 0
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1512
1213
|
onSaved?.(invoice.id);
|
|
1513
1214
|
}
|
|
1514
1215
|
} finally {
|
|
@@ -1578,26 +1279,64 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1578
1279
|
},
|
|
1579
1280
|
n
|
|
1580
1281
|
)) }),
|
|
1581
|
-
installments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: totalAmount <= 0 ? t2("financial.invoiceForm.addItemsFirst") : t2("financial.invoiceForm.selectInstallments") }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: installments.map((inst, i) =>
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1282
|
+
installments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: totalAmount <= 0 ? t2("financial.invoiceForm.addItemsFirst") : t2("financial.invoiceForm.selectInstallments") }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: installments.map((inst, i) => {
|
|
1283
|
+
const patch = (next) => {
|
|
1284
|
+
const updated = [...installments];
|
|
1285
|
+
updated[i] = { ...inst, ...next };
|
|
1286
|
+
setInstallments(updated);
|
|
1287
|
+
};
|
|
1288
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 rounded border p-2.5", children: [
|
|
1289
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1290
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-muted-foreground", children: [
|
|
1291
|
+
"#",
|
|
1292
|
+
i + 1
|
|
1293
|
+
] }),
|
|
1294
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: money(inst.amount) })
|
|
1586
1295
|
] }),
|
|
1587
|
-
/* @__PURE__ */ jsx(
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
value: inst.dueDate,
|
|
1593
|
-
onChange: (v) => {
|
|
1594
|
-
const updated = [...installments];
|
|
1595
|
-
updated[i] = { ...inst, dueDate: v };
|
|
1596
|
-
setInstallments(updated);
|
|
1296
|
+
/* @__PURE__ */ jsx(
|
|
1297
|
+
DatePicker,
|
|
1298
|
+
{
|
|
1299
|
+
value: inst.dueDate,
|
|
1300
|
+
onChange: (v) => patch({ dueDate: v })
|
|
1597
1301
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1302
|
+
),
|
|
1303
|
+
/* @__PURE__ */ jsx(
|
|
1304
|
+
ReceiptField,
|
|
1305
|
+
{
|
|
1306
|
+
value: inst.receiptUrl,
|
|
1307
|
+
onChange: (url) => patch({ receiptUrl: url }),
|
|
1308
|
+
autoOpen: focusReceipt && i === 0 && !inst.receiptUrl
|
|
1309
|
+
}
|
|
1310
|
+
),
|
|
1311
|
+
inst.receiptUrl && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 border-t pt-1.5", children: [
|
|
1312
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium text-success", children: t2("financial.invoiceForm.willBePaid") }),
|
|
1313
|
+
/* @__PURE__ */ jsxs(
|
|
1314
|
+
"select",
|
|
1315
|
+
{
|
|
1316
|
+
value: inst.payMethodTypeId ?? "",
|
|
1317
|
+
onChange: (e) => patch({ payMethodTypeId: e.target.value }),
|
|
1318
|
+
className: "w-full rounded-input border border-input bg-card px-2 py-1.5 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1319
|
+
children: [
|
|
1320
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectMethod") }),
|
|
1321
|
+
paymentMethodTypes.map((tp) => /* @__PURE__ */ jsx("option", { value: tp.id, children: tp.name }, tp.id))
|
|
1322
|
+
]
|
|
1323
|
+
}
|
|
1324
|
+
),
|
|
1325
|
+
/* @__PURE__ */ jsxs(
|
|
1326
|
+
"select",
|
|
1327
|
+
{
|
|
1328
|
+
value: inst.payAccountId ?? (bankAccounts.length === 1 ? bankAccounts[0].id : ""),
|
|
1329
|
+
onChange: (e) => patch({ payAccountId: e.target.value }),
|
|
1330
|
+
className: "w-full rounded-input border border-input bg-card px-2 py-1.5 text-xs shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1331
|
+
children: [
|
|
1332
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t2("financial.invoiceForm.selectAccount") }),
|
|
1333
|
+
bankAccounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
|
|
1334
|
+
]
|
|
1335
|
+
}
|
|
1336
|
+
)
|
|
1337
|
+
] })
|
|
1338
|
+
] }, inst._id);
|
|
1339
|
+
}) })
|
|
1601
1340
|
] }) });
|
|
1602
1341
|
const scheduleSection = isEdit ? /* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.paymentSchedule"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1603
1342
|
hasPayments && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.invoiceForm.scheduleLocked") }),
|
|
@@ -1667,6 +1406,13 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1667
1406
|
const results = await config.contactLookup.search(q);
|
|
1668
1407
|
return results.map((r) => ({ id: r.id, label: r.label, subtitle: r.subtitle, group: r.group, data: r }));
|
|
1669
1408
|
},
|
|
1409
|
+
createLabel: t2("financial.invoiceForm.createContact"),
|
|
1410
|
+
clearLabel: t2("financial.invoiceForm.clear"),
|
|
1411
|
+
onCreate: config.contactLookup?.create ? async (name) => {
|
|
1412
|
+
const created = await config.contactLookup.create(name);
|
|
1413
|
+
setContactId(created.id);
|
|
1414
|
+
setContactName(created.label);
|
|
1415
|
+
} : void 0,
|
|
1670
1416
|
placeholder: t2("financial.invoiceForm.searchType")
|
|
1671
1417
|
}
|
|
1672
1418
|
) }),
|
|
@@ -1854,6 +1600,13 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
|
|
|
1854
1600
|
const [payments, setPayments] = useState([]);
|
|
1855
1601
|
const [loading, setLoading] = useState(true);
|
|
1856
1602
|
const [payingMovement, setPayingMovement] = useState(null);
|
|
1603
|
+
const [receiptFor, setReceiptFor] = useState(null);
|
|
1604
|
+
const [pendingReceipt, setPendingReceipt] = useState();
|
|
1605
|
+
const receiptPicker = useReceiptPicker((dataUrl) => {
|
|
1606
|
+
setPendingReceipt(dataUrl);
|
|
1607
|
+
setPayingMovement(receiptFor);
|
|
1608
|
+
setReceiptFor(null);
|
|
1609
|
+
});
|
|
1857
1610
|
const [expandedMovId, setExpandedMovId] = useState(null);
|
|
1858
1611
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
1859
1612
|
const [confirmCancel, setConfirmCancel] = useState(false);
|
|
@@ -2053,6 +1806,20 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
|
|
|
2053
1806
|
mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
|
|
2054
1807
|
] })
|
|
2055
1808
|
] }),
|
|
1809
|
+
!isPaid && invoice.status !== "cancelled" && /* @__PURE__ */ jsx(
|
|
1810
|
+
"button",
|
|
1811
|
+
{
|
|
1812
|
+
onClick: (e) => {
|
|
1813
|
+
e.stopPropagation();
|
|
1814
|
+
setReceiptFor(mov);
|
|
1815
|
+
receiptPicker.open();
|
|
1816
|
+
},
|
|
1817
|
+
title: t2("financial.quickTx.receiptCapture"),
|
|
1818
|
+
"aria-label": t2("financial.quickTx.receiptCapture"),
|
|
1819
|
+
className: "inline-flex shrink-0 items-center gap-1 rounded-lg border bg-card px-2.5 py-1.5 text-xs font-medium shadow-button transition-colors hover:bg-muted active:shadow-button-inset",
|
|
1820
|
+
children: /* @__PURE__ */ jsx(Camera, { className: "h-3 w-3" })
|
|
1821
|
+
}
|
|
1822
|
+
),
|
|
2056
1823
|
!isPaid && !isPartial && invoice.status !== "cancelled" && /* @__PURE__ */ jsxs(
|
|
2057
1824
|
"button",
|
|
2058
1825
|
{
|
|
@@ -2153,13 +1920,19 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel })
|
|
|
2153
1920
|
extraSections: sideBlocks
|
|
2154
1921
|
}
|
|
2155
1922
|
),
|
|
1923
|
+
receiptPicker.input,
|
|
2156
1924
|
payingMovement && /* @__PURE__ */ jsx(
|
|
2157
1925
|
PaymentModal,
|
|
2158
1926
|
{
|
|
2159
1927
|
movement: payingMovement,
|
|
2160
|
-
|
|
1928
|
+
receiptUrl: pendingReceipt,
|
|
1929
|
+
onClose: () => {
|
|
1930
|
+
setPayingMovement(null);
|
|
1931
|
+
setPendingReceipt(void 0);
|
|
1932
|
+
},
|
|
2161
1933
|
onPaid: () => {
|
|
2162
1934
|
setPayingMovement(null);
|
|
1935
|
+
setPendingReceipt(void 0);
|
|
2163
1936
|
loadData();
|
|
2164
1937
|
}
|
|
2165
1938
|
}
|
|
@@ -2212,7 +1985,7 @@ function referrerLabel(view, t2) {
|
|
|
2212
1985
|
function PayablesView({ intent, onNavigate, previousView, back }) {
|
|
2213
1986
|
const t2 = useTranslation();
|
|
2214
1987
|
if (intent.mode === "new") {
|
|
2215
|
-
return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", onSaved: (id) => onNavigate(id ? `payables-detail:${id}` : "payables-list") });
|
|
1988
|
+
return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", focusReceipt: intent.receipt, onSaved: (id) => onNavigate(id ? `payables-detail:${id}` : "payables-list") });
|
|
2216
1989
|
}
|
|
2217
1990
|
if (intent.mode === "edit" && intent.editId) {
|
|
2218
1991
|
return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "debit", editId: intent.editId, onSaved: () => onNavigate(`payables-detail:${intent.editId}`) });
|
|
@@ -2235,6 +2008,7 @@ function PayablesView({ intent, onNavigate, previousView, back }) {
|
|
|
2235
2008
|
{
|
|
2236
2009
|
direction: "debit",
|
|
2237
2010
|
onNew: () => onNavigate("payables-new"),
|
|
2011
|
+
onNewReceipt: () => onNavigate("payables-new-receipt"),
|
|
2238
2012
|
onEdit: (id) => onNavigate(`payables-detail:${id}`)
|
|
2239
2013
|
}
|
|
2240
2014
|
);
|
|
@@ -2242,7 +2016,7 @@ function PayablesView({ intent, onNavigate, previousView, back }) {
|
|
|
2242
2016
|
function ReceivablesView({ intent, onNavigate, previousView, back }) {
|
|
2243
2017
|
const t2 = useTranslation();
|
|
2244
2018
|
if (intent.mode === "new") {
|
|
2245
|
-
return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", onSaved: (id) => onNavigate(id ? `receivables-detail:${id}` : "receivables-list") });
|
|
2019
|
+
return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", focusReceipt: intent.receipt, onSaved: (id) => onNavigate(id ? `receivables-detail:${id}` : "receivables-list") });
|
|
2246
2020
|
}
|
|
2247
2021
|
if (intent.mode === "edit" && intent.editId) {
|
|
2248
2022
|
return /* @__PURE__ */ jsx(InvoiceFormView, { direction: "credit", editId: intent.editId, onSaved: () => onNavigate(`receivables-detail:${intent.editId}`) });
|
|
@@ -2265,6 +2039,7 @@ function ReceivablesView({ intent, onNavigate, previousView, back }) {
|
|
|
2265
2039
|
{
|
|
2266
2040
|
direction: "credit",
|
|
2267
2041
|
onNew: () => onNavigate("receivables-new"),
|
|
2042
|
+
onNewReceipt: () => onNavigate("receivables-new-receipt"),
|
|
2268
2043
|
onEdit: (id) => onNavigate(`receivables-detail:${id}`)
|
|
2269
2044
|
}
|
|
2270
2045
|
);
|
|
@@ -3083,7 +2858,7 @@ function StatementsView({ onNavigate }) {
|
|
|
3083
2858
|
);
|
|
3084
2859
|
};
|
|
3085
2860
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3086
|
-
/* @__PURE__ */ jsx(
|
|
2861
|
+
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setShowTransfer(true), disabled: bankAccounts.length < 2, children: [
|
|
3087
2862
|
/* @__PURE__ */ jsx(ArrowRightLeft, { className: "h-3.5 w-3.5" }),
|
|
3088
2863
|
t2("financial.statements.newTransfer")
|
|
3089
2864
|
] }) }),
|
|
@@ -3183,15 +2958,13 @@ function cardMath(acc) {
|
|
|
3183
2958
|
const pct = limit > 0 ? Math.min(100, used / limit * 100) : 0;
|
|
3184
2959
|
return { limit, used, available, pct };
|
|
3185
2960
|
}
|
|
3186
|
-
function CardsView() {
|
|
2961
|
+
function CardsView({ onNavigate }) {
|
|
3187
2962
|
const t2 = useTranslation();
|
|
3188
2963
|
const { currency } = useFinancialConfig();
|
|
3189
2964
|
const bankAccounts = useFinancialStore((s) => s.bankAccounts);
|
|
3190
2965
|
const bankAccountsLoading = useFinancialStore((s) => s.bankAccountsLoading);
|
|
3191
2966
|
const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
|
|
3192
2967
|
const [tab, setTab] = useState("open");
|
|
3193
|
-
const [addOpen, setAddOpen] = useState(false);
|
|
3194
|
-
const [addCardId, setAddCardId] = useState();
|
|
3195
2968
|
useEffect(() => {
|
|
3196
2969
|
fetchBankAccounts();
|
|
3197
2970
|
}, []);
|
|
@@ -3212,9 +2985,8 @@ function CardsView() {
|
|
|
3212
2985
|
}
|
|
3213
2986
|
return { totalAvailable, totalBills };
|
|
3214
2987
|
}, [openCards]);
|
|
3215
|
-
function openAddExpense(
|
|
3216
|
-
|
|
3217
|
-
setAddOpen(true);
|
|
2988
|
+
function openAddExpense(_cardId) {
|
|
2989
|
+
onNavigate?.("payables-new");
|
|
3218
2990
|
}
|
|
3219
2991
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3220
2992
|
/* @__PURE__ */ jsx(SubpageHeader, { title: t2("financial.cards.title"), subtitle: t2("financial.cards.subtitle") }),
|
|
@@ -3301,16 +3073,7 @@ function CardsView() {
|
|
|
3301
3073
|
] })
|
|
3302
3074
|
] }, card.id);
|
|
3303
3075
|
}) })
|
|
3304
|
-
] })
|
|
3305
|
-
/* @__PURE__ */ jsx(
|
|
3306
|
-
QuickTransactionForm,
|
|
3307
|
-
{
|
|
3308
|
-
open: addOpen,
|
|
3309
|
-
onOpenChange: setAddOpen,
|
|
3310
|
-
defaultType: "expense",
|
|
3311
|
-
defaultAccountId: addCardId
|
|
3312
|
-
}
|
|
3313
|
-
)
|
|
3076
|
+
] })
|
|
3314
3077
|
] });
|
|
3315
3078
|
}
|
|
3316
3079
|
function ReconciliationView() {
|
|
@@ -3692,8 +3455,12 @@ function FinancialOnboarding({ onComplete }) {
|
|
|
3692
3455
|
}
|
|
3693
3456
|
function parseIntent(activeView) {
|
|
3694
3457
|
const { base, id } = parseViewId(activeView);
|
|
3695
|
-
const m = base.match(/^(payables|receivables)-(new|list|detail|edit)$/);
|
|
3696
|
-
if (m)
|
|
3458
|
+
const m = base.match(/^(payables|receivables)-(new-receipt|new|list|detail|edit)$/);
|
|
3459
|
+
if (m) {
|
|
3460
|
+
const raw = m[2];
|
|
3461
|
+
const mode = raw === "new-receipt" ? "new" : raw;
|
|
3462
|
+
return { view: m[1], mode, editId: id, receipt: raw === "new-receipt" };
|
|
3463
|
+
}
|
|
3697
3464
|
return { view: activeView };
|
|
3698
3465
|
}
|
|
3699
3466
|
function buildNav(config, activeView, navigate, t2) {
|
|
@@ -3713,7 +3480,7 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
3713
3480
|
icon: "ArrowUpCircle",
|
|
3714
3481
|
active: activeView.startsWith("payables"),
|
|
3715
3482
|
children: [
|
|
3716
|
-
{ id: "payables-new", label: t2("financial.nav.new"), active: activeView
|
|
3483
|
+
{ id: "payables-new", label: t2("financial.nav.new"), active: activeView.startsWith("payables-new"), onClick: () => navigate("payables-new") },
|
|
3717
3484
|
{ id: "payables-list", label: t2("financial.nav.list"), active: activeView === "payables-list", onClick: () => navigate("payables-list") },
|
|
3718
3485
|
{ id: "payables-recurring", label: t2("financial.nav.recurringExpenses"), active: activeView === "payables-recurring", onClick: () => navigate("payables-recurring") }
|
|
3719
3486
|
]
|
|
@@ -3726,7 +3493,7 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
3726
3493
|
icon: "ArrowDownCircle",
|
|
3727
3494
|
active: activeView.startsWith("receivables"),
|
|
3728
3495
|
children: [
|
|
3729
|
-
{ id: "receivables-new", label: t2("financial.nav.new"), active: activeView
|
|
3496
|
+
{ id: "receivables-new", label: t2("financial.nav.new"), active: activeView.startsWith("receivables-new"), onClick: () => navigate("receivables-new") },
|
|
3730
3497
|
{ id: "receivables-list", label: t2("financial.nav.list"), active: activeView === "receivables-list", onClick: () => navigate("receivables-list") }
|
|
3731
3498
|
]
|
|
3732
3499
|
});
|
|
@@ -3793,7 +3560,7 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
3793
3560
|
}
|
|
3794
3561
|
return items;
|
|
3795
3562
|
}
|
|
3796
|
-
function FinancialPage({ config, provider, store:
|
|
3563
|
+
function FinancialPage({ config, provider, store: store6, registries }) {
|
|
3797
3564
|
const { view, direction, navigate, previousView, back } = useModuleNavigation("/financial", {
|
|
3798
3565
|
summary: 0,
|
|
3799
3566
|
"payables-list": 0,
|
|
@@ -3832,26 +3599,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
|
|
|
3832
3599
|
const intent = parseIntent(view);
|
|
3833
3600
|
const isSettings = view === "settings";
|
|
3834
3601
|
const nav = useModuleNavAccess(buildNav(config, view, navigate, t2));
|
|
3835
|
-
const [quickAddOpen, setQuickAddOpen] = useState(false);
|
|
3836
|
-
const [quickAddType, setQuickAddType] = useState("expense");
|
|
3837
|
-
const [quickAddReceipt, setQuickAddReceipt] = useState(false);
|
|
3838
|
-
const openQuickAdd2 = useCallback((type = "expense") => {
|
|
3839
|
-
setQuickAddType(type);
|
|
3840
|
-
setQuickAddReceipt(false);
|
|
3841
|
-
setQuickAddOpen(true);
|
|
3842
|
-
}, []);
|
|
3843
|
-
const pendingQuickAdd = usePendingQuickAdd();
|
|
3844
|
-
useEffect(() => {
|
|
3845
|
-
if (!pendingQuickAdd) return;
|
|
3846
|
-
const req = consumeQuickAdd();
|
|
3847
|
-
if (!req) return;
|
|
3848
|
-
if (!canCreate) return;
|
|
3849
|
-
setQuickAddType(req.type);
|
|
3850
|
-
setQuickAddReceipt(req.receipt);
|
|
3851
|
-
setQuickAddOpen(true);
|
|
3852
|
-
}, [pendingQuickAdd]);
|
|
3853
3602
|
const quickActions = useMemo(() => {
|
|
3854
|
-
if (config.quickAdd) return [];
|
|
3855
3603
|
if (!canCreate) return [];
|
|
3856
3604
|
const actions = [];
|
|
3857
3605
|
if (config.modules.payables) {
|
|
@@ -3882,7 +3630,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
|
|
|
3882
3630
|
});
|
|
3883
3631
|
}
|
|
3884
3632
|
return actions;
|
|
3885
|
-
}, [config.modules,
|
|
3633
|
+
}, [config.modules, canCreate, navigate, t2]);
|
|
3886
3634
|
function handleOnboardingComplete() {
|
|
3887
3635
|
setOnboardingComplete(true);
|
|
3888
3636
|
try {
|
|
@@ -3891,10 +3639,10 @@ function FinancialPage({ config, provider, store: store2, registries }) {
|
|
|
3891
3639
|
}
|
|
3892
3640
|
}
|
|
3893
3641
|
if (!onboardingComplete) {
|
|
3894
|
-
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store:
|
|
3642
|
+
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete: handleOnboardingComplete }) });
|
|
3895
3643
|
}
|
|
3896
3644
|
if (isSettings && registries && registries.length > 0) {
|
|
3897
|
-
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store:
|
|
3645
|
+
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(PageTransition, { transitionKey: "settings", direction, children: /* @__PURE__ */ jsx("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx(
|
|
3898
3646
|
PluginSettingsPanel,
|
|
3899
3647
|
{
|
|
3900
3648
|
title: t2("financial.settingsPage.title"),
|
|
@@ -3923,7 +3671,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
|
|
|
3923
3671
|
case "commissions":
|
|
3924
3672
|
return /* @__PURE__ */ jsx(CommissionsView, {});
|
|
3925
3673
|
case "cards":
|
|
3926
|
-
return /* @__PURE__ */ jsx(CardsView, {});
|
|
3674
|
+
return /* @__PURE__ */ jsx(CardsView, { onNavigate: navigate });
|
|
3927
3675
|
case "notas-fiscais":
|
|
3928
3676
|
return /* @__PURE__ */ jsx(NotasFiscaisView, {});
|
|
3929
3677
|
default:
|
|
@@ -3935,7 +3683,7 @@ function FinancialPage({ config, provider, store: store2, registries }) {
|
|
|
3935
3683
|
if (!feature) return renderView();
|
|
3936
3684
|
return /* @__PURE__ */ jsx(EntitlementGate, { feature, fallback: /* @__PURE__ */ jsx(UpgradePrompt, { feature }), children: renderView() });
|
|
3937
3685
|
}
|
|
3938
|
-
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store:
|
|
3686
|
+
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(
|
|
3939
3687
|
ModulePage,
|
|
3940
3688
|
{
|
|
3941
3689
|
title: config.labels.pageTitle,
|
|
@@ -3945,45 +3693,15 @@ function FinancialPage({ config, provider, store: store2, registries }) {
|
|
|
3945
3693
|
showHeader: intent.view === "summary" || view === "summary",
|
|
3946
3694
|
viewKey: view,
|
|
3947
3695
|
direction,
|
|
3948
|
-
headerAction: /* @__PURE__ */
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
size: "sm",
|
|
3958
|
-
className: "hidden md:inline-flex",
|
|
3959
|
-
onClick: () => openQuickAdd2("expense"),
|
|
3960
|
-
children: [
|
|
3961
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
3962
|
-
t2("financial.quickTx.newTransaction")
|
|
3963
|
-
]
|
|
3964
|
-
}
|
|
3965
|
-
) }),
|
|
3966
|
-
/* @__PURE__ */ jsx(
|
|
3967
|
-
ModuleActionBar,
|
|
3968
|
-
{
|
|
3969
|
-
quickActions,
|
|
3970
|
-
settingsPath: registries && registries.length > 0 ? "/settings/financial" : void 0,
|
|
3971
|
-
settingsLabel: t2("financial.settingsPage.title")
|
|
3972
|
-
}
|
|
3973
|
-
)
|
|
3974
|
-
] }),
|
|
3975
|
-
children: [
|
|
3976
|
-
renderGatedView(),
|
|
3977
|
-
/* @__PURE__ */ jsx(
|
|
3978
|
-
QuickTransactionForm,
|
|
3979
|
-
{
|
|
3980
|
-
open: quickAddOpen,
|
|
3981
|
-
onOpenChange: setQuickAddOpen,
|
|
3982
|
-
defaultType: quickAddType,
|
|
3983
|
-
focusReceipt: quickAddReceipt
|
|
3984
|
-
}
|
|
3985
|
-
)
|
|
3986
|
-
]
|
|
3696
|
+
headerAction: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
|
|
3697
|
+
ModuleActionBar,
|
|
3698
|
+
{
|
|
3699
|
+
quickActions,
|
|
3700
|
+
settingsPath: registries && registries.length > 0 ? "/settings/financial" : void 0,
|
|
3701
|
+
settingsLabel: t2("financial.settingsPage.title")
|
|
3702
|
+
}
|
|
3703
|
+
) }),
|
|
3704
|
+
children: renderGatedView()
|
|
3987
3705
|
}
|
|
3988
3706
|
) });
|
|
3989
3707
|
}
|
|
@@ -4149,7 +3867,7 @@ function BalanceHero() {
|
|
|
4149
3867
|
const summary = useFinancialStore((s) => s.summary);
|
|
4150
3868
|
const navigate = useDashboardNavigate();
|
|
4151
3869
|
useEnsureSummary();
|
|
4152
|
-
const [hidden, setHidden] =
|
|
3870
|
+
const [hidden, setHidden] = React17.useState(false);
|
|
4153
3871
|
const money = (v) => hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022" : formatCurrency(v ?? 0, currency);
|
|
4154
3872
|
const inflow = summary?.monthlyInflow ?? 0;
|
|
4155
3873
|
const outflow = summary?.monthlyOutflow ?? 0;
|
|
@@ -4222,7 +3940,7 @@ function categorizeSpend(notes) {
|
|
|
4222
3940
|
function SpendByCategoryChart() {
|
|
4223
3941
|
const t2 = useTranslation();
|
|
4224
3942
|
const provider = useFinancialProvider();
|
|
4225
|
-
const [rows, setRows] =
|
|
3943
|
+
const [rows, setRows] = React17.useState([]);
|
|
4226
3944
|
useEffect(() => {
|
|
4227
3945
|
let alive = true;
|
|
4228
3946
|
void (async () => {
|
|
@@ -4265,14 +3983,14 @@ function UpcomingBills() {
|
|
|
4265
3983
|
const { currency } = useFinancialConfig();
|
|
4266
3984
|
const provider = useFinancialProvider();
|
|
4267
3985
|
const dueLabel = useDueLabel();
|
|
4268
|
-
const [bills, setBills] =
|
|
3986
|
+
const [bills, setBills] = React17.useState([]);
|
|
4269
3987
|
useEffect(() => {
|
|
4270
3988
|
let alive = true;
|
|
4271
3989
|
void (async () => {
|
|
4272
|
-
const
|
|
3990
|
+
const pending = await provider.getMovements({ status: "pending" });
|
|
4273
3991
|
if (!alive) return;
|
|
4274
3992
|
setBills(
|
|
4275
|
-
|
|
3993
|
+
pending.data.filter((m) => m.direction === "debit" && m.movementKind === "bill").sort((a, b) => a.dueDate.localeCompare(b.dueDate))
|
|
4276
3994
|
);
|
|
4277
3995
|
})();
|
|
4278
3996
|
return () => {
|
|
@@ -4445,7 +4163,7 @@ function createStore(options) {
|
|
|
4445
4163
|
};
|
|
4446
4164
|
}
|
|
4447
4165
|
function createMockFinancialProvider(options) {
|
|
4448
|
-
const
|
|
4166
|
+
const store6 = createStore(options);
|
|
4449
4167
|
const tenantId = "mock-tenant";
|
|
4450
4168
|
function recalcInvoiceStatus(invoice) {
|
|
4451
4169
|
if (invoice.paidAmount >= invoice.totalAmount) {
|
|
@@ -4453,7 +4171,7 @@ function createMockFinancialProvider(options) {
|
|
|
4453
4171
|
} else if (invoice.paidAmount > 0) {
|
|
4454
4172
|
invoice.status = "partial";
|
|
4455
4173
|
} else {
|
|
4456
|
-
const movements =
|
|
4174
|
+
const movements = store6.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
|
|
4457
4175
|
const hasOverdue = movements.some((m) => m.status === "overdue" || m.status === "pending" && m.dueDate < today());
|
|
4458
4176
|
invoice.status = hasOverdue ? "overdue" : "open";
|
|
4459
4177
|
}
|
|
@@ -4462,16 +4180,16 @@ function createMockFinancialProvider(options) {
|
|
|
4462
4180
|
const provider = {
|
|
4463
4181
|
// --- Invoices ---
|
|
4464
4182
|
async getInvoices(query) {
|
|
4465
|
-
let results = [...
|
|
4183
|
+
let results = [...store6.invoices];
|
|
4466
4184
|
if (query.direction) results = results.filter((i) => i.direction === query.direction);
|
|
4467
4185
|
if (query.status) results = results.filter((i) => matchesStatus(i.status, query.status));
|
|
4468
4186
|
if (query.contactId) results = results.filter((i) => i.contactId === query.contactId);
|
|
4469
4187
|
if (query.accountId) {
|
|
4470
|
-
const invoiceIds = new Set(
|
|
4188
|
+
const invoiceIds = new Set(store6.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
|
|
4471
4189
|
results = results.filter((invoice) => invoiceIds.has(invoice.id));
|
|
4472
4190
|
}
|
|
4473
4191
|
if (query.costCenterId) {
|
|
4474
|
-
const invoiceIds = new Set(
|
|
4192
|
+
const invoiceIds = new Set(store6.invoiceItems.filter((item) => item.costCenterId === query.costCenterId).map((item) => item.invoiceId));
|
|
4475
4193
|
results = results.filter((invoice) => invoiceIds.has(invoice.id));
|
|
4476
4194
|
}
|
|
4477
4195
|
if (query.dateRange) results = results.filter((i) => matchesDateRange(i.invoiceDate, query.dateRange));
|
|
@@ -4485,10 +4203,10 @@ function createMockFinancialProvider(options) {
|
|
|
4485
4203
|
return paginate(results, query.page, query.pageSize);
|
|
4486
4204
|
},
|
|
4487
4205
|
async getInvoiceById(id) {
|
|
4488
|
-
return
|
|
4206
|
+
return store6.invoices.find((i) => i.id === id) ?? null;
|
|
4489
4207
|
},
|
|
4490
4208
|
async getInvoiceItems(invoiceId) {
|
|
4491
|
-
return
|
|
4209
|
+
return store6.invoiceItems.filter((i) => i.invoiceId === invoiceId);
|
|
4492
4210
|
},
|
|
4493
4211
|
async createInvoice(input) {
|
|
4494
4212
|
const invoiceId = uid();
|
|
@@ -4517,7 +4235,7 @@ function createMockFinancialProvider(options) {
|
|
|
4517
4235
|
createdAt: now(),
|
|
4518
4236
|
updatedAt: now()
|
|
4519
4237
|
};
|
|
4520
|
-
|
|
4238
|
+
store6.invoices.push(invoice);
|
|
4521
4239
|
for (const itemInput of input.items) {
|
|
4522
4240
|
const itemTotal = itemInput.quantity * itemInput.unitPrice - (itemInput.discount ?? 0) + (itemInput.surcharge ?? 0);
|
|
4523
4241
|
const item = {
|
|
@@ -4536,7 +4254,7 @@ function createMockFinancialProvider(options) {
|
|
|
4536
4254
|
metadata: itemInput.metadata,
|
|
4537
4255
|
createdAt: now()
|
|
4538
4256
|
};
|
|
4539
|
-
|
|
4257
|
+
store6.invoiceItems.push(item);
|
|
4540
4258
|
}
|
|
4541
4259
|
for (const movInput of input.installments) {
|
|
4542
4260
|
const movement = {
|
|
@@ -4557,29 +4275,29 @@ function createMockFinancialProvider(options) {
|
|
|
4557
4275
|
createdAt: now(),
|
|
4558
4276
|
updatedAt: now()
|
|
4559
4277
|
};
|
|
4560
|
-
|
|
4278
|
+
store6.movements.push(movement);
|
|
4561
4279
|
}
|
|
4562
4280
|
return invoice;
|
|
4563
4281
|
},
|
|
4564
4282
|
async updateInvoice(id, data) {
|
|
4565
|
-
const invoice =
|
|
4283
|
+
const invoice = store6.invoices.find((i) => i.id === id);
|
|
4566
4284
|
if (!invoice) throw new Error(`Invoice ${id} not found`);
|
|
4567
4285
|
Object.assign(invoice, data, { updatedAt: now() });
|
|
4568
4286
|
return invoice;
|
|
4569
4287
|
},
|
|
4570
4288
|
async cancelInvoice(id) {
|
|
4571
|
-
const invoice =
|
|
4289
|
+
const invoice = store6.invoices.find((i) => i.id === id);
|
|
4572
4290
|
if (!invoice) throw new Error(`Invoice ${id} not found`);
|
|
4573
4291
|
invoice.status = "cancelled";
|
|
4574
4292
|
invoice.updatedAt = now();
|
|
4575
|
-
|
|
4293
|
+
store6.movements.filter((m) => m.invoiceId === id && m.status === "pending").forEach((m) => {
|
|
4576
4294
|
m.status = "cancelled";
|
|
4577
4295
|
m.updatedAt = now();
|
|
4578
4296
|
});
|
|
4579
4297
|
},
|
|
4580
4298
|
// --- Movements ---
|
|
4581
4299
|
async getMovements(query) {
|
|
4582
|
-
let results = [...
|
|
4300
|
+
let results = [...store6.movements];
|
|
4583
4301
|
if (query.direction) results = results.filter((m) => m.direction === query.direction);
|
|
4584
4302
|
if (query.status) results = results.filter((m) => matchesStatus(m.status, query.status));
|
|
4585
4303
|
if (query.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
|
|
@@ -4589,9 +4307,9 @@ function createMockFinancialProvider(options) {
|
|
|
4589
4307
|
return paginate(results, query.page, query.pageSize);
|
|
4590
4308
|
},
|
|
4591
4309
|
async payMovement(input) {
|
|
4592
|
-
const bill =
|
|
4310
|
+
const bill = store6.movements.find((m) => m.id === input.movementId);
|
|
4593
4311
|
if (!bill) throw new Error(`Movement ${input.movementId} not found`);
|
|
4594
|
-
const method = input.paymentMethodId ?
|
|
4312
|
+
const method = input.paymentMethodId ? store6.paymentMethods.find((pm) => pm.id === input.paymentMethodId) : void 0;
|
|
4595
4313
|
const feePct = bill.direction === "credit" ? method?.discountValue ?? 0 : 0;
|
|
4596
4314
|
const thisFee = round2(input.amount * (feePct / 100));
|
|
4597
4315
|
const payment = {
|
|
@@ -4613,18 +4331,19 @@ function createMockFinancialProvider(options) {
|
|
|
4613
4331
|
cardBrand: input.cardBrand,
|
|
4614
4332
|
cardInstallments: input.cardInstallments,
|
|
4615
4333
|
notes: input.notes,
|
|
4334
|
+
metadata: input.metadata,
|
|
4616
4335
|
tenantId,
|
|
4617
4336
|
createdAt: now(),
|
|
4618
4337
|
updatedAt: now()
|
|
4619
4338
|
};
|
|
4620
|
-
|
|
4339
|
+
store6.movements.push(payment);
|
|
4621
4340
|
bill.paidAmount = round2(bill.paidAmount + input.amount);
|
|
4622
4341
|
bill.status = bill.paidAmount >= bill.amount ? "paid" : "partial";
|
|
4623
4342
|
bill.paymentDate = void 0;
|
|
4624
4343
|
bill.bankAccountId = void 0;
|
|
4625
4344
|
bill.updatedAt = now();
|
|
4626
4345
|
if (input.bankAccountId) {
|
|
4627
|
-
const account =
|
|
4346
|
+
const account = store6.bankAccounts.find((a) => a.id === input.bankAccountId);
|
|
4628
4347
|
if (account) {
|
|
4629
4348
|
if (bill.direction === "credit") {
|
|
4630
4349
|
account.currentBalance = round2(account.currentBalance + (input.amount - thisFee));
|
|
@@ -4635,9 +4354,9 @@ function createMockFinancialProvider(options) {
|
|
|
4635
4354
|
}
|
|
4636
4355
|
}
|
|
4637
4356
|
if (bill.invoiceId) {
|
|
4638
|
-
const invoice =
|
|
4357
|
+
const invoice = store6.invoices.find((i) => i.id === bill.invoiceId);
|
|
4639
4358
|
if (invoice) {
|
|
4640
|
-
const bills =
|
|
4359
|
+
const bills = store6.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
|
|
4641
4360
|
invoice.paidAmount = bills.reduce((sum, m) => sum + m.paidAmount, 0);
|
|
4642
4361
|
recalcInvoiceStatus(invoice);
|
|
4643
4362
|
}
|
|
@@ -4645,7 +4364,7 @@ function createMockFinancialProvider(options) {
|
|
|
4645
4364
|
return payment;
|
|
4646
4365
|
},
|
|
4647
4366
|
async cancelMovement(id) {
|
|
4648
|
-
const movement =
|
|
4367
|
+
const movement = store6.movements.find((m) => m.id === id);
|
|
4649
4368
|
if (!movement) throw new Error(`Movement ${id} not found`);
|
|
4650
4369
|
movement.status = "cancelled";
|
|
4651
4370
|
movement.updatedAt = now();
|
|
@@ -4672,22 +4391,22 @@ function createMockFinancialProvider(options) {
|
|
|
4672
4391
|
createdAt: now(),
|
|
4673
4392
|
updatedAt: now()
|
|
4674
4393
|
};
|
|
4675
|
-
|
|
4394
|
+
store6.movements.push({
|
|
4676
4395
|
...base,
|
|
4677
4396
|
id: debitMovementId,
|
|
4678
4397
|
direction: "debit",
|
|
4679
4398
|
bankAccountId: input.fromAccountId,
|
|
4680
4399
|
metadata: { transferId, transferRole: "out", counterAccountId: input.toAccountId }
|
|
4681
4400
|
});
|
|
4682
|
-
|
|
4401
|
+
store6.movements.push({
|
|
4683
4402
|
...base,
|
|
4684
4403
|
id: creditMovementId,
|
|
4685
4404
|
direction: "credit",
|
|
4686
4405
|
bankAccountId: input.toAccountId,
|
|
4687
4406
|
metadata: { transferId, transferRole: "in", counterAccountId: input.fromAccountId }
|
|
4688
4407
|
});
|
|
4689
|
-
const from =
|
|
4690
|
-
const to =
|
|
4408
|
+
const from = store6.bankAccounts.find((a) => a.id === input.fromAccountId);
|
|
4409
|
+
const to = store6.bankAccounts.find((a) => a.id === input.toAccountId);
|
|
4691
4410
|
if (from) {
|
|
4692
4411
|
from.currentBalance = round2(from.currentBalance - input.amount);
|
|
4693
4412
|
from.updatedAt = now();
|
|
@@ -4700,7 +4419,7 @@ function createMockFinancialProvider(options) {
|
|
|
4700
4419
|
},
|
|
4701
4420
|
// --- Bank Accounts ---
|
|
4702
4421
|
async getBankAccounts() {
|
|
4703
|
-
return
|
|
4422
|
+
return store6.bankAccounts.filter((a) => a.isActive);
|
|
4704
4423
|
},
|
|
4705
4424
|
async createBankAccount(data) {
|
|
4706
4425
|
const account = {
|
|
@@ -4721,26 +4440,26 @@ function createMockFinancialProvider(options) {
|
|
|
4721
4440
|
createdAt: now(),
|
|
4722
4441
|
updatedAt: now()
|
|
4723
4442
|
};
|
|
4724
|
-
|
|
4443
|
+
store6.bankAccounts.push(account);
|
|
4725
4444
|
return account;
|
|
4726
4445
|
},
|
|
4727
4446
|
async updateBankAccount(id, data) {
|
|
4728
|
-
const account =
|
|
4447
|
+
const account = store6.bankAccounts.find((a) => a.id === id);
|
|
4729
4448
|
if (!account) throw new Error(`Bank account ${id} not found`);
|
|
4730
4449
|
Object.assign(account, data, { updatedAt: now() });
|
|
4731
4450
|
return account;
|
|
4732
4451
|
},
|
|
4733
4452
|
// --- Cash Sessions ---
|
|
4734
4453
|
async getCashSessions(bankAccountId) {
|
|
4735
|
-
let results = [...
|
|
4454
|
+
let results = [...store6.cashSessions];
|
|
4736
4455
|
if (bankAccountId) results = results.filter((s) => s.bankAccountId === bankAccountId);
|
|
4737
4456
|
return results.sort((a, b) => b.openedAt.localeCompare(a.openedAt));
|
|
4738
4457
|
},
|
|
4739
4458
|
async getOpenSession(bankAccountId) {
|
|
4740
|
-
return
|
|
4459
|
+
return store6.cashSessions.find((s) => s.bankAccountId === bankAccountId && s.status === "open") ?? null;
|
|
4741
4460
|
},
|
|
4742
4461
|
async openCashSession(input) {
|
|
4743
|
-
const account =
|
|
4462
|
+
const account = store6.bankAccounts.find((a) => a.id === input.bankAccountId);
|
|
4744
4463
|
const session = {
|
|
4745
4464
|
id: uid(),
|
|
4746
4465
|
bankAccountId: input.bankAccountId,
|
|
@@ -4754,13 +4473,13 @@ function createMockFinancialProvider(options) {
|
|
|
4754
4473
|
tenantId,
|
|
4755
4474
|
createdAt: now()
|
|
4756
4475
|
};
|
|
4757
|
-
|
|
4476
|
+
store6.cashSessions.push(session);
|
|
4758
4477
|
return session;
|
|
4759
4478
|
},
|
|
4760
4479
|
async closeCashSession(input) {
|
|
4761
|
-
const session =
|
|
4480
|
+
const session = store6.cashSessions.find((s) => s.id === input.sessionId);
|
|
4762
4481
|
if (!session) throw new Error(`Cash session ${input.sessionId} not found`);
|
|
4763
|
-
const sessionMovements =
|
|
4482
|
+
const sessionMovements = store6.movements.filter((m) => m.cashSessionId === session.id && m.status === "paid");
|
|
4764
4483
|
const inflow = sessionMovements.filter((m) => m.direction === "credit").reduce((s, m) => s + m.paidAmount, 0);
|
|
4765
4484
|
const outflow = sessionMovements.filter((m) => m.direction === "debit").reduce((s, m) => s + m.paidAmount, 0);
|
|
4766
4485
|
const expectedBalance = session.openingBalance + inflow - outflow;
|
|
@@ -4775,9 +4494,9 @@ function createMockFinancialProvider(options) {
|
|
|
4775
4494
|
return session;
|
|
4776
4495
|
},
|
|
4777
4496
|
async getCashSessionSummary(sessionId) {
|
|
4778
|
-
const session =
|
|
4497
|
+
const session = store6.cashSessions.find((s) => s.id === sessionId);
|
|
4779
4498
|
if (!session) throw new Error(`Cash session ${sessionId} not found`);
|
|
4780
|
-
const sessionMovements =
|
|
4499
|
+
const sessionMovements = store6.movements.filter((m) => m.cashSessionId === sessionId && m.status === "paid");
|
|
4781
4500
|
return {
|
|
4782
4501
|
session,
|
|
4783
4502
|
movementCount: sessionMovements.length,
|
|
@@ -4787,21 +4506,21 @@ function createMockFinancialProvider(options) {
|
|
|
4787
4506
|
},
|
|
4788
4507
|
// --- Payment Methods ---
|
|
4789
4508
|
async getPaymentMethods() {
|
|
4790
|
-
return
|
|
4509
|
+
return store6.paymentMethods.filter((pm) => pm.isActive);
|
|
4791
4510
|
},
|
|
4792
4511
|
async getPaymentMethodTypes() {
|
|
4793
|
-
return
|
|
4512
|
+
return store6.paymentMethodTypes.filter((t2) => t2.isActive);
|
|
4794
4513
|
},
|
|
4795
4514
|
// --- Chart of Accounts ---
|
|
4796
4515
|
async getChartOfAccounts() {
|
|
4797
|
-
return
|
|
4516
|
+
return store6.chartOfAccounts.filter((n) => n.isActive);
|
|
4798
4517
|
},
|
|
4799
4518
|
async getCostCenters() {
|
|
4800
|
-
return
|
|
4519
|
+
return store6.costCenters.filter((c) => c.isActive);
|
|
4801
4520
|
},
|
|
4802
4521
|
// --- Card Transactions ---
|
|
4803
4522
|
async getCardTransactions(dateRange) {
|
|
4804
|
-
let results = [...
|
|
4523
|
+
let results = [...store6.cardTransactions];
|
|
4805
4524
|
if (dateRange) results = results.filter((t2) => matchesDateRange(t2.expectedDate, dateRange));
|
|
4806
4525
|
return results.sort((a, b) => a.expectedDate.localeCompare(b.expectedDate));
|
|
4807
4526
|
},
|
|
@@ -4810,15 +4529,15 @@ function createMockFinancialProvider(options) {
|
|
|
4810
4529
|
const todayStr = today();
|
|
4811
4530
|
const isRealized = (m) => m.status !== "cancelled" && !!m.paymentDate;
|
|
4812
4531
|
const consolidated = !query.bankAccountId;
|
|
4813
|
-
const scopeAccounts = consolidated ?
|
|
4532
|
+
const scopeAccounts = consolidated ? store6.bankAccounts.filter((a) => a.isActive) : store6.bankAccounts.filter((a) => a.id === query.bankAccountId);
|
|
4814
4533
|
const accountIds = new Set(scopeAccounts.map((a) => a.id));
|
|
4815
|
-
const accountNameById = new Map(
|
|
4534
|
+
const accountNameById = new Map(store6.bankAccounts.map((a) => [a.id, a.name]));
|
|
4816
4535
|
const initialSum = scopeAccounts.reduce((s, a) => s + a.initialBalance, 0);
|
|
4817
4536
|
if (!consolidated && accountIds.size === 0) {
|
|
4818
4537
|
return { entries: [], openingBalance: 0, closingBalance: 0, totalCredits: 0, totalDebits: 0, totalFees: 0, net: 0, accountId: query.bankAccountId };
|
|
4819
4538
|
}
|
|
4820
4539
|
const netOf = (m) => m.direction === "credit" ? m.paidAmount - (m.feeAmount ?? 0) : -m.paidAmount;
|
|
4821
|
-
const inScope =
|
|
4540
|
+
const inScope = store6.movements.filter((m) => isRealized(m) && (consolidated || m.bankAccountId && accountIds.has(m.bankAccountId)));
|
|
4822
4541
|
const openingBalance = round2(initialSum + inScope.filter((m) => (m.paymentDate ?? "") < query.dateRange.from).reduce((s, m) => s + netOf(m), 0));
|
|
4823
4542
|
const rangeMovs = inScope.filter((m) => matchesDateRange(m.paymentDate, query.dateRange)).sort((a, b) => (a.paymentDate ?? "").localeCompare(b.paymentDate ?? "") || a.createdAt.localeCompare(b.createdAt));
|
|
4824
4543
|
let balance = openingBalance;
|
|
@@ -4840,7 +4559,7 @@ function createMockFinancialProvider(options) {
|
|
|
4840
4559
|
}
|
|
4841
4560
|
return {
|
|
4842
4561
|
movement,
|
|
4843
|
-
invoice: movement.invoiceId ?
|
|
4562
|
+
invoice: movement.invoiceId ? store6.invoices.find((i) => i.id === movement.invoiceId) : void 0,
|
|
4844
4563
|
runningBalance: balance,
|
|
4845
4564
|
gross,
|
|
4846
4565
|
fee,
|
|
@@ -4852,9 +4571,9 @@ function createMockFinancialProvider(options) {
|
|
|
4852
4571
|
});
|
|
4853
4572
|
for (const e of entries) {
|
|
4854
4573
|
const mid = e.movement.matchedMovementId;
|
|
4855
|
-
const mm = mid ?
|
|
4574
|
+
const mm = mid ? store6.movements.find((m) => m.id === mid) : void 0;
|
|
4856
4575
|
if (!mm) continue;
|
|
4857
|
-
const inv = mm.invoiceId ?
|
|
4576
|
+
const inv = mm.invoiceId ? store6.invoices.find((i) => i.id === mm.invoiceId) : void 0;
|
|
4858
4577
|
e.reconciledWith = {
|
|
4859
4578
|
movementId: mm.id,
|
|
4860
4579
|
description: mm.notes ?? void 0,
|
|
@@ -4878,11 +4597,11 @@ function createMockFinancialProvider(options) {
|
|
|
4878
4597
|
},
|
|
4879
4598
|
// --- Summary ---
|
|
4880
4599
|
async getSummary(dateRange) {
|
|
4881
|
-
const totalBalance =
|
|
4882
|
-
const pendingReceivable =
|
|
4600
|
+
const totalBalance = store6.bankAccounts.filter((a) => a.isActive).reduce((sum, a) => sum + a.currentBalance, 0);
|
|
4601
|
+
const pendingReceivable = store6.movements.filter(
|
|
4883
4602
|
(m) => m.direction === "credit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
|
|
4884
4603
|
);
|
|
4885
|
-
const pendingPayable =
|
|
4604
|
+
const pendingPayable = store6.movements.filter(
|
|
4886
4605
|
(m) => m.direction === "debit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
|
|
4887
4606
|
);
|
|
4888
4607
|
const todayStr = today();
|
|
@@ -4890,7 +4609,7 @@ function createMockFinancialProvider(options) {
|
|
|
4890
4609
|
monthAgo.setDate(monthAgo.getDate() - 30);
|
|
4891
4610
|
const monthStart = monthAgo.toISOString().slice(0, 10);
|
|
4892
4611
|
const monthEnd = todayStr;
|
|
4893
|
-
const monthMovements =
|
|
4612
|
+
const monthMovements = store6.movements.filter(
|
|
4894
4613
|
(m) => m.movementKind === "payment" && matchesDateRange(m.paymentDate, dateRange ?? { from: monthStart, to: monthEnd })
|
|
4895
4614
|
);
|
|
4896
4615
|
const overdueReceivable = pendingReceivable.filter((m) => m.dueDate < todayStr);
|
|
@@ -4911,12 +4630,12 @@ function createMockFinancialProvider(options) {
|
|
|
4911
4630
|
async importBankTransactions(input) {
|
|
4912
4631
|
let imported = 0, duplicates = 0;
|
|
4913
4632
|
for (const line of input.lines) {
|
|
4914
|
-
const dup =
|
|
4633
|
+
const dup = store6.movements.some((m) => m.externalSource === line.externalSource && m.externalId === line.externalId);
|
|
4915
4634
|
if (dup) {
|
|
4916
4635
|
duplicates++;
|
|
4917
4636
|
continue;
|
|
4918
4637
|
}
|
|
4919
|
-
|
|
4638
|
+
store6.movements.push({
|
|
4920
4639
|
id: uid(),
|
|
4921
4640
|
direction: line.direction,
|
|
4922
4641
|
movementKind: "payment",
|
|
@@ -4938,38 +4657,38 @@ function createMockFinancialProvider(options) {
|
|
|
4938
4657
|
return { imported, duplicates };
|
|
4939
4658
|
},
|
|
4940
4659
|
async getUnreconciled(query) {
|
|
4941
|
-
let results =
|
|
4660
|
+
let results = store6.movements.filter((m) => m.externalSource && !m.reconciledAt);
|
|
4942
4661
|
if (query?.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
|
|
4943
4662
|
if (query?.dateRange) results = results.filter((m) => matchesDateRange(m.paymentDate, query.dateRange));
|
|
4944
4663
|
return results.sort((a, b) => (b.paymentDate ?? "").localeCompare(a.paymentDate ?? ""));
|
|
4945
4664
|
},
|
|
4946
4665
|
async suggestReconciliation(bankMovementId) {
|
|
4947
|
-
const line =
|
|
4666
|
+
const line = store6.movements.find((m) => m.id === bankMovementId);
|
|
4948
4667
|
if (!line) return [];
|
|
4949
4668
|
const amount = line.amount;
|
|
4950
4669
|
const tol = Math.max(0.01, round2(amount * 0.02));
|
|
4951
4670
|
const lineDate = line.paymentDate ?? line.dueDate;
|
|
4952
|
-
return
|
|
4671
|
+
return store6.movements.filter((m) => !m.externalSource && !m.reconciledAt && m.direction === line.direction && m.id !== bankMovementId && Math.abs(m.amount - amount) <= tol).map((m) => {
|
|
4953
4672
|
const amtScore = amount > 0 ? Math.max(0, 1 - Math.abs(m.amount - amount) / amount) : 0;
|
|
4954
4673
|
const refDate = m.dueDate ?? m.paymentDate;
|
|
4955
4674
|
const days = lineDate && refDate ? Math.abs((new Date(lineDate).getTime() - new Date(refDate).getTime()) / 864e5) : 30;
|
|
4956
4675
|
const dateScore = Math.max(0, 1 - days / 30);
|
|
4957
4676
|
return {
|
|
4958
4677
|
movement: m,
|
|
4959
|
-
invoice: m.invoiceId ?
|
|
4678
|
+
invoice: m.invoiceId ? store6.invoices.find((i) => i.id === m.invoiceId) : void 0,
|
|
4960
4679
|
score: round2(0.7 * amtScore + 0.3 * dateScore)
|
|
4961
4680
|
};
|
|
4962
4681
|
}).sort((a, b) => b.score - a.score).slice(0, 25);
|
|
4963
4682
|
},
|
|
4964
4683
|
async reconcileMovement(input) {
|
|
4965
|
-
const line =
|
|
4684
|
+
const line = store6.movements.find((m) => m.id === input.bankMovementId);
|
|
4966
4685
|
if (line) {
|
|
4967
4686
|
line.reconciledAt = now();
|
|
4968
4687
|
line.matchedMovementId = input.matchedMovementId;
|
|
4969
4688
|
line.updatedAt = now();
|
|
4970
4689
|
}
|
|
4971
4690
|
if (input.matchedMovementId) {
|
|
4972
|
-
const internal =
|
|
4691
|
+
const internal = store6.movements.find((m) => m.id === input.matchedMovementId);
|
|
4973
4692
|
if (internal) {
|
|
4974
4693
|
internal.reconciledAt = now();
|
|
4975
4694
|
internal.updatedAt = now();
|
|
@@ -4977,14 +4696,14 @@ function createMockFinancialProvider(options) {
|
|
|
4977
4696
|
}
|
|
4978
4697
|
},
|
|
4979
4698
|
async unreconcileMovement(bankMovementId) {
|
|
4980
|
-
const line =
|
|
4699
|
+
const line = store6.movements.find((m) => m.id === bankMovementId);
|
|
4981
4700
|
if (!line) return;
|
|
4982
4701
|
const matchedId = line.matchedMovementId;
|
|
4983
4702
|
line.reconciledAt = void 0;
|
|
4984
4703
|
line.matchedMovementId = void 0;
|
|
4985
4704
|
line.updatedAt = now();
|
|
4986
4705
|
if (matchedId) {
|
|
4987
|
-
const internal =
|
|
4706
|
+
const internal = store6.movements.find((m) => m.id === matchedId);
|
|
4988
4707
|
if (internal) {
|
|
4989
4708
|
internal.reconciledAt = void 0;
|
|
4990
4709
|
internal.updatedAt = now();
|
|
@@ -4993,7 +4712,7 @@ function createMockFinancialProvider(options) {
|
|
|
4993
4712
|
},
|
|
4994
4713
|
// --- Notas fiscais (NF-e / NFC-e / NFS-e) ---
|
|
4995
4714
|
async getNotas(query) {
|
|
4996
|
-
let list = [...
|
|
4715
|
+
let list = [...store6.notas].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
4997
4716
|
if (query.status) list = list.filter((n) => matchesStatus(n.status, query.status));
|
|
4998
4717
|
if (query.model) list = list.filter((n) => n.model === query.model);
|
|
4999
4718
|
if (query.orderId) list = list.filter((n) => n.orderId === query.orderId);
|
|
@@ -5004,7 +4723,7 @@ function createMockFinancialProvider(options) {
|
|
|
5004
4723
|
return paginate(list, query.page, query.pageSize);
|
|
5005
4724
|
},
|
|
5006
4725
|
async getNotaById(id) {
|
|
5007
|
-
return
|
|
4726
|
+
return store6.notas.find((n) => n.id === id) ?? null;
|
|
5008
4727
|
},
|
|
5009
4728
|
async createNota(input) {
|
|
5010
4729
|
const nota = {
|
|
@@ -5022,19 +4741,19 @@ function createMockFinancialProvider(options) {
|
|
|
5022
4741
|
createdAt: now(),
|
|
5023
4742
|
updatedAt: now()
|
|
5024
4743
|
};
|
|
5025
|
-
|
|
4744
|
+
store6.notas.unshift(nota);
|
|
5026
4745
|
return nota;
|
|
5027
4746
|
},
|
|
5028
4747
|
async updateNota(id, patch) {
|
|
5029
|
-
const nota =
|
|
4748
|
+
const nota = store6.notas.find((n) => n.id === id);
|
|
5030
4749
|
if (!nota) throw new Error("Nota fiscal not found");
|
|
5031
4750
|
Object.assign(nota, patch, { updatedAt: now() });
|
|
5032
4751
|
return nota;
|
|
5033
4752
|
},
|
|
5034
4753
|
async issueNota(id) {
|
|
5035
|
-
const nota =
|
|
4754
|
+
const nota = store6.notas.find((n) => n.id === id);
|
|
5036
4755
|
if (!nota) throw new Error("Nota fiscal not found");
|
|
5037
|
-
const issuedCount =
|
|
4756
|
+
const issuedCount = store6.notas.filter((n) => n.status === "issued").length;
|
|
5038
4757
|
nota.status = "issued";
|
|
5039
4758
|
nota.number = String(issuedCount + 1).padStart(6, "0");
|
|
5040
4759
|
nota.series = nota.series ?? "1";
|
|
@@ -5044,7 +4763,7 @@ function createMockFinancialProvider(options) {
|
|
|
5044
4763
|
return nota;
|
|
5045
4764
|
},
|
|
5046
4765
|
async cancelNota(id, reason) {
|
|
5047
|
-
const nota =
|
|
4766
|
+
const nota = store6.notas.find((n) => n.id === id);
|
|
5048
4767
|
if (!nota) throw new Error("Nota fiscal not found");
|
|
5049
4768
|
nota.status = "cancelled";
|
|
5050
4769
|
nota.errorMessage = reason;
|
|
@@ -5331,7 +5050,10 @@ function createSupabaseFinancialProvider() {
|
|
|
5331
5050
|
paid_amount: 0,
|
|
5332
5051
|
status: "pending",
|
|
5333
5052
|
due_date: inst.dueDate,
|
|
5334
|
-
installment_number: inst.installmentNumber ?? i + 1
|
|
5053
|
+
installment_number: inst.installmentNumber ?? i + 1,
|
|
5054
|
+
// O metadata da parcela vinha sendo descartado aqui: o comprovante
|
|
5055
|
+
// anexado no cadastro sobrevivia no mock e sumia no banco.
|
|
5056
|
+
metadata: inst.metadata ?? null
|
|
5335
5057
|
}))
|
|
5336
5058
|
);
|
|
5337
5059
|
}
|
|
@@ -5409,7 +5131,8 @@ function createSupabaseFinancialProvider() {
|
|
|
5409
5131
|
cash_session_id: input.cashSessionId ?? null,
|
|
5410
5132
|
card_brand: input.cardBrand ?? null,
|
|
5411
5133
|
card_installments: input.cardInstallments ?? null,
|
|
5412
|
-
notes: input.notes ?? null
|
|
5134
|
+
notes: input.notes ?? null,
|
|
5135
|
+
metadata: input.metadata ?? null
|
|
5413
5136
|
};
|
|
5414
5137
|
let { data, error } = await pub.from(T.movements).insert(paymentRow).select().single();
|
|
5415
5138
|
if (error && /fee_amount/i.test(error.message ?? "")) {
|
|
@@ -6097,82 +5820,6 @@ function createFinancialStore(provider) {
|
|
|
6097
5820
|
throw err;
|
|
6098
5821
|
}
|
|
6099
5822
|
},
|
|
6100
|
-
async createQuickTransaction(input) {
|
|
6101
|
-
try {
|
|
6102
|
-
if (input.type === "transfer") {
|
|
6103
|
-
if (!input.bankAccountId || !input.toAccountId) {
|
|
6104
|
-
throw new Error("Select source and destination accounts");
|
|
6105
|
-
}
|
|
6106
|
-
await provider.createTransfer({
|
|
6107
|
-
fromAccountId: input.bankAccountId,
|
|
6108
|
-
toAccountId: input.toAccountId,
|
|
6109
|
-
amount: input.amount,
|
|
6110
|
-
date: input.date,
|
|
6111
|
-
notes: input.description?.trim() || void 0
|
|
6112
|
-
});
|
|
6113
|
-
} else {
|
|
6114
|
-
const direction = input.type === "income" ? "credit" : "debit";
|
|
6115
|
-
const desc = input.description?.trim() || (input.type === "income" ? "Receita" : "Despesa");
|
|
6116
|
-
const invoiceMeta = {};
|
|
6117
|
-
if (input.recurring) invoiceMeta.recurring = true;
|
|
6118
|
-
if (input.receiptUrl) invoiceMeta.receiptUrl = input.receiptUrl;
|
|
6119
|
-
const invoice = await provider.createInvoice({
|
|
6120
|
-
direction,
|
|
6121
|
-
invoiceDate: input.date,
|
|
6122
|
-
contactName: desc,
|
|
6123
|
-
metadata: Object.keys(invoiceMeta).length > 0 ? invoiceMeta : void 0,
|
|
6124
|
-
items: [{
|
|
6125
|
-
itemKind: "other",
|
|
6126
|
-
description: desc,
|
|
6127
|
-
quantity: 1,
|
|
6128
|
-
unitPrice: input.amount,
|
|
6129
|
-
accountId: input.categoryId
|
|
6130
|
-
}],
|
|
6131
|
-
installments: [{
|
|
6132
|
-
direction,
|
|
6133
|
-
movementKind: "bill",
|
|
6134
|
-
amount: input.amount,
|
|
6135
|
-
dueDate: input.date,
|
|
6136
|
-
installmentNumber: 1,
|
|
6137
|
-
bankAccountId: input.bankAccountId,
|
|
6138
|
-
notes: desc
|
|
6139
|
-
}]
|
|
6140
|
-
});
|
|
6141
|
-
if (input.paid) {
|
|
6142
|
-
const movements = await provider.getMovements({ direction, status: "pending" });
|
|
6143
|
-
const bill = movements.data.find((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
|
|
6144
|
-
if (bill) {
|
|
6145
|
-
await provider.payMovement({
|
|
6146
|
-
movementId: bill.id,
|
|
6147
|
-
amount: input.amount,
|
|
6148
|
-
paymentDate: input.date,
|
|
6149
|
-
bankAccountId: input.bankAccountId,
|
|
6150
|
-
notes: desc
|
|
6151
|
-
});
|
|
6152
|
-
}
|
|
6153
|
-
}
|
|
6154
|
-
}
|
|
6155
|
-
const invoiceQuery = get().invoiceQuery;
|
|
6156
|
-
const statementQuery = get().statementQuery;
|
|
6157
|
-
const [summary, bankAccounts, invoicesRes, statement] = await Promise.all([
|
|
6158
|
-
provider.getSummary(),
|
|
6159
|
-
provider.getBankAccounts(),
|
|
6160
|
-
provider.getInvoices(invoiceQuery),
|
|
6161
|
-
statementQuery ? provider.getStatement(statementQuery) : Promise.resolve(get().statement)
|
|
6162
|
-
]);
|
|
6163
|
-
set({
|
|
6164
|
-
summary,
|
|
6165
|
-
bankAccounts,
|
|
6166
|
-
invoices: invoicesRes.data,
|
|
6167
|
-
invoicesTotal: invoicesRes.total,
|
|
6168
|
-
statement
|
|
6169
|
-
});
|
|
6170
|
-
toast.success("Transaction saved");
|
|
6171
|
-
} catch (err) {
|
|
6172
|
-
toast.error("Failed to save transaction", { description: err?.message });
|
|
6173
|
-
throw err;
|
|
6174
|
-
}
|
|
6175
|
-
},
|
|
6176
5823
|
async payMovement(input) {
|
|
6177
5824
|
try {
|
|
6178
5825
|
await provider.payMovement(input);
|
|
@@ -6567,6 +6214,17 @@ var en = {
|
|
|
6567
6214
|
"financial.invoice.items": "Items",
|
|
6568
6215
|
"financial.invoice.keepInvoice": "Keep Invoice",
|
|
6569
6216
|
"financial.invoice.new": "New",
|
|
6217
|
+
"financial.invoice.newManual": "Fill it in by hand",
|
|
6218
|
+
"financial.invoiceForm.createContact": "Create",
|
|
6219
|
+
"financial.invoiceForm.alreadyPaid": "Already paid",
|
|
6220
|
+
"financial.invoiceForm.paidWith": "Payment method",
|
|
6221
|
+
"financial.invoiceForm.paidFrom": "Paid from",
|
|
6222
|
+
"financial.invoiceForm.selectMethod": "Select a method",
|
|
6223
|
+
"financial.invoiceForm.selectAccount": "Select an account",
|
|
6224
|
+
"financial.invoiceForm.paidNeedsMethod": "Tell us the payment method and the account",
|
|
6225
|
+
"financial.invoiceForm.willBePaid": "This installment will be settled",
|
|
6226
|
+
"financial.invoiceForm.clear": "Clear",
|
|
6227
|
+
"financial.invoice.newManualHint": "Open the full invoice form",
|
|
6570
6228
|
"financial.invoice.noDetails": "No additional details recorded",
|
|
6571
6229
|
"financial.invoice.noInstallments": "No installments",
|
|
6572
6230
|
"financial.invoice.noInvoices": "No invoices yet",
|
|
@@ -7068,6 +6726,17 @@ var ptBR = {
|
|
|
7068
6726
|
"financial.invoice.items": "Itens",
|
|
7069
6727
|
"financial.invoice.keepInvoice": "Manter Fatura",
|
|
7070
6728
|
"financial.invoice.new": "Nova",
|
|
6729
|
+
"financial.invoice.newManual": "Cadastrar manualmente",
|
|
6730
|
+
"financial.invoiceForm.createContact": "Cadastrar",
|
|
6731
|
+
"financial.invoiceForm.alreadyPaid": "J\xE1 foi pago",
|
|
6732
|
+
"financial.invoiceForm.paidWith": "Forma de pagamento",
|
|
6733
|
+
"financial.invoiceForm.paidFrom": "Saiu de",
|
|
6734
|
+
"financial.invoiceForm.selectMethod": "Selecione a forma",
|
|
6735
|
+
"financial.invoiceForm.selectAccount": "Selecione a conta",
|
|
6736
|
+
"financial.invoiceForm.paidNeedsMethod": "Diga a forma de pagamento e a conta",
|
|
6737
|
+
"financial.invoiceForm.willBePaid": "Esta parcela entra como paga",
|
|
6738
|
+
"financial.invoiceForm.clear": "Limpar",
|
|
6739
|
+
"financial.invoice.newManualHint": "Preencher os campos da conta",
|
|
7071
6740
|
"financial.invoice.noDetails": "Nenhum detalhe adicional registrado",
|
|
7072
6741
|
"financial.invoice.noInstallments": "Sem parcelas",
|
|
7073
6742
|
"financial.invoice.noInvoices": "Nenhuma fatura ainda",
|
|
@@ -7657,6 +7326,306 @@ var stripeConnectorDef = {
|
|
|
7657
7326
|
await store.disconnect();
|
|
7658
7327
|
}
|
|
7659
7328
|
};
|
|
7329
|
+
|
|
7330
|
+
// src/integrations/acquirers/shared.ts
|
|
7331
|
+
function receivablePermissions(provider) {
|
|
7332
|
+
return [
|
|
7333
|
+
{
|
|
7334
|
+
kind: "data",
|
|
7335
|
+
resource: "payment",
|
|
7336
|
+
actions: ["write"],
|
|
7337
|
+
reason: `Lan\xE7ar no Financeiro cada venda que o ${provider} recebeu: valor, forma de pagamento, parcelas, taxa cobrada e a data em que o dinheiro cai na conta.`,
|
|
7338
|
+
sensitive: true
|
|
7339
|
+
},
|
|
7340
|
+
{
|
|
7341
|
+
kind: "data",
|
|
7342
|
+
resource: "invoice",
|
|
7343
|
+
actions: ["read", "write"],
|
|
7344
|
+
reason: `Encontrar o receb\xEDvel que corresponde \xE0quela venda e dar a baixa quando o ${provider} confirmar o repasse. O valor do receb\xEDvel n\xE3o \xE9 alterado.`,
|
|
7345
|
+
sensitive: true
|
|
7346
|
+
}
|
|
7347
|
+
];
|
|
7348
|
+
}
|
|
7349
|
+
var SETTLE_ON_SETTING = {
|
|
7350
|
+
key: "settleOn",
|
|
7351
|
+
label: "Quando dar a baixa",
|
|
7352
|
+
description: "A venda no cart\xE3o entra hoje e o dinheiro cai depois. Escolha o que conta como recebido.",
|
|
7353
|
+
type: "select",
|
|
7354
|
+
options: [
|
|
7355
|
+
{ value: "payout", label: "Quando o dinheiro cai na conta (repasse)" },
|
|
7356
|
+
{ value: "sale", label: "Na hora da venda" }
|
|
7357
|
+
],
|
|
7358
|
+
defaultValue: "payout"
|
|
7359
|
+
};
|
|
7360
|
+
function methodsSetting(options) {
|
|
7361
|
+
return {
|
|
7362
|
+
key: "methods",
|
|
7363
|
+
label: "Quais vendas trazer",
|
|
7364
|
+
description: "S\xF3 o que estiver marcado aqui vira lan\xE7amento no Financeiro.",
|
|
7365
|
+
type: "multi-select",
|
|
7366
|
+
options,
|
|
7367
|
+
// Nothing marked is a feed with nothing to bring: the connection is real and
|
|
7368
|
+
// reads as "conectado, falta configurar".
|
|
7369
|
+
requiredToOperate: true
|
|
7370
|
+
};
|
|
7371
|
+
}
|
|
7372
|
+
function methodsDetail2(labels, value) {
|
|
7373
|
+
const list = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
7374
|
+
const named = list.map((v) => labels[String(v).trim()] ?? String(v).trim()).filter(Boolean);
|
|
7375
|
+
return named.length > 0 ? named.join(", ") : void 0;
|
|
7376
|
+
}
|
|
7377
|
+
|
|
7378
|
+
// src/integrations/mercadopago/connectorDef.tsx
|
|
7379
|
+
var MERCADOPAGO_CONNECTOR_ID = "mercadopago";
|
|
7380
|
+
var METHOD_LABELS2 = {
|
|
7381
|
+
credit_card: "Cr\xE9dito",
|
|
7382
|
+
debit_card: "D\xE9bito",
|
|
7383
|
+
pix: "Pix",
|
|
7384
|
+
ticket: "Boleto",
|
|
7385
|
+
account_money: "Saldo em conta"
|
|
7386
|
+
};
|
|
7387
|
+
var MERCADOPAGO_SETTINGS = [
|
|
7388
|
+
methodsSetting([
|
|
7389
|
+
{ value: "credit_card", label: METHOD_LABELS2.credit_card },
|
|
7390
|
+
{ value: "debit_card", label: METHOD_LABELS2.debit_card },
|
|
7391
|
+
{ value: "pix", label: METHOD_LABELS2.pix },
|
|
7392
|
+
{ value: "ticket", label: METHOD_LABELS2.ticket },
|
|
7393
|
+
{ value: "account_money", label: METHOD_LABELS2.account_money }
|
|
7394
|
+
]),
|
|
7395
|
+
SETTLE_ON_SETTING
|
|
7396
|
+
];
|
|
7397
|
+
var store2 = createConnectionStore(MERCADOPAGO_CONNECTOR_ID, {
|
|
7398
|
+
settings: MERCADOPAGO_SETTINGS,
|
|
7399
|
+
settingsKeys: ["collectorId"]
|
|
7400
|
+
});
|
|
7401
|
+
var mercadoPagoConnectorDef = {
|
|
7402
|
+
id: MERCADOPAGO_CONNECTOR_ID,
|
|
7403
|
+
hostPluginId: "financial",
|
|
7404
|
+
name: "Mercado Pago",
|
|
7405
|
+
description: "Traga para o Financeiro tudo que entrou no Mercado Pago \u2014 Pix, cart\xE3o e maquininha \u2014 com a taxa e a data do repasse.",
|
|
7406
|
+
icon: "CreditCard",
|
|
7407
|
+
category: "payments",
|
|
7408
|
+
authKind: "oauth",
|
|
7409
|
+
// We reach Mercado Pago for what it received; nothing here charges anyone.
|
|
7410
|
+
direction: "inbound",
|
|
7411
|
+
planes: ["sync"],
|
|
7412
|
+
docsUrl: "https://www.mercadopago.com.br/developers/pt/docs/checkout-api/additional-content/security/oauth/creation",
|
|
7413
|
+
permissions: receivablePermissions("Mercado Pago"),
|
|
7414
|
+
fields: [],
|
|
7415
|
+
settings: MERCADOPAGO_SETTINGS,
|
|
7416
|
+
howItSyncs: [
|
|
7417
|
+
"De tempos em tempos, o que o Mercado Pago recebeu vira lan\xE7amento no Financeiro.",
|
|
7418
|
+
"Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem no Mercado Pago.",
|
|
7419
|
+
"A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
|
|
7420
|
+
],
|
|
7421
|
+
...connectionStatus(
|
|
7422
|
+
MERCADOPAGO_CONNECTOR_ID,
|
|
7423
|
+
(connection) => ({
|
|
7424
|
+
connected: !!connection?.active,
|
|
7425
|
+
detail: methodsDetail2(METHOD_LABELS2, connection?.settings?.methods),
|
|
7426
|
+
plane: "sync",
|
|
7427
|
+
...settingsStatus(MERCADOPAGO_SETTINGS, connection)
|
|
7428
|
+
}),
|
|
7429
|
+
{ store: () => store2 }
|
|
7430
|
+
),
|
|
7431
|
+
...connectionSettings(store2),
|
|
7432
|
+
...connectionRuns(store2),
|
|
7433
|
+
startOAuth: fayzConnectorOAuth(MERCADOPAGO_CONNECTOR_ID),
|
|
7434
|
+
saveConnection: async () => {
|
|
7435
|
+
await store2.save({ active: true });
|
|
7436
|
+
},
|
|
7437
|
+
disconnect: () => store2.disconnect(),
|
|
7438
|
+
disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
|
|
7439
|
+
};
|
|
7440
|
+
var PAGBANK_CONNECTOR_ID = "pagbank";
|
|
7441
|
+
var METHOD_LABELS3 = {
|
|
7442
|
+
CREDIT_CARD: "Cr\xE9dito",
|
|
7443
|
+
DEBIT_CARD: "D\xE9bito",
|
|
7444
|
+
PIX: "Pix",
|
|
7445
|
+
BOLETO: "Boleto"
|
|
7446
|
+
};
|
|
7447
|
+
var PAGBANK_SETTINGS = [
|
|
7448
|
+
methodsSetting([
|
|
7449
|
+
{ value: "CREDIT_CARD", label: METHOD_LABELS3.CREDIT_CARD },
|
|
7450
|
+
{ value: "DEBIT_CARD", label: METHOD_LABELS3.DEBIT_CARD },
|
|
7451
|
+
{ value: "PIX", label: METHOD_LABELS3.PIX },
|
|
7452
|
+
{ value: "BOLETO", label: METHOD_LABELS3.BOLETO }
|
|
7453
|
+
]),
|
|
7454
|
+
SETTLE_ON_SETTING
|
|
7455
|
+
];
|
|
7456
|
+
var store3 = createConnectionStore(PAGBANK_CONNECTOR_ID, {
|
|
7457
|
+
settings: PAGBANK_SETTINGS,
|
|
7458
|
+
settingsKeys: ["accountId"]
|
|
7459
|
+
});
|
|
7460
|
+
var pagBankConnectorDef = {
|
|
7461
|
+
id: PAGBANK_CONNECTOR_ID,
|
|
7462
|
+
hostPluginId: "financial",
|
|
7463
|
+
name: "PagBank",
|
|
7464
|
+
description: "Traga para o Financeiro tudo que entrou no PagBank \u2014 Pix, cart\xE3o e maquininha \u2014 com a taxa e a data do repasse.",
|
|
7465
|
+
icon: "CreditCard",
|
|
7466
|
+
// The catalogue carries no `pagbank` row yet, so the mark is declared here
|
|
7467
|
+
// rather than borrowed. Without it the row renders as a grey plug.
|
|
7468
|
+
logoUrl: "https://assets.fayz.ai/connectors/pagbank_pagseguro.png",
|
|
7469
|
+
category: "payments",
|
|
7470
|
+
authKind: "oauth",
|
|
7471
|
+
direction: "inbound",
|
|
7472
|
+
planes: ["sync"],
|
|
7473
|
+
docsUrl: "https://developer.pagbank.com.br/docs/connect-authorization",
|
|
7474
|
+
permissions: receivablePermissions("PagBank"),
|
|
7475
|
+
fields: [],
|
|
7476
|
+
settings: PAGBANK_SETTINGS,
|
|
7477
|
+
howItSyncs: [
|
|
7478
|
+
"De tempos em tempos, o que o PagBank recebeu vira lan\xE7amento no Financeiro.",
|
|
7479
|
+
"Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem no PagBank.",
|
|
7480
|
+
"A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
|
|
7481
|
+
],
|
|
7482
|
+
...connectionStatus(
|
|
7483
|
+
PAGBANK_CONNECTOR_ID,
|
|
7484
|
+
(connection) => ({
|
|
7485
|
+
connected: !!connection?.active,
|
|
7486
|
+
detail: methodsDetail2(METHOD_LABELS3, connection?.settings?.methods),
|
|
7487
|
+
plane: "sync",
|
|
7488
|
+
...settingsStatus(PAGBANK_SETTINGS, connection)
|
|
7489
|
+
}),
|
|
7490
|
+
{ store: () => store3 }
|
|
7491
|
+
),
|
|
7492
|
+
...connectionSettings(store3),
|
|
7493
|
+
...connectionRuns(store3),
|
|
7494
|
+
startOAuth: fayzConnectorOAuth(PAGBANK_CONNECTOR_ID),
|
|
7495
|
+
saveConnection: async () => {
|
|
7496
|
+
await store3.save({ active: true });
|
|
7497
|
+
},
|
|
7498
|
+
disconnect: () => store3.disconnect(),
|
|
7499
|
+
disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
|
|
7500
|
+
};
|
|
7501
|
+
var CIELO_CONNECTOR_ID = "cielo";
|
|
7502
|
+
var CIELO_FIELDS = [
|
|
7503
|
+
{ key: "merchantId", label: "Merchant ID", type: "text", secret: true, placeholder: "Cielo \u2192 Gest\xE3o API \u2192 Credencial API" },
|
|
7504
|
+
{ key: "merchantKey", label: "Merchant Key", type: "password", placeholder: "Mostrada uma \xFAnica vez, ao gerar" }
|
|
7505
|
+
];
|
|
7506
|
+
var METHOD_LABELS4 = {
|
|
7507
|
+
CreditCard: "Cr\xE9dito",
|
|
7508
|
+
DebitCard: "D\xE9bito",
|
|
7509
|
+
Pix: "Pix"
|
|
7510
|
+
};
|
|
7511
|
+
var CIELO_SETTINGS = [
|
|
7512
|
+
methodsSetting([
|
|
7513
|
+
{ value: "CreditCard", label: METHOD_LABELS4.CreditCard },
|
|
7514
|
+
{ value: "DebitCard", label: METHOD_LABELS4.DebitCard },
|
|
7515
|
+
{ value: "Pix", label: METHOD_LABELS4.Pix }
|
|
7516
|
+
]),
|
|
7517
|
+
SETTLE_ON_SETTING
|
|
7518
|
+
];
|
|
7519
|
+
var store4 = createConnectionStore(CIELO_CONNECTOR_ID, {
|
|
7520
|
+
fields: CIELO_FIELDS,
|
|
7521
|
+
settings: CIELO_SETTINGS
|
|
7522
|
+
});
|
|
7523
|
+
var cieloConnectorDef = {
|
|
7524
|
+
id: CIELO_CONNECTOR_ID,
|
|
7525
|
+
hostPluginId: "financial",
|
|
7526
|
+
name: "Cielo",
|
|
7527
|
+
description: "Traga para o Financeiro tudo que entrou na Cielo \u2014 cr\xE9dito, d\xE9bito e Pix \u2014 com a taxa e a data do repasse.",
|
|
7528
|
+
icon: "CreditCard",
|
|
7529
|
+
// The catalogue carries no `cielo` row yet, so the mark is declared here.
|
|
7530
|
+
logoUrl: "https://assets.fayz.ai/connectors/cielo.jpg",
|
|
7531
|
+
category: "payments",
|
|
7532
|
+
authKind: "api-key",
|
|
7533
|
+
direction: "inbound",
|
|
7534
|
+
planes: ["sync"],
|
|
7535
|
+
docsUrl: "https://developercielo.github.io/manual/cielo-ecommerce",
|
|
7536
|
+
providerInstall: { url: "https://www.cielo.com.br/" },
|
|
7537
|
+
permissions: receivablePermissions("Cielo"),
|
|
7538
|
+
fields: CIELO_FIELDS,
|
|
7539
|
+
settings: CIELO_SETTINGS,
|
|
7540
|
+
howItSyncs: [
|
|
7541
|
+
"De tempos em tempos, o que a Cielo recebeu vira lan\xE7amento no Financeiro.",
|
|
7542
|
+
"Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem na Cielo.",
|
|
7543
|
+
"A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
|
|
7544
|
+
],
|
|
7545
|
+
...connectionStatus(
|
|
7546
|
+
CIELO_CONNECTOR_ID,
|
|
7547
|
+
(connection) => ({
|
|
7548
|
+
connected: !!connection?.active,
|
|
7549
|
+
detail: methodsDetail2(METHOD_LABELS4, connection?.settings?.methods),
|
|
7550
|
+
plane: "sync",
|
|
7551
|
+
...settingsStatus(CIELO_SETTINGS, connection)
|
|
7552
|
+
}),
|
|
7553
|
+
{ store: () => store4 }
|
|
7554
|
+
),
|
|
7555
|
+
...connectionSettings(store4),
|
|
7556
|
+
...connectionRuns(store4),
|
|
7557
|
+
// Reached only after the vault accepted the pair; neither half arrives here.
|
|
7558
|
+
saveConnection: async () => {
|
|
7559
|
+
await store4.save({ active: true });
|
|
7560
|
+
},
|
|
7561
|
+
disconnect: () => store4.disconnect(),
|
|
7562
|
+
disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
|
|
7563
|
+
};
|
|
7564
|
+
var STONE_CONNECTOR_ID = "stone";
|
|
7565
|
+
var STONE_FIELDS = [
|
|
7566
|
+
{ key: "stoneCode", label: "Stone Code", type: "text", placeholder: "N\xFAmero de afilia\xE7\xE3o da sua loja" },
|
|
7567
|
+
{ key: "secretKey", label: "Secret Key", type: "password", placeholder: "Stone \u2192 painel \u2192 chaves de API" }
|
|
7568
|
+
];
|
|
7569
|
+
var METHOD_LABELS5 = {
|
|
7570
|
+
credit: "Cr\xE9dito",
|
|
7571
|
+
debit: "D\xE9bito",
|
|
7572
|
+
pix: "Pix",
|
|
7573
|
+
voucher: "Vale-refei\xE7\xE3o"
|
|
7574
|
+
};
|
|
7575
|
+
var STONE_SETTINGS = [
|
|
7576
|
+
methodsSetting([
|
|
7577
|
+
{ value: "credit", label: METHOD_LABELS5.credit },
|
|
7578
|
+
{ value: "debit", label: METHOD_LABELS5.debit },
|
|
7579
|
+
{ value: "pix", label: METHOD_LABELS5.pix },
|
|
7580
|
+
{ value: "voucher", label: METHOD_LABELS5.voucher }
|
|
7581
|
+
]),
|
|
7582
|
+
SETTLE_ON_SETTING
|
|
7583
|
+
];
|
|
7584
|
+
var store5 = createConnectionStore(STONE_CONNECTOR_ID, {
|
|
7585
|
+
fields: STONE_FIELDS,
|
|
7586
|
+
settings: STONE_SETTINGS
|
|
7587
|
+
});
|
|
7588
|
+
var stoneConnectorDef = {
|
|
7589
|
+
id: STONE_CONNECTOR_ID,
|
|
7590
|
+
hostPluginId: "financial",
|
|
7591
|
+
name: "Stone",
|
|
7592
|
+
description: "Traga para o Financeiro tudo que entrou na Stone \u2014 cr\xE9dito, d\xE9bito, Pix e vale \u2014 com a taxa e a data do repasse.",
|
|
7593
|
+
icon: "CreditCard",
|
|
7594
|
+
category: "payments",
|
|
7595
|
+
authKind: "api-key",
|
|
7596
|
+
direction: "inbound",
|
|
7597
|
+
planes: ["sync"],
|
|
7598
|
+
docsUrl: "https://docs.stone.com.br",
|
|
7599
|
+
permissions: receivablePermissions("Stone"),
|
|
7600
|
+
fields: STONE_FIELDS,
|
|
7601
|
+
settings: STONE_SETTINGS,
|
|
7602
|
+
howItSyncs: [
|
|
7603
|
+
"De tempos em tempos, o que a Stone recebeu vira lan\xE7amento no Financeiro.",
|
|
7604
|
+
"Uma venda j\xE1 lan\xE7ada n\xE3o entra duas vezes: ela guarda o n\xFAmero que tem na Stone.",
|
|
7605
|
+
"A taxa vem junto, ent\xE3o o valor l\xEDquido \xE9 o que aparece no caixa."
|
|
7606
|
+
],
|
|
7607
|
+
...connectionStatus(
|
|
7608
|
+
STONE_CONNECTOR_ID,
|
|
7609
|
+
(connection) => ({
|
|
7610
|
+
connected: !!connection?.active,
|
|
7611
|
+
detail: methodsDetail2(METHOD_LABELS5, connection?.settings?.methods),
|
|
7612
|
+
plane: "sync",
|
|
7613
|
+
...settingsStatus(STONE_SETTINGS, connection)
|
|
7614
|
+
}),
|
|
7615
|
+
{ store: () => store5 }
|
|
7616
|
+
),
|
|
7617
|
+
...connectionSettings(store5),
|
|
7618
|
+
...connectionRuns(store5),
|
|
7619
|
+
saveConnection: async (values) => {
|
|
7620
|
+
await store5.save({
|
|
7621
|
+
active: true,
|
|
7622
|
+
providerAccountKey: values.stoneCode ?? null,
|
|
7623
|
+
settings: { stoneCode: values.stoneCode }
|
|
7624
|
+
});
|
|
7625
|
+
},
|
|
7626
|
+
disconnect: () => store5.disconnect(),
|
|
7627
|
+
disconnectNote: "Desconectar para de trazer vendas novas. O que j\xE1 foi lan\xE7ado no Financeiro continua l\xE1."
|
|
7628
|
+
};
|
|
7660
7629
|
var STATUS_STYLE = {
|
|
7661
7630
|
paid: "bg-success/15 text-success",
|
|
7662
7631
|
partial: "bg-warning/15 text-warning",
|
|
@@ -7811,8 +7780,6 @@ function resolveConfig(options) {
|
|
|
7811
7780
|
// Opt-in: only shown when an app explicitly enables fiscal-invoice tracking.
|
|
7812
7781
|
notasFiscais: options?.modules?.notasFiscais === true
|
|
7813
7782
|
},
|
|
7814
|
-
// Opt-in: only render the plugin's own quick-add buttons/FAB when the host asks.
|
|
7815
|
-
quickAdd: options?.quickAdd === true,
|
|
7816
7783
|
labels: { ...DEFAULT_LABELS, ...options?.labels },
|
|
7817
7784
|
currency: { ...DEFAULT_CURRENCY, ...options?.currency },
|
|
7818
7785
|
moduleNav: options?.moduleNav,
|
|
@@ -7834,12 +7801,12 @@ function createFinancialPlugin(options) {
|
|
|
7834
7801
|
registerTranslations(financialLocales);
|
|
7835
7802
|
registerMessageDefaults(buildFinancialMessageDefaults());
|
|
7836
7803
|
const provider = options?.dataProvider ?? createSafeFinancialProvider();
|
|
7837
|
-
const
|
|
7838
|
-
const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store:
|
|
7804
|
+
const store6 = createFinancialStore(provider);
|
|
7805
|
+
const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store: store6 });
|
|
7839
7806
|
const registries = [...financialRegistries, ...options?.settingsRegistries ?? []];
|
|
7840
|
-
const PersonStatementBound = (props) =>
|
|
7807
|
+
const PersonStatementBound = (props) => React17.createElement(PersonStatementWidget, { person: props.item, provider, config });
|
|
7841
7808
|
PersonStatementBound.displayName = "FinancialPersonStatement";
|
|
7842
|
-
const PageComponent = () =>
|
|
7809
|
+
const PageComponent = () => React17.createElement(FinancialPage, { config, provider, store: store6, registries });
|
|
7843
7810
|
return {
|
|
7844
7811
|
id: "financial",
|
|
7845
7812
|
name: config.labels.pageTitle,
|
|
@@ -8045,10 +8012,10 @@ function createFinancialPlugin(options) {
|
|
|
8045
8012
|
label: "Financial",
|
|
8046
8013
|
icon: "DollarSign",
|
|
8047
8014
|
component: (() => {
|
|
8048
|
-
const FinancialSettingsTab = () =>
|
|
8015
|
+
const FinancialSettingsTab = () => React17.createElement(PluginSettingsPanel, {
|
|
8049
8016
|
title: "Financial Settings",
|
|
8050
8017
|
subtitle: "Payment methods, accounts, and configurations",
|
|
8051
|
-
generalSettings:
|
|
8018
|
+
generalSettings: React17.createElement(FinancialGeneralSettings),
|
|
8052
8019
|
registries,
|
|
8053
8020
|
hostPluginId: "financial",
|
|
8054
8021
|
routeBase: "/settings/financial"
|
|
@@ -8063,7 +8030,18 @@ function createFinancialPlugin(options) {
|
|
|
8063
8030
|
// Stripe is the first connector this module implements rather than merely
|
|
8064
8031
|
// shelves — it stays in `integrations` too, because the union is what the
|
|
8065
8032
|
// catalogue row joins on.
|
|
8066
|
-
|
|
8033
|
+
// Stripe emits the receivable; the four below bring back what was actually
|
|
8034
|
+
// received, which is the other half of a conciliação. Mercado Pago and
|
|
8035
|
+
// PagBank broker the merchant's consent, so they ask for nothing; Cielo and
|
|
8036
|
+
// Stone issue their credential TO the merchant and have no consent flow to
|
|
8037
|
+
// broker — see src/integrations/acquirers/ACQUIRERS.md.
|
|
8038
|
+
connectors: [
|
|
8039
|
+
stripeConnectorDef,
|
|
8040
|
+
mercadoPagoConnectorDef,
|
|
8041
|
+
pagBankConnectorDef,
|
|
8042
|
+
cieloConnectorDef,
|
|
8043
|
+
stoneConnectorDef
|
|
8044
|
+
],
|
|
8067
8045
|
// TecnoSpeed is live already — the openbanking addon's `plugbank`
|
|
8068
8046
|
// connector IS this slug. AbacatePay and Pagar.me were named as what
|
|
8069
8047
|
// having Financeiro should put on the shelf; Mercado Pago, Bling and Stone
|
|
@@ -8073,6 +8051,6 @@ function createFinancialPlugin(options) {
|
|
|
8073
8051
|
};
|
|
8074
8052
|
}
|
|
8075
8053
|
|
|
8076
|
-
export { FINANCIAL_NAV_FEATURES, buildFinancialDomainEvents, buildFinancialMessageDefaults, createFinancialPlugin, createMockFinancialProvider, createSafeFinancialProvider
|
|
8054
|
+
export { FINANCIAL_NAV_FEATURES, buildFinancialDomainEvents, buildFinancialMessageDefaults, createFinancialPlugin, createMockFinancialProvider, createSafeFinancialProvider };
|
|
8077
8055
|
//# sourceMappingURL=index.js.map
|
|
8078
8056
|
//# sourceMappingURL=index.js.map
|