@fayz-ai/plugin-financial 0.8.0 → 0.8.1
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/FinancialContext.d.ts +6 -0
- package/dist/FinancialContext.d.ts.map +1 -1
- package/dist/FinancialPage.d.ts.map +1 -1
- package/dist/components/FinancialGeneralSettings.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/index.cjs +281 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +283 -32
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +3 -1
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/views/CardsView.d.ts.map +1 -1
- package/dist/views/InvoiceFormView.d.ts.map +1 -1
- package/dist/views/InvoiceListView.d.ts.map +1 -1
- package/dist/views/QuickTransactionForm.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/FinancialContext.tsx +6 -0
- package/src/FinancialPage.tsx +56 -12
- package/src/components/FinancialGeneralSettings.tsx +21 -8
- package/src/data/supabase.ts +6 -1
- package/src/index.ts +128 -0
- package/src/locales/en.ts +10 -0
- package/src/locales/pt-BR.ts +10 -0
- package/src/migrations/004_order_to_cash.sql +15 -1
- package/src/migrations/009_agent_rpcs.sql +165 -0
- package/src/migrations/010_invoice_internal.sql +133 -0
- package/src/migrations/index.ts +320 -2
- package/src/views/CardsView.tsx +12 -9
- package/src/views/InvoiceFormView.tsx +10 -0
- package/src/views/InvoiceListView.tsx +98 -9
- package/src/views/QuickTransactionForm.tsx +11 -0
package/dist/index.d.ts
CHANGED
|
@@ -95,7 +95,38 @@ export interface FinancialPluginOptions {
|
|
|
95
95
|
settingsRegistries?: PluginRegistryDef[];
|
|
96
96
|
/** Callback when user clicks a booking link on an invoice. Receives the order ID. */
|
|
97
97
|
onBookingClick?: (orderId: string) => void;
|
|
98
|
+
/**
|
|
99
|
+
* Which internal-nav links this APP gates, as `nav item id → feature id`.
|
|
100
|
+
* Empty by default: the plugin ships no opinion about what is paid — packaging
|
|
101
|
+
* is the app's decision, declared alongside its `permissions.ts` (RBAC) and
|
|
102
|
+
* `billing.ts` (plan entitlements). An id absent from this map is never hidden
|
|
103
|
+
* and never gated, in the nav OR in the content.
|
|
104
|
+
*
|
|
105
|
+
* {@link FINANCIAL_NAV_FEATURES} is an opt-in preset of the `fin_*` ids used
|
|
106
|
+
* by the reference apps — spread the entries you actually want:
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* navFeatureMap: { reconciliation: FINANCIAL_NAV_FEATURES.reconciliation }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
navFeatureMap?: Record<string, string>;
|
|
98
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* OPT-IN preset of feature ids for the financial sub-modules — a naming
|
|
116
|
+
* convention offered to apps, NOT a default. Nothing here is applied unless an
|
|
117
|
+
* app passes the entries it wants through `options.navFeatureMap`, because what
|
|
118
|
+
* is paid is a product decision that belongs to the app (its `permissions.ts` +
|
|
119
|
+
* `billing.ts`), never to the plugin.
|
|
120
|
+
*/
|
|
121
|
+
export declare const FINANCIAL_NAV_FEATURES: {
|
|
122
|
+
readonly reconciliation: "fin_reconciliation";
|
|
123
|
+
readonly cards: "fin_cards";
|
|
124
|
+
readonly commissions: "fin_commissions";
|
|
125
|
+
readonly statements: "fin_statements";
|
|
126
|
+
readonly receivables: "fin_receivables";
|
|
127
|
+
readonly payables: "fin_payables";
|
|
128
|
+
readonly 'cash-registers': "fin_cash_registers";
|
|
129
|
+
};
|
|
99
130
|
export declare function createFinancialPlugin(options?: FinancialPluginOptions): PluginManifest;
|
|
100
131
|
export type { FinancialDataProvider } from './data/types';
|
|
101
132
|
export type { ResolvedFinancialConfig } from './FinancialContext';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import * as React17 from 'react';
|
|
|
2
2
|
import React17__default, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
|
3
3
|
import { createSafeDataProvider, registerTranslations, useTranslation, getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core';
|
|
4
4
|
import { toast, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, DataTable, PageTransition, ModulePage, Button, Modal, ModalContent, DatePicker, DashboardCanvas, SubpageHeader, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, CurrencyInput, useSaveBar, SearchSelect, ListView, KpiCard, ChartWidget, Card, CardHeader, CardTitle, CardContent, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
|
|
5
|
-
import { createPluginContext, PluginSettingsPanel, dedup, useModuleNavigation, ModuleActionBar, SettingsGroup, ToggleRow, formatCurrency, parseViewId, PersonLink } from '@fayz-ai/saas';
|
|
6
|
-
import { Wallet, Plus, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, X, CalendarDays, Paperclip, Camera, Tag, Repeat, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Receipt,
|
|
5
|
+
import { createPluginContext, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, PermissionGate, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, useLimitGuard, invalidateLimit, PersonLink } from '@fayz-ai/saas';
|
|
6
|
+
import { Wallet, Plus, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, X, CalendarDays, Paperclip, Camera, Tag, Repeat, ChevronDown, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Receipt, Clock, ArrowLeftRight, CheckCheck, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, Ban, LayoutList, CircleDashed, CircleAlert, CircleCheckBig, CircleEllipsis, Pencil, FileText, User, Calendar, Hash, ExternalLink, Building2, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, Search, Trash2, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
import { createStore as createStore$1 } from 'zustand/vanilla';
|
|
@@ -252,6 +252,8 @@ function QuickTransactionForm({ open, onOpenChange, defaultType = "expense", def
|
|
|
252
252
|
const { currency } = useFinancialConfig();
|
|
253
253
|
const provider = useFinancialProvider();
|
|
254
254
|
const createQuickTransaction = useFinancialStore((s) => s.createQuickTransaction);
|
|
255
|
+
const guardMovements = useLimitGuard("movements_month");
|
|
256
|
+
const can = usePermissionOptional();
|
|
255
257
|
const [type, setType] = useState(defaultType);
|
|
256
258
|
const [amount, setAmount] = useState(0);
|
|
257
259
|
const [amountStr, setAmountStr] = useState("");
|
|
@@ -337,12 +339,14 @@ function QuickTransactionForm({ open, onOpenChange, defaultType = "expense", def
|
|
|
337
339
|
}
|
|
338
340
|
const canSave = amount > 0 && !saving && (type === "transfer" ? !!accountId && !!toAccountId && accountId !== toAccountId : true);
|
|
339
341
|
async function handleSave() {
|
|
342
|
+
if (!can("financial", "create")) return;
|
|
340
343
|
if (!canSave) {
|
|
341
344
|
if (type === "transfer" && accountId && accountId === toAccountId) {
|
|
342
345
|
toast.error(t2("financial.transfer.sameAccount"));
|
|
343
346
|
}
|
|
344
347
|
return;
|
|
345
348
|
}
|
|
349
|
+
if (await guardMovements(type === "transfer" ? 2 : 1) === "blocked") return;
|
|
346
350
|
setSaving(true);
|
|
347
351
|
try {
|
|
348
352
|
await createQuickTransaction({
|
|
@@ -357,6 +361,7 @@ function QuickTransactionForm({ open, onOpenChange, defaultType = "expense", def
|
|
|
357
361
|
recurring: type === "transfer" ? false : recurring,
|
|
358
362
|
receiptUrl: type === "transfer" ? void 0 : receiptUrl
|
|
359
363
|
});
|
|
364
|
+
invalidateLimit("movements_month");
|
|
360
365
|
onOpenChange(false);
|
|
361
366
|
} catch {
|
|
362
367
|
} finally {
|
|
@@ -645,6 +650,23 @@ var STATUS_OPTIONS = [
|
|
|
645
650
|
{ value: "overdue", labelKey: "financial.invoice.statusOverdue", color: "bg-destructive-soft text-destructive-soft-foreground", icon: CircleAlert },
|
|
646
651
|
{ value: "cancelled", labelKey: "financial.invoice.statusCancelled", color: "bg-muted text-muted-foreground", icon: Ban }
|
|
647
652
|
];
|
|
653
|
+
var isoDate = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
654
|
+
function presetRange(preset) {
|
|
655
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
656
|
+
switch (preset) {
|
|
657
|
+
case "thisMonth":
|
|
658
|
+
return { from: isoDate(new Date(now2.getFullYear(), now2.getMonth(), 1)), to: isoDate(new Date(now2.getFullYear(), now2.getMonth() + 1, 0)) };
|
|
659
|
+
case "lastMonth":
|
|
660
|
+
return { from: isoDate(new Date(now2.getFullYear(), now2.getMonth() - 1, 1)), to: isoDate(new Date(now2.getFullYear(), now2.getMonth(), 0)) };
|
|
661
|
+
case "last30": {
|
|
662
|
+
const from = new Date(now2);
|
|
663
|
+
from.setDate(from.getDate() - 29);
|
|
664
|
+
return { from: isoDate(from), to: isoDate(now2) };
|
|
665
|
+
}
|
|
666
|
+
case "thisYear":
|
|
667
|
+
return { from: isoDate(new Date(now2.getFullYear(), 0, 1)), to: isoDate(new Date(now2.getFullYear(), 11, 31)) };
|
|
668
|
+
}
|
|
669
|
+
}
|
|
648
670
|
function StatusBadge({ status }) {
|
|
649
671
|
const t2 = useTranslation();
|
|
650
672
|
const opt = STATUS_OPTIONS.find((o) => o.value === status);
|
|
@@ -708,6 +730,8 @@ function useInvoiceColumns(currency) {
|
|
|
708
730
|
}
|
|
709
731
|
function InvoiceListView({ direction, onNew, onEdit }) {
|
|
710
732
|
const t2 = useTranslation();
|
|
733
|
+
const can = usePermissionOptional();
|
|
734
|
+
const onNewGated = can("financial", "create") ? onNew : void 0;
|
|
711
735
|
const { currency } = useFinancialConfig();
|
|
712
736
|
const provider = useFinancialProvider();
|
|
713
737
|
const invoices = useFinancialStore((s) => s.invoices);
|
|
@@ -720,6 +744,25 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
720
744
|
const [costCenterId, setCostCenterId] = useState("");
|
|
721
745
|
const [accounts, setAccounts] = useState([]);
|
|
722
746
|
const [costCenters, setCostCenters] = useState([]);
|
|
747
|
+
const [datePreset, setDatePreset] = useState("all");
|
|
748
|
+
const [dateFrom, setDateFrom] = useState("");
|
|
749
|
+
const [dateTo, setDateTo] = useState("");
|
|
750
|
+
const dateRange = useMemo(() => {
|
|
751
|
+
if (!dateFrom && !dateTo) return void 0;
|
|
752
|
+
return { from: dateFrom || "0001-01-01", to: dateTo || "9999-12-31" };
|
|
753
|
+
}, [dateFrom, dateTo]);
|
|
754
|
+
const applyPreset = (value) => {
|
|
755
|
+
setDatePreset(value);
|
|
756
|
+
if (value === "all") {
|
|
757
|
+
setDateFrom("");
|
|
758
|
+
setDateTo("");
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (value === "custom") return;
|
|
762
|
+
const range = presetRange(value);
|
|
763
|
+
setDateFrom(range.from);
|
|
764
|
+
setDateTo(range.to);
|
|
765
|
+
};
|
|
723
766
|
useEffect(() => {
|
|
724
767
|
let active = true;
|
|
725
768
|
Promise.all([provider.getChartOfAccounts(), provider.getCostCenters()]).then(([accountRows, costCenterRows]) => {
|
|
@@ -741,9 +784,10 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
741
784
|
status: statusFilter.length > 0 ? statusFilter : void 0,
|
|
742
785
|
search: search || void 0,
|
|
743
786
|
accountId: accountId || void 0,
|
|
744
|
-
costCenterId: costCenterId || void 0
|
|
787
|
+
costCenterId: costCenterId || void 0,
|
|
788
|
+
dateRange
|
|
745
789
|
});
|
|
746
|
-
}, [direction, statusFilter, search, accountId, costCenterId]);
|
|
790
|
+
}, [direction, statusFilter, search, accountId, costCenterId, dateRange]);
|
|
747
791
|
const filtered = invoices.filter((inv) => inv.direction === direction);
|
|
748
792
|
const listTitle = direction === "debit" ? t2("financial.invoice.accountsPayable") : t2("financial.invoice.accountsReceivable");
|
|
749
793
|
const columns = useInvoiceColumns(currency);
|
|
@@ -773,7 +817,67 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
773
817
|
subtitle: t2("financial.invoice.invoices", { count: filtered.length })
|
|
774
818
|
}
|
|
775
819
|
),
|
|
776
|
-
|
|
820
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-3 rounded-lg border bg-muted/20 px-4 py-3", children: [
|
|
821
|
+
/* @__PURE__ */ jsxs("label", { className: "flex min-w-[180px] flex-col gap-1 text-xs font-medium text-muted-foreground", children: [
|
|
822
|
+
t2("financial.invoice.filterPeriod"),
|
|
823
|
+
/* @__PURE__ */ jsxs(
|
|
824
|
+
"select",
|
|
825
|
+
{
|
|
826
|
+
className: "h-9 rounded-md border bg-background px-3 text-sm text-foreground",
|
|
827
|
+
value: datePreset,
|
|
828
|
+
onChange: (event) => applyPreset(event.target.value),
|
|
829
|
+
children: [
|
|
830
|
+
/* @__PURE__ */ jsx("option", { value: "all", children: t2("financial.invoice.filterAllPeriods") }),
|
|
831
|
+
/* @__PURE__ */ jsx("option", { value: "thisMonth", children: t2("financial.invoice.filterThisMonth") }),
|
|
832
|
+
/* @__PURE__ */ jsx("option", { value: "lastMonth", children: t2("financial.invoice.filterLastMonth") }),
|
|
833
|
+
/* @__PURE__ */ jsx("option", { value: "last30", children: t2("financial.invoice.filterLast30") }),
|
|
834
|
+
/* @__PURE__ */ jsx("option", { value: "thisYear", children: t2("financial.invoice.filterThisYear") }),
|
|
835
|
+
/* @__PURE__ */ jsx("option", { value: "custom", children: t2("financial.invoice.filterCustom") })
|
|
836
|
+
]
|
|
837
|
+
}
|
|
838
|
+
)
|
|
839
|
+
] }),
|
|
840
|
+
/* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs font-medium text-muted-foreground", children: [
|
|
841
|
+
t2("financial.invoice.filterFrom"),
|
|
842
|
+
/* @__PURE__ */ jsx(
|
|
843
|
+
"input",
|
|
844
|
+
{
|
|
845
|
+
type: "date",
|
|
846
|
+
className: "h-9 rounded-md border bg-background px-3 text-sm text-foreground",
|
|
847
|
+
value: dateFrom,
|
|
848
|
+
max: dateTo || void 0,
|
|
849
|
+
onChange: (event) => {
|
|
850
|
+
setDateFrom(event.target.value);
|
|
851
|
+
setDatePreset("custom");
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
)
|
|
855
|
+
] }),
|
|
856
|
+
/* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs font-medium text-muted-foreground", children: [
|
|
857
|
+
t2("financial.invoice.filterTo"),
|
|
858
|
+
/* @__PURE__ */ jsx(
|
|
859
|
+
"input",
|
|
860
|
+
{
|
|
861
|
+
type: "date",
|
|
862
|
+
className: "h-9 rounded-md border bg-background px-3 text-sm text-foreground",
|
|
863
|
+
value: dateTo,
|
|
864
|
+
min: dateFrom || void 0,
|
|
865
|
+
onChange: (event) => {
|
|
866
|
+
setDateTo(event.target.value);
|
|
867
|
+
setDatePreset("custom");
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
)
|
|
871
|
+
] }),
|
|
872
|
+
(dateFrom || dateTo) && /* @__PURE__ */ jsx(
|
|
873
|
+
"button",
|
|
874
|
+
{
|
|
875
|
+
type: "button",
|
|
876
|
+
className: "h-9 rounded-md px-2 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
877
|
+
onClick: () => applyPreset("all"),
|
|
878
|
+
children: t2("financial.invoice.filterClear")
|
|
879
|
+
}
|
|
880
|
+
),
|
|
777
881
|
accounts.length > 0 && /* @__PURE__ */ jsxs("label", { className: "flex min-w-[220px] flex-col gap-1 text-xs font-medium text-muted-foreground", children: [
|
|
778
882
|
t2("financial.invoice.filterAccount"),
|
|
779
883
|
/* @__PURE__ */ jsxs(
|
|
@@ -827,12 +931,12 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
827
931
|
activeTag: statusFilter.length === 1 ? statusFilter[0] : void 0,
|
|
828
932
|
onTagChange: (v) => setStatusFilter(v ? [v] : []),
|
|
829
933
|
newLabel: t2("financial.invoice.new"),
|
|
830
|
-
onNew,
|
|
934
|
+
onNew: onNewGated,
|
|
831
935
|
onRowClick: (row) => onEdit?.(row.id),
|
|
832
936
|
emptyIcon: FileText,
|
|
833
937
|
emptyMessage: t2("financial.invoice.noInvoices"),
|
|
834
|
-
emptyActionLabel:
|
|
835
|
-
onEmptyAction:
|
|
938
|
+
emptyActionLabel: onNewGated ? t2("financial.invoice.createFirst") : void 0,
|
|
939
|
+
onEmptyAction: onNewGated
|
|
836
940
|
}
|
|
837
941
|
),
|
|
838
942
|
filtered.length > 0 && !invoicesLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-6 rounded-lg border bg-muted/30 px-5 py-3 text-sm", children: [
|
|
@@ -1172,6 +1276,8 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1172
1276
|
const hasItemLookups = itemTypes.some((tp) => config.entityLookups[tp.value]);
|
|
1173
1277
|
const defaultItemKind = itemTypes.find((tp) => tp.value === "other")?.value ?? itemTypes.find((tp) => !config.entityLookups[tp.value])?.value ?? itemTypes[0]?.value ?? "other";
|
|
1174
1278
|
const createInvoice = useFinancialStore((s) => s.createInvoice);
|
|
1279
|
+
const guardMovements = useLimitGuard("movements_month");
|
|
1280
|
+
const can = usePermissionOptional();
|
|
1175
1281
|
const hasLocations = locations.length > 0;
|
|
1176
1282
|
const isEdit = !!editId;
|
|
1177
1283
|
const [loaded, setLoaded] = useState(!editId);
|
|
@@ -1309,10 +1415,12 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1309
1415
|
setInstallments(inst);
|
|
1310
1416
|
}
|
|
1311
1417
|
async function handleSave() {
|
|
1418
|
+
if (!isEdit && !can("financial", "create")) return;
|
|
1312
1419
|
if (items.length === 0) {
|
|
1313
1420
|
toast.error(t2("common.formIncomplete"));
|
|
1314
1421
|
return;
|
|
1315
1422
|
}
|
|
1423
|
+
if (!isEdit && await guardMovements(Math.max(1, installments.length)) === "blocked") return;
|
|
1316
1424
|
setSaving(true);
|
|
1317
1425
|
try {
|
|
1318
1426
|
const movInputs = installments.map((inst, i) => ({
|
|
@@ -1354,6 +1462,7 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1354
1462
|
items: items.map(({ _id, ...rest }) => rest),
|
|
1355
1463
|
installments: movInputs
|
|
1356
1464
|
});
|
|
1465
|
+
invalidateLimit("movements_month");
|
|
1357
1466
|
onSaved?.(invoice.id);
|
|
1358
1467
|
}
|
|
1359
1468
|
} finally {
|
|
@@ -3495,7 +3604,7 @@ function CardsView() {
|
|
|
3495
3604
|
/* @__PURE__ */ jsx("span", { children: t2("financial.cards.dueOn", { day: String(card.dueDay) }) })
|
|
3496
3605
|
] })
|
|
3497
3606
|
] }),
|
|
3498
|
-
/* @__PURE__ */ jsxs(
|
|
3607
|
+
/* @__PURE__ */ jsx(PermissionGate, { feature: "financial", action: "create", children: /* @__PURE__ */ jsxs(
|
|
3499
3608
|
"button",
|
|
3500
3609
|
{
|
|
3501
3610
|
type: "button",
|
|
@@ -3506,7 +3615,7 @@ function CardsView() {
|
|
|
3506
3615
|
t2("financial.cards.addExpense")
|
|
3507
3616
|
]
|
|
3508
3617
|
}
|
|
3509
|
-
)
|
|
3618
|
+
) })
|
|
3510
3619
|
] })
|
|
3511
3620
|
] }, card.id);
|
|
3512
3621
|
}) })
|
|
@@ -3667,28 +3776,31 @@ function ReconciliationView() {
|
|
|
3667
3776
|
] })
|
|
3668
3777
|
] });
|
|
3669
3778
|
}
|
|
3779
|
+
var FINANCIAL_DEFAULTS = {
|
|
3780
|
+
autoInstallments: true,
|
|
3781
|
+
requireDoc: false,
|
|
3782
|
+
allowPartial: true,
|
|
3783
|
+
requireOpening: true,
|
|
3784
|
+
autoClose: false,
|
|
3785
|
+
overdueAlerts: true,
|
|
3786
|
+
dailySummary: false
|
|
3787
|
+
};
|
|
3670
3788
|
function FinancialGeneralSettings() {
|
|
3671
3789
|
const t2 = useTranslation();
|
|
3790
|
+
const s = useTenantPluginSettings("financial", FINANCIAL_DEFAULTS);
|
|
3672
3791
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3673
3792
|
/* @__PURE__ */ jsxs(SettingsGroup, { title: t2("financial.settings.invoices"), description: t2("financial.settings.invoicesDesc"), children: [
|
|
3674
|
-
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.autoInstallments"), description: t2("financial.settings.autoInstallmentsDesc"), checked:
|
|
3675
|
-
|
|
3676
|
-
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.
|
|
3677
|
-
} }),
|
|
3678
|
-
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.allowPartial"), description: t2("financial.settings.allowPartialDesc"), checked: true, onChange: () => {
|
|
3679
|
-
} })
|
|
3793
|
+
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.autoInstallments"), description: t2("financial.settings.autoInstallmentsDesc"), checked: s.get("autoInstallments"), onChange: (v) => s.set("autoInstallments", v) }),
|
|
3794
|
+
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.requireDoc"), description: t2("financial.settings.requireDocDesc"), checked: s.get("requireDoc"), onChange: (v) => s.set("requireDoc", v) }),
|
|
3795
|
+
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.allowPartial"), description: t2("financial.settings.allowPartialDesc"), checked: s.get("allowPartial"), onChange: (v) => s.set("allowPartial", v) })
|
|
3680
3796
|
] }),
|
|
3681
3797
|
/* @__PURE__ */ jsxs(SettingsGroup, { title: t2("financial.settings.cashRegister"), description: t2("financial.settings.cashRegisterDesc"), children: [
|
|
3682
|
-
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.requireOpening"), description: t2("financial.settings.requireOpeningDesc"), checked:
|
|
3683
|
-
|
|
3684
|
-
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.autoClose"), description: t2("financial.settings.autoCloseDesc"), checked: false, onChange: () => {
|
|
3685
|
-
} })
|
|
3798
|
+
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.requireOpening"), description: t2("financial.settings.requireOpeningDesc"), checked: s.get("requireOpening"), onChange: (v) => s.set("requireOpening", v) }),
|
|
3799
|
+
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.autoClose"), description: t2("financial.settings.autoCloseDesc"), checked: s.get("autoClose"), onChange: (v) => s.set("autoClose", v) })
|
|
3686
3800
|
] }),
|
|
3687
3801
|
/* @__PURE__ */ jsxs(SettingsGroup, { title: t2("financial.settings.notifications"), description: t2("financial.settings.notificationsDesc"), children: [
|
|
3688
|
-
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.overdueAlerts"), description: t2("financial.settings.overdueAlertsDesc"), checked:
|
|
3689
|
-
|
|
3690
|
-
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.dailySummary"), description: t2("financial.settings.dailySummaryDesc"), checked: false, onChange: () => {
|
|
3691
|
-
} })
|
|
3802
|
+
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.overdueAlerts"), description: t2("financial.settings.overdueAlertsDesc"), checked: s.get("overdueAlerts"), onChange: (v) => s.set("overdueAlerts", v) }),
|
|
3803
|
+
/* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.dailySummary"), description: t2("financial.settings.dailySummaryDesc"), checked: s.get("dailySummary"), onChange: (v) => s.set("dailySummary", v) })
|
|
3692
3804
|
] })
|
|
3693
3805
|
] });
|
|
3694
3806
|
}
|
|
@@ -3829,6 +3941,10 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
3829
3941
|
]
|
|
3830
3942
|
});
|
|
3831
3943
|
}
|
|
3944
|
+
for (const item of items) {
|
|
3945
|
+
const feature = config.navFeatureMap[item.id];
|
|
3946
|
+
if (feature) item.feature = feature;
|
|
3947
|
+
}
|
|
3832
3948
|
return items;
|
|
3833
3949
|
}
|
|
3834
3950
|
function FinancialPage({ config, provider, store, registries }) {
|
|
@@ -3864,9 +3980,11 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3864
3980
|
};
|
|
3865
3981
|
}, [onboardingComplete, provider]);
|
|
3866
3982
|
const t2 = useTranslation();
|
|
3983
|
+
const can = usePermissionOptional();
|
|
3984
|
+
const canCreate = can("financial", "create");
|
|
3867
3985
|
const intent = parseIntent(view);
|
|
3868
3986
|
const isSettings = view === "settings";
|
|
3869
|
-
const nav = buildNav(config, view, navigate, t2);
|
|
3987
|
+
const nav = useModuleNavAccess(buildNav(config, view, navigate, t2));
|
|
3870
3988
|
const [quickAddOpen, setQuickAddOpen] = useState(false);
|
|
3871
3989
|
const [quickAddType, setQuickAddType] = useState("expense");
|
|
3872
3990
|
const [quickAddReceipt, setQuickAddReceipt] = useState(false);
|
|
@@ -3880,12 +3998,14 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3880
3998
|
if (!pendingQuickAdd) return;
|
|
3881
3999
|
const req = consumeQuickAdd();
|
|
3882
4000
|
if (!req) return;
|
|
4001
|
+
if (!canCreate) return;
|
|
3883
4002
|
setQuickAddType(req.type);
|
|
3884
4003
|
setQuickAddReceipt(req.receipt);
|
|
3885
4004
|
setQuickAddOpen(true);
|
|
3886
4005
|
}, [pendingQuickAdd]);
|
|
3887
4006
|
const quickActions = useMemo(() => {
|
|
3888
4007
|
if (config.quickAdd) return [];
|
|
4008
|
+
if (!canCreate) return [];
|
|
3889
4009
|
const actions = [];
|
|
3890
4010
|
if (config.modules.payables) {
|
|
3891
4011
|
actions.push({
|
|
@@ -3915,7 +4035,7 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3915
4035
|
});
|
|
3916
4036
|
}
|
|
3917
4037
|
return actions;
|
|
3918
|
-
}, [config.modules, config.quickAdd]);
|
|
4038
|
+
}, [config.modules, config.quickAdd, canCreate, navigate, t2]);
|
|
3919
4039
|
function handleOnboardingComplete() {
|
|
3920
4040
|
setOnboardingComplete(true);
|
|
3921
4041
|
try {
|
|
@@ -3959,6 +4079,11 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3959
4079
|
return /* @__PURE__ */ jsx(SummaryView, {});
|
|
3960
4080
|
}
|
|
3961
4081
|
}
|
|
4082
|
+
function renderGatedView() {
|
|
4083
|
+
const feature = config.navFeatureMap[intent.view];
|
|
4084
|
+
if (!feature) return renderView();
|
|
4085
|
+
return /* @__PURE__ */ jsx(EntitlementGate, { feature, fallback: /* @__PURE__ */ jsx(UpgradePrompt, { feature }), children: renderView() });
|
|
4086
|
+
}
|
|
3962
4087
|
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store, children: /* @__PURE__ */ jsxs(
|
|
3963
4088
|
ModulePage,
|
|
3964
4089
|
{
|
|
@@ -3973,7 +4098,8 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3973
4098
|
// add affordance in B2C mode. The receipt entry point lives inside the
|
|
3974
4099
|
// sheet's "Fotografar ou anexar recibo" attachment step, so no separate
|
|
3975
4100
|
// "Enviar recibo" button is needed here.
|
|
3976
|
-
|
|
4101
|
+
// Gated on the role's `create` action so read-only members don't see it.
|
|
4102
|
+
/* @__PURE__ */ jsx(PermissionGate, { feature: "financial", action: "create", children: /* @__PURE__ */ jsxs(
|
|
3977
4103
|
Button,
|
|
3978
4104
|
{
|
|
3979
4105
|
size: "sm",
|
|
@@ -3984,7 +4110,7 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3984
4110
|
t2("financial.quickTx.newTransaction")
|
|
3985
4111
|
]
|
|
3986
4112
|
}
|
|
3987
|
-
),
|
|
4113
|
+
) }),
|
|
3988
4114
|
/* @__PURE__ */ jsx(
|
|
3989
4115
|
ModuleActionBar,
|
|
3990
4116
|
{
|
|
@@ -3995,7 +4121,7 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3995
4121
|
)
|
|
3996
4122
|
] }),
|
|
3997
4123
|
children: [
|
|
3998
|
-
|
|
4124
|
+
renderGatedView(),
|
|
3999
4125
|
/* @__PURE__ */ jsx(
|
|
4000
4126
|
QuickTransactionForm,
|
|
4001
4127
|
{
|
|
@@ -5154,7 +5280,10 @@ function createSupabaseFinancialProvider() {
|
|
|
5154
5280
|
}
|
|
5155
5281
|
if (query.contactId) qb = qb.eq("party_id", query.contactId);
|
|
5156
5282
|
if (query.search) qb = qb.ilike("notes", `%${query.search}%`);
|
|
5157
|
-
if (query.dateRange)
|
|
5283
|
+
if (query.dateRange) {
|
|
5284
|
+
const to = query.dateRange.to.length === 10 ? `${query.dateRange.to}T23:59:59.999` : query.dateRange.to;
|
|
5285
|
+
qb = qb.gte("created_at", query.dateRange.from).lte("created_at", to);
|
|
5286
|
+
}
|
|
5158
5287
|
const page = query.page ?? 1;
|
|
5159
5288
|
const pageSize = query.pageSize ?? 50;
|
|
5160
5289
|
qb = qb.range((page - 1) * pageSize, page * pageSize - 1).order("created_at", { ascending: false });
|
|
@@ -6391,6 +6520,16 @@ var en = {
|
|
|
6391
6520
|
"financial.invoice.filterAllAccounts": "All accounts",
|
|
6392
6521
|
"financial.invoice.filterCostCenter": "Cost center",
|
|
6393
6522
|
"financial.invoice.filterAllCostCenters": "All cost centers",
|
|
6523
|
+
"financial.invoice.filterPeriod": "Period",
|
|
6524
|
+
"financial.invoice.filterAllPeriods": "All time",
|
|
6525
|
+
"financial.invoice.filterThisMonth": "This month",
|
|
6526
|
+
"financial.invoice.filterLastMonth": "Last month",
|
|
6527
|
+
"financial.invoice.filterLast30": "Last 30 days",
|
|
6528
|
+
"financial.invoice.filterThisYear": "This year",
|
|
6529
|
+
"financial.invoice.filterCustom": "Custom",
|
|
6530
|
+
"financial.invoice.filterFrom": "From",
|
|
6531
|
+
"financial.invoice.filterTo": "To",
|
|
6532
|
+
"financial.invoice.filterClear": "Clear",
|
|
6394
6533
|
"financial.reconciliation.title": "Reconciliation",
|
|
6395
6534
|
"financial.reconciliation.subtitle": "Match imported bank lines to your movements",
|
|
6396
6535
|
"financial.reconciliation.pending": "{{count}} to reconcile",
|
|
@@ -6874,6 +7013,16 @@ var ptBR = {
|
|
|
6874
7013
|
"financial.invoice.filterAllAccounts": "Todos os planos",
|
|
6875
7014
|
"financial.invoice.filterCostCenter": "Centro de custo",
|
|
6876
7015
|
"financial.invoice.filterAllCostCenters": "Todos os centros",
|
|
7016
|
+
"financial.invoice.filterPeriod": "Per\xEDodo",
|
|
7017
|
+
"financial.invoice.filterAllPeriods": "Todo o per\xEDodo",
|
|
7018
|
+
"financial.invoice.filterThisMonth": "Este m\xEAs",
|
|
7019
|
+
"financial.invoice.filterLastMonth": "M\xEAs passado",
|
|
7020
|
+
"financial.invoice.filterLast30": "\xDAltimos 30 dias",
|
|
7021
|
+
"financial.invoice.filterThisYear": "Este ano",
|
|
7022
|
+
"financial.invoice.filterCustom": "Personalizado",
|
|
7023
|
+
"financial.invoice.filterFrom": "De",
|
|
7024
|
+
"financial.invoice.filterTo": "At\xE9",
|
|
7025
|
+
"financial.invoice.filterClear": "Limpar",
|
|
6877
7026
|
"financial.reconciliation.title": "Concilia\xE7\xE3o",
|
|
6878
7027
|
"financial.reconciliation.subtitle": "Concilie as linhas importadas do banco com seus lan\xE7amentos",
|
|
6879
7028
|
"financial.reconciliation.pending": "{{count}} para conciliar",
|
|
@@ -7343,6 +7492,15 @@ var DEFAULT_LABELS = {
|
|
|
7343
7492
|
cardsReconciliation: "Reconciliation"
|
|
7344
7493
|
};
|
|
7345
7494
|
var DEFAULT_CURRENCY = { code: "BRL", locale: "pt-BR", symbol: "R$" };
|
|
7495
|
+
var FINANCIAL_NAV_FEATURES = {
|
|
7496
|
+
reconciliation: "fin_reconciliation",
|
|
7497
|
+
cards: "fin_cards",
|
|
7498
|
+
commissions: "fin_commissions",
|
|
7499
|
+
statements: "fin_statements",
|
|
7500
|
+
receivables: "fin_receivables",
|
|
7501
|
+
payables: "fin_payables",
|
|
7502
|
+
"cash-registers": "fin_cash_registers"
|
|
7503
|
+
};
|
|
7346
7504
|
var DEFAULT_ITEM_TYPES = [
|
|
7347
7505
|
{ value: "service", label: "Service", icon: "Briefcase" },
|
|
7348
7506
|
{ value: "product", label: "Product", icon: "Package" },
|
|
@@ -7373,7 +7531,8 @@ function resolveConfig(options) {
|
|
|
7373
7531
|
locations: options?.locations ?? [],
|
|
7374
7532
|
entityLookups: options?.entityLookups ?? {},
|
|
7375
7533
|
contactLookup: options?.contactLookup,
|
|
7376
|
-
onBookingClick: options?.onBookingClick
|
|
7534
|
+
onBookingClick: options?.onBookingClick,
|
|
7535
|
+
navFeatureMap: { ...options?.navFeatureMap }
|
|
7377
7536
|
};
|
|
7378
7537
|
}
|
|
7379
7538
|
function createFinancialPlugin(options) {
|
|
@@ -7401,6 +7560,70 @@ function createFinancialPlugin(options) {
|
|
|
7401
7560
|
...config.modules.payables ? [{ id: "financial.payables", label: config.labels.payables, group: config.labels.pageTitle }] : [],
|
|
7402
7561
|
...config.modules.commissions ? [{ id: "financial.commissions", label: config.labels.commissions, group: config.labels.pageTitle }] : []
|
|
7403
7562
|
],
|
|
7563
|
+
// Recurring monthly quota — counts financial movement rows created this
|
|
7564
|
+
// month (invoices write one per installment; quick transactions one, or two
|
|
7565
|
+
// for a transfer).
|
|
7566
|
+
declaredLimits: [
|
|
7567
|
+
{ key: "movements_month", label: "Financial movements this month", table: "plg_financial_movements", period: "month" }
|
|
7568
|
+
],
|
|
7569
|
+
queryEntities: [
|
|
7570
|
+
{
|
|
7571
|
+
key: "financial:invoices",
|
|
7572
|
+
entity: {
|
|
7573
|
+
name: "Invoice",
|
|
7574
|
+
namePlural: "Invoices (receipts REC- / payables PAG-)",
|
|
7575
|
+
icon: "Receipt",
|
|
7576
|
+
permission: { feature: "financial", action: "read" },
|
|
7577
|
+
fields: [
|
|
7578
|
+
{ key: "referenceNumber", label: "Reference (REC-/PAG-)", type: "text", searchable: true },
|
|
7579
|
+
{ key: "flow", label: "Flow (receivable/payable)", type: "text" },
|
|
7580
|
+
{ key: "amount", label: "Amount", type: "number" },
|
|
7581
|
+
{ key: "paid", label: "Paid", type: "number" },
|
|
7582
|
+
{ key: "balance", label: "Balance", type: "number" },
|
|
7583
|
+
{ key: "status", label: "Status", type: "text" },
|
|
7584
|
+
{ key: "createdAt", label: "Created at", type: "text" }
|
|
7585
|
+
],
|
|
7586
|
+
data: {
|
|
7587
|
+
table: "v_invoice_balances",
|
|
7588
|
+
tenantScoped: true,
|
|
7589
|
+
searchColumns: ["reference_number"]
|
|
7590
|
+
}
|
|
7591
|
+
}
|
|
7592
|
+
},
|
|
7593
|
+
{
|
|
7594
|
+
key: "financial:movements",
|
|
7595
|
+
entity: {
|
|
7596
|
+
name: "Financial movement",
|
|
7597
|
+
namePlural: "Financial movements (installment ledger)",
|
|
7598
|
+
icon: "Banknote",
|
|
7599
|
+
permission: { feature: "financial", action: "read" },
|
|
7600
|
+
fields: [
|
|
7601
|
+
{ key: "direction", label: "Direction", type: "text" },
|
|
7602
|
+
{ key: "movementKind", label: "Kind (bill/payment)", type: "text" },
|
|
7603
|
+
{ key: "amount", label: "Amount", type: "number" },
|
|
7604
|
+
{ key: "paidAmount", label: "Paid amount", type: "number" },
|
|
7605
|
+
{ key: "status", label: "Status", type: "text" },
|
|
7606
|
+
{ key: "dueDate", label: "Due date", type: "text" },
|
|
7607
|
+
{ key: "paymentDate", label: "Payment date", type: "text" },
|
|
7608
|
+
{ key: "installmentNumber", label: "Installment", type: "number" },
|
|
7609
|
+
{ key: "cardInstallments", label: "Card installments", type: "number" },
|
|
7610
|
+
{ key: "createdAt", label: "Created at", type: "text" }
|
|
7611
|
+
],
|
|
7612
|
+
data: {
|
|
7613
|
+
table: "plg_financial_movements",
|
|
7614
|
+
tenantScoped: true
|
|
7615
|
+
}
|
|
7616
|
+
}
|
|
7617
|
+
}
|
|
7618
|
+
],
|
|
7619
|
+
declaredRpcs: [
|
|
7620
|
+
{
|
|
7621
|
+
name: "agent_financial_mark_payment_received",
|
|
7622
|
+
kind: "write",
|
|
7623
|
+
description: "Guarded settle: invoices the order via fn_invoice_from_order (installments for credit card) and marks the receivable paid, audited with the acting user.",
|
|
7624
|
+
audits: true
|
|
7625
|
+
}
|
|
7626
|
+
],
|
|
7404
7627
|
navigation: [
|
|
7405
7628
|
{
|
|
7406
7629
|
section: options?.navSection ?? "main",
|
|
@@ -7454,6 +7677,7 @@ function createFinancialPlugin(options) {
|
|
|
7454
7677
|
description: "Creates a new invoice/payable for a contact.",
|
|
7455
7678
|
icon: "FileText",
|
|
7456
7679
|
mode: "persist",
|
|
7680
|
+
limitKey: "movements_month",
|
|
7457
7681
|
category: "Finance",
|
|
7458
7682
|
parameters: {
|
|
7459
7683
|
type: "object",
|
|
@@ -7469,6 +7693,33 @@ function createFinancialPlugin(options) {
|
|
|
7469
7693
|
],
|
|
7470
7694
|
permission: { feature: "financial", action: "create" }
|
|
7471
7695
|
},
|
|
7696
|
+
{
|
|
7697
|
+
id: "financial.mark-payment-received",
|
|
7698
|
+
name: "markPaymentReceived",
|
|
7699
|
+
description: 'Marks an order/appointment as paid: invoices it (with installments for credit card, e.g. "cart\xE3o 2x") and settles the receivable. Resolve the booking or order id via search tools first.',
|
|
7700
|
+
icon: "BadgeDollarSign",
|
|
7701
|
+
mode: "persist",
|
|
7702
|
+
execution: { plane: "server", kind: "rpc", rpc: "agent_financial_mark_payment_received" },
|
|
7703
|
+
category: "Finance",
|
|
7704
|
+
parameters: {
|
|
7705
|
+
type: "object",
|
|
7706
|
+
properties: {
|
|
7707
|
+
booking_id: { type: "string", description: "Appointment id (alternative to order_id)" },
|
|
7708
|
+
order_id: { type: "string", description: "Order id" },
|
|
7709
|
+
method: {
|
|
7710
|
+
type: "string",
|
|
7711
|
+
description: "Payment method",
|
|
7712
|
+
enum: ["cash", "pix", "credit_card", "debit_card", "transfer"]
|
|
7713
|
+
},
|
|
7714
|
+
installments: { type: "number", description: 'Credit-card installments (1-12), e.g. 2 for "cart\xE3o 2x"' }
|
|
7715
|
+
},
|
|
7716
|
+
required: ["method"]
|
|
7717
|
+
},
|
|
7718
|
+
suggestions: [
|
|
7719
|
+
{ label: "Marcar o atendimento como pago no cart\xE3o em 2x" }
|
|
7720
|
+
],
|
|
7721
|
+
permission: { feature: "financial.receivables", action: "update" }
|
|
7722
|
+
},
|
|
7472
7723
|
{
|
|
7473
7724
|
id: "financial.list-payables",
|
|
7474
7725
|
name: "listPayables",
|
|
@@ -7516,6 +7767,6 @@ function createFinancialPlugin(options) {
|
|
|
7516
7767
|
};
|
|
7517
7768
|
}
|
|
7518
7769
|
|
|
7519
|
-
export { createFinancialPlugin, createMockFinancialProvider, createSafeFinancialProvider, openQuickAdd };
|
|
7770
|
+
export { FINANCIAL_NAV_FEATURES, createFinancialPlugin, createMockFinancialProvider, createSafeFinancialProvider, openQuickAdd };
|
|
7520
7771
|
//# sourceMappingURL=index.js.map
|
|
7521
7772
|
//# sourceMappingURL=index.js.map
|