@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/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';
@@ -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, ChevronDown, Clock, CheckCheck, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, Ban, LayoutList, CircleDashed, CircleAlert, CircleCheckBig, CircleEllipsis, Pencil, FileText, User, Calendar, Hash, ExternalLink, Building2, ArrowLeftRight, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, Search, Trash2, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
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
- (accounts.length > 0 || costCenters.length > 0) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-3 rounded-lg border bg-muted/20 px-4 py-3", children: [
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: onNew ? t2("financial.invoice.createFirst") : void 0,
835
- onEmptyAction: onNew
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: true, onChange: () => {
3675
- } }),
3676
- /* @__PURE__ */ jsx(ToggleRow, { label: t2("financial.settings.requireDoc"), description: t2("financial.settings.requireDocDesc"), checked: false, onChange: () => {
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: true, onChange: () => {
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: true, onChange: () => {
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
- /* @__PURE__ */ jsxs(
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
- renderView(),
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) qb = qb.gte("created_at", query.dateRange.from).lte("created_at", query.dateRange.to);
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