@fayz-ai/plugin-financial 0.8.0 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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
|
@@ -34,6 +34,12 @@ export interface ResolvedFinancialConfig {
|
|
|
34
34
|
entityLookups: EntityLookupMap;
|
|
35
35
|
contactLookup?: EntityLookup;
|
|
36
36
|
onBookingClick?: (orderId: string) => void;
|
|
37
|
+
/** Internal nav item id → access feature id, exactly as the APP declared it
|
|
38
|
+
* (`options.navFeatureMap`; empty when the app gates nothing — the plugin
|
|
39
|
+
* ships no defaults). Drives BOTH the sub-module link paywall
|
|
40
|
+
* (`useModuleNavAccess`) and the matching content gate, so the two can't
|
|
41
|
+
* drift. Unmapped ids are ungated. */
|
|
42
|
+
navFeatureMap: Record<string, string>;
|
|
37
43
|
}
|
|
38
44
|
export declare const FinancialContextProvider: import("react").FC<{
|
|
39
45
|
config: ResolvedFinancialConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FinancialContext.d.ts","sourceRoot":"","sources":["../src/FinancialContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AACjI,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACpE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAM/C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,OAAO,CAAA;IACpB,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,OAAO,CAAA;IACnB,WAAW,EAAE,OAAO,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,mGAAmG;IACnG,cAAc,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE9C,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,gBAAgB,CAAA;IACzB,oFAAoF;IACpF,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B,sBAAsB,EAAE,OAAO,CAAA;IAC/B,aAAa,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACvD,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B,aAAa,EAAE,eAAe,CAAA;IAC9B,aAAa,CAAC,EAAE,YAAY,CAAA;IAC5B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"FinancialContext.d.ts","sourceRoot":"","sources":["../src/FinancialContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AACjI,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACpE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAM/C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,OAAO,CAAA;IACpB,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,OAAO,CAAA;IACnB,WAAW,EAAE,OAAO,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,mGAAmG;IACnG,cAAc,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE9C,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,gBAAgB,CAAA;IACzB,oFAAoF;IACpF,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B,sBAAsB,EAAE,OAAO,CAAA;IAC/B,aAAa,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACvD,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B,aAAa,EAAE,eAAe,CAAA;IAC9B,aAAa,CAAC,EAAE,YAAY,CAAA;IAC5B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C;;;;2CAIuC;IACvC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC;AAID,eAAO,MAAM,wBAAwB;;;;;EAAsB,CAAA;AAC3D,eAAO,MAAM,kBAAkB,+BAAgB,CAAA;AAC/C,eAAO,MAAM,oBAAoB,6BAAkB,CAAA;AACnD,eAAO,MAAM,iBAAiB,oDAAe,CAAA;AAE7C,OAAO,EAAE,cAAc,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FinancialPage.d.ts","sourceRoot":"","sources":["../src/FinancialPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoD,MAAM,OAAO,CAAA;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG/C,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAC3F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAqB,MAAM,eAAe,CAAA;AAsBzE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAA;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;
|
|
1
|
+
{"version":3,"file":"FinancialPage.d.ts","sourceRoot":"","sources":["../src/FinancialPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoD,MAAM,OAAO,CAAA;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG/C,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAC3F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAqB,MAAM,eAAe,CAAA;AAsBzE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAA;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA6HD,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;IACrE,MAAM,EAAE,uBAAuB,CAAA;IAC/B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAA;IACjC,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAA;CACjC,qBA2OA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FinancialGeneralSettings.d.ts","sourceRoot":"","sources":["../../src/components/FinancialGeneralSettings.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"FinancialGeneralSettings.d.ts","sourceRoot":"","sources":["../../src/components/FinancialGeneralSettings.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAgBzB,wBAAgB,wBAAwB,sBAsBvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AA0JpD,wBAAgB,+BAA+B,IAAI,qBAAqB,
|
|
1
|
+
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AA0JpD,wBAAgB,+BAA+B,IAAI,qBAAqB,CA8tBvE"}
|
package/dist/index.cjs
CHANGED
|
@@ -273,6 +273,8 @@ function QuickTransactionForm({ open, onOpenChange, defaultType = "expense", def
|
|
|
273
273
|
const { currency } = useFinancialConfig();
|
|
274
274
|
const provider = useFinancialProvider();
|
|
275
275
|
const createQuickTransaction = useFinancialStore((s) => s.createQuickTransaction);
|
|
276
|
+
const guardMovements = saas.useLimitGuard("movements_month");
|
|
277
|
+
const can = saas.usePermissionOptional();
|
|
276
278
|
const [type, setType] = React17.useState(defaultType);
|
|
277
279
|
const [amount, setAmount] = React17.useState(0);
|
|
278
280
|
const [amountStr, setAmountStr] = React17.useState("");
|
|
@@ -358,12 +360,14 @@ function QuickTransactionForm({ open, onOpenChange, defaultType = "expense", def
|
|
|
358
360
|
}
|
|
359
361
|
const canSave = amount > 0 && !saving && (type === "transfer" ? !!accountId && !!toAccountId && accountId !== toAccountId : true);
|
|
360
362
|
async function handleSave() {
|
|
363
|
+
if (!can("financial", "create")) return;
|
|
361
364
|
if (!canSave) {
|
|
362
365
|
if (type === "transfer" && accountId && accountId === toAccountId) {
|
|
363
366
|
ui.toast.error(t2("financial.transfer.sameAccount"));
|
|
364
367
|
}
|
|
365
368
|
return;
|
|
366
369
|
}
|
|
370
|
+
if (await guardMovements(type === "transfer" ? 2 : 1) === "blocked") return;
|
|
367
371
|
setSaving(true);
|
|
368
372
|
try {
|
|
369
373
|
await createQuickTransaction({
|
|
@@ -378,6 +382,7 @@ function QuickTransactionForm({ open, onOpenChange, defaultType = "expense", def
|
|
|
378
382
|
recurring: type === "transfer" ? false : recurring,
|
|
379
383
|
receiptUrl: type === "transfer" ? void 0 : receiptUrl
|
|
380
384
|
});
|
|
385
|
+
saas.invalidateLimit("movements_month");
|
|
381
386
|
onOpenChange(false);
|
|
382
387
|
} catch {
|
|
383
388
|
} finally {
|
|
@@ -666,6 +671,23 @@ var STATUS_OPTIONS = [
|
|
|
666
671
|
{ value: "overdue", labelKey: "financial.invoice.statusOverdue", color: "bg-destructive-soft text-destructive-soft-foreground", icon: lucideReact.CircleAlert },
|
|
667
672
|
{ value: "cancelled", labelKey: "financial.invoice.statusCancelled", color: "bg-muted text-muted-foreground", icon: lucideReact.Ban }
|
|
668
673
|
];
|
|
674
|
+
var isoDate = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
675
|
+
function presetRange(preset) {
|
|
676
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
677
|
+
switch (preset) {
|
|
678
|
+
case "thisMonth":
|
|
679
|
+
return { from: isoDate(new Date(now2.getFullYear(), now2.getMonth(), 1)), to: isoDate(new Date(now2.getFullYear(), now2.getMonth() + 1, 0)) };
|
|
680
|
+
case "lastMonth":
|
|
681
|
+
return { from: isoDate(new Date(now2.getFullYear(), now2.getMonth() - 1, 1)), to: isoDate(new Date(now2.getFullYear(), now2.getMonth(), 0)) };
|
|
682
|
+
case "last30": {
|
|
683
|
+
const from = new Date(now2);
|
|
684
|
+
from.setDate(from.getDate() - 29);
|
|
685
|
+
return { from: isoDate(from), to: isoDate(now2) };
|
|
686
|
+
}
|
|
687
|
+
case "thisYear":
|
|
688
|
+
return { from: isoDate(new Date(now2.getFullYear(), 0, 1)), to: isoDate(new Date(now2.getFullYear(), 11, 31)) };
|
|
689
|
+
}
|
|
690
|
+
}
|
|
669
691
|
function StatusBadge({ status }) {
|
|
670
692
|
const t2 = core.useTranslation();
|
|
671
693
|
const opt = STATUS_OPTIONS.find((o) => o.value === status);
|
|
@@ -729,6 +751,8 @@ function useInvoiceColumns(currency) {
|
|
|
729
751
|
}
|
|
730
752
|
function InvoiceListView({ direction, onNew, onEdit }) {
|
|
731
753
|
const t2 = core.useTranslation();
|
|
754
|
+
const can = saas.usePermissionOptional();
|
|
755
|
+
const onNewGated = can("financial", "create") ? onNew : void 0;
|
|
732
756
|
const { currency } = useFinancialConfig();
|
|
733
757
|
const provider = useFinancialProvider();
|
|
734
758
|
const invoices = useFinancialStore((s) => s.invoices);
|
|
@@ -741,6 +765,25 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
741
765
|
const [costCenterId, setCostCenterId] = React17.useState("");
|
|
742
766
|
const [accounts, setAccounts] = React17.useState([]);
|
|
743
767
|
const [costCenters, setCostCenters] = React17.useState([]);
|
|
768
|
+
const [datePreset, setDatePreset] = React17.useState("all");
|
|
769
|
+
const [dateFrom, setDateFrom] = React17.useState("");
|
|
770
|
+
const [dateTo, setDateTo] = React17.useState("");
|
|
771
|
+
const dateRange = React17.useMemo(() => {
|
|
772
|
+
if (!dateFrom && !dateTo) return void 0;
|
|
773
|
+
return { from: dateFrom || "0001-01-01", to: dateTo || "9999-12-31" };
|
|
774
|
+
}, [dateFrom, dateTo]);
|
|
775
|
+
const applyPreset = (value) => {
|
|
776
|
+
setDatePreset(value);
|
|
777
|
+
if (value === "all") {
|
|
778
|
+
setDateFrom("");
|
|
779
|
+
setDateTo("");
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
if (value === "custom") return;
|
|
783
|
+
const range = presetRange(value);
|
|
784
|
+
setDateFrom(range.from);
|
|
785
|
+
setDateTo(range.to);
|
|
786
|
+
};
|
|
744
787
|
React17.useEffect(() => {
|
|
745
788
|
let active = true;
|
|
746
789
|
Promise.all([provider.getChartOfAccounts(), provider.getCostCenters()]).then(([accountRows, costCenterRows]) => {
|
|
@@ -762,9 +805,10 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
762
805
|
status: statusFilter.length > 0 ? statusFilter : void 0,
|
|
763
806
|
search: search || void 0,
|
|
764
807
|
accountId: accountId || void 0,
|
|
765
|
-
costCenterId: costCenterId || void 0
|
|
808
|
+
costCenterId: costCenterId || void 0,
|
|
809
|
+
dateRange
|
|
766
810
|
});
|
|
767
|
-
}, [direction, statusFilter, search, accountId, costCenterId]);
|
|
811
|
+
}, [direction, statusFilter, search, accountId, costCenterId, dateRange]);
|
|
768
812
|
const filtered = invoices.filter((inv) => inv.direction === direction);
|
|
769
813
|
const listTitle = direction === "debit" ? t2("financial.invoice.accountsPayable") : t2("financial.invoice.accountsReceivable");
|
|
770
814
|
const columns = useInvoiceColumns(currency);
|
|
@@ -794,7 +838,67 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
794
838
|
subtitle: t2("financial.invoice.invoices", { count: filtered.length })
|
|
795
839
|
}
|
|
796
840
|
),
|
|
797
|
-
|
|
841
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-end gap-3 rounded-lg border bg-muted/20 px-4 py-3", children: [
|
|
842
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-[180px] flex-col gap-1 text-xs font-medium text-muted-foreground", children: [
|
|
843
|
+
t2("financial.invoice.filterPeriod"),
|
|
844
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
845
|
+
"select",
|
|
846
|
+
{
|
|
847
|
+
className: "h-9 rounded-md border bg-background px-3 text-sm text-foreground",
|
|
848
|
+
value: datePreset,
|
|
849
|
+
onChange: (event) => applyPreset(event.target.value),
|
|
850
|
+
children: [
|
|
851
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: t2("financial.invoice.filterAllPeriods") }),
|
|
852
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "thisMonth", children: t2("financial.invoice.filterThisMonth") }),
|
|
853
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "lastMonth", children: t2("financial.invoice.filterLastMonth") }),
|
|
854
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "last30", children: t2("financial.invoice.filterLast30") }),
|
|
855
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "thisYear", children: t2("financial.invoice.filterThisYear") }),
|
|
856
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "custom", children: t2("financial.invoice.filterCustom") })
|
|
857
|
+
]
|
|
858
|
+
}
|
|
859
|
+
)
|
|
860
|
+
] }),
|
|
861
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-1 text-xs font-medium text-muted-foreground", children: [
|
|
862
|
+
t2("financial.invoice.filterFrom"),
|
|
863
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
864
|
+
"input",
|
|
865
|
+
{
|
|
866
|
+
type: "date",
|
|
867
|
+
className: "h-9 rounded-md border bg-background px-3 text-sm text-foreground",
|
|
868
|
+
value: dateFrom,
|
|
869
|
+
max: dateTo || void 0,
|
|
870
|
+
onChange: (event) => {
|
|
871
|
+
setDateFrom(event.target.value);
|
|
872
|
+
setDatePreset("custom");
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
)
|
|
876
|
+
] }),
|
|
877
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-1 text-xs font-medium text-muted-foreground", children: [
|
|
878
|
+
t2("financial.invoice.filterTo"),
|
|
879
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
880
|
+
"input",
|
|
881
|
+
{
|
|
882
|
+
type: "date",
|
|
883
|
+
className: "h-9 rounded-md border bg-background px-3 text-sm text-foreground",
|
|
884
|
+
value: dateTo,
|
|
885
|
+
min: dateFrom || void 0,
|
|
886
|
+
onChange: (event) => {
|
|
887
|
+
setDateTo(event.target.value);
|
|
888
|
+
setDatePreset("custom");
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
)
|
|
892
|
+
] }),
|
|
893
|
+
(dateFrom || dateTo) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
894
|
+
"button",
|
|
895
|
+
{
|
|
896
|
+
type: "button",
|
|
897
|
+
className: "h-9 rounded-md px-2 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
898
|
+
onClick: () => applyPreset("all"),
|
|
899
|
+
children: t2("financial.invoice.filterClear")
|
|
900
|
+
}
|
|
901
|
+
),
|
|
798
902
|
accounts.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-[220px] flex-col gap-1 text-xs font-medium text-muted-foreground", children: [
|
|
799
903
|
t2("financial.invoice.filterAccount"),
|
|
800
904
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -848,12 +952,12 @@ function InvoiceListView({ direction, onNew, onEdit }) {
|
|
|
848
952
|
activeTag: statusFilter.length === 1 ? statusFilter[0] : void 0,
|
|
849
953
|
onTagChange: (v) => setStatusFilter(v ? [v] : []),
|
|
850
954
|
newLabel: t2("financial.invoice.new"),
|
|
851
|
-
onNew,
|
|
955
|
+
onNew: onNewGated,
|
|
852
956
|
onRowClick: (row) => onEdit?.(row.id),
|
|
853
957
|
emptyIcon: lucideReact.FileText,
|
|
854
958
|
emptyMessage: t2("financial.invoice.noInvoices"),
|
|
855
|
-
emptyActionLabel:
|
|
856
|
-
onEmptyAction:
|
|
959
|
+
emptyActionLabel: onNewGated ? t2("financial.invoice.createFirst") : void 0,
|
|
960
|
+
onEmptyAction: onNewGated
|
|
857
961
|
}
|
|
858
962
|
),
|
|
859
963
|
filtered.length > 0 && !invoicesLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-6 rounded-lg border bg-muted/30 px-5 py-3 text-sm", children: [
|
|
@@ -1193,6 +1297,8 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1193
1297
|
const hasItemLookups = itemTypes.some((tp) => config.entityLookups[tp.value]);
|
|
1194
1298
|
const defaultItemKind = itemTypes.find((tp) => tp.value === "other")?.value ?? itemTypes.find((tp) => !config.entityLookups[tp.value])?.value ?? itemTypes[0]?.value ?? "other";
|
|
1195
1299
|
const createInvoice = useFinancialStore((s) => s.createInvoice);
|
|
1300
|
+
const guardMovements = saas.useLimitGuard("movements_month");
|
|
1301
|
+
const can = saas.usePermissionOptional();
|
|
1196
1302
|
const hasLocations = locations.length > 0;
|
|
1197
1303
|
const isEdit = !!editId;
|
|
1198
1304
|
const [loaded, setLoaded] = React17.useState(!editId);
|
|
@@ -1330,10 +1436,12 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1330
1436
|
setInstallments(inst);
|
|
1331
1437
|
}
|
|
1332
1438
|
async function handleSave() {
|
|
1439
|
+
if (!isEdit && !can("financial", "create")) return;
|
|
1333
1440
|
if (items.length === 0) {
|
|
1334
1441
|
ui.toast.error(t2("common.formIncomplete"));
|
|
1335
1442
|
return;
|
|
1336
1443
|
}
|
|
1444
|
+
if (!isEdit && await guardMovements(Math.max(1, installments.length)) === "blocked") return;
|
|
1337
1445
|
setSaving(true);
|
|
1338
1446
|
try {
|
|
1339
1447
|
const movInputs = installments.map((inst, i) => ({
|
|
@@ -1375,6 +1483,7 @@ function InvoiceFormView({ direction, editId, onSaved }) {
|
|
|
1375
1483
|
items: items.map(({ _id, ...rest }) => rest),
|
|
1376
1484
|
installments: movInputs
|
|
1377
1485
|
});
|
|
1486
|
+
saas.invalidateLimit("movements_month");
|
|
1378
1487
|
onSaved?.(invoice.id);
|
|
1379
1488
|
}
|
|
1380
1489
|
} finally {
|
|
@@ -3516,7 +3625,7 @@ function CardsView() {
|
|
|
3516
3625
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t2("financial.cards.dueOn", { day: String(card.dueDay) }) })
|
|
3517
3626
|
] })
|
|
3518
3627
|
] }),
|
|
3519
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3628
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.PermissionGate, { feature: "financial", action: "create", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3520
3629
|
"button",
|
|
3521
3630
|
{
|
|
3522
3631
|
type: "button",
|
|
@@ -3527,7 +3636,7 @@ function CardsView() {
|
|
|
3527
3636
|
t2("financial.cards.addExpense")
|
|
3528
3637
|
]
|
|
3529
3638
|
}
|
|
3530
|
-
)
|
|
3639
|
+
) })
|
|
3531
3640
|
] })
|
|
3532
3641
|
] }, card.id);
|
|
3533
3642
|
}) })
|
|
@@ -3688,28 +3797,31 @@ function ReconciliationView() {
|
|
|
3688
3797
|
] })
|
|
3689
3798
|
] });
|
|
3690
3799
|
}
|
|
3800
|
+
var FINANCIAL_DEFAULTS = {
|
|
3801
|
+
autoInstallments: true,
|
|
3802
|
+
requireDoc: false,
|
|
3803
|
+
allowPartial: true,
|
|
3804
|
+
requireOpening: true,
|
|
3805
|
+
autoClose: false,
|
|
3806
|
+
overdueAlerts: true,
|
|
3807
|
+
dailySummary: false
|
|
3808
|
+
};
|
|
3691
3809
|
function FinancialGeneralSettings() {
|
|
3692
3810
|
const t2 = core.useTranslation();
|
|
3811
|
+
const s = saas.useTenantPluginSettings("financial", FINANCIAL_DEFAULTS);
|
|
3693
3812
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3694
3813
|
/* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("financial.settings.invoices"), description: t2("financial.settings.invoicesDesc"), children: [
|
|
3695
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.autoInstallments"), description: t2("financial.settings.autoInstallmentsDesc"), checked:
|
|
3696
|
-
|
|
3697
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.
|
|
3698
|
-
} }),
|
|
3699
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.allowPartial"), description: t2("financial.settings.allowPartialDesc"), checked: true, onChange: () => {
|
|
3700
|
-
} })
|
|
3814
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.autoInstallments"), description: t2("financial.settings.autoInstallmentsDesc"), checked: s.get("autoInstallments"), onChange: (v) => s.set("autoInstallments", v) }),
|
|
3815
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.requireDoc"), description: t2("financial.settings.requireDocDesc"), checked: s.get("requireDoc"), onChange: (v) => s.set("requireDoc", v) }),
|
|
3816
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.allowPartial"), description: t2("financial.settings.allowPartialDesc"), checked: s.get("allowPartial"), onChange: (v) => s.set("allowPartial", v) })
|
|
3701
3817
|
] }),
|
|
3702
3818
|
/* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("financial.settings.cashRegister"), description: t2("financial.settings.cashRegisterDesc"), children: [
|
|
3703
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.requireOpening"), description: t2("financial.settings.requireOpeningDesc"), checked:
|
|
3704
|
-
|
|
3705
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.autoClose"), description: t2("financial.settings.autoCloseDesc"), checked: false, onChange: () => {
|
|
3706
|
-
} })
|
|
3819
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.requireOpening"), description: t2("financial.settings.requireOpeningDesc"), checked: s.get("requireOpening"), onChange: (v) => s.set("requireOpening", v) }),
|
|
3820
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.autoClose"), description: t2("financial.settings.autoCloseDesc"), checked: s.get("autoClose"), onChange: (v) => s.set("autoClose", v) })
|
|
3707
3821
|
] }),
|
|
3708
3822
|
/* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("financial.settings.notifications"), description: t2("financial.settings.notificationsDesc"), children: [
|
|
3709
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.overdueAlerts"), description: t2("financial.settings.overdueAlertsDesc"), checked:
|
|
3710
|
-
|
|
3711
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.dailySummary"), description: t2("financial.settings.dailySummaryDesc"), checked: false, onChange: () => {
|
|
3712
|
-
} })
|
|
3823
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.overdueAlerts"), description: t2("financial.settings.overdueAlertsDesc"), checked: s.get("overdueAlerts"), onChange: (v) => s.set("overdueAlerts", v) }),
|
|
3824
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("financial.settings.dailySummary"), description: t2("financial.settings.dailySummaryDesc"), checked: s.get("dailySummary"), onChange: (v) => s.set("dailySummary", v) })
|
|
3713
3825
|
] })
|
|
3714
3826
|
] });
|
|
3715
3827
|
}
|
|
@@ -3850,6 +3962,10 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
3850
3962
|
]
|
|
3851
3963
|
});
|
|
3852
3964
|
}
|
|
3965
|
+
for (const item of items) {
|
|
3966
|
+
const feature = config.navFeatureMap[item.id];
|
|
3967
|
+
if (feature) item.feature = feature;
|
|
3968
|
+
}
|
|
3853
3969
|
return items;
|
|
3854
3970
|
}
|
|
3855
3971
|
function FinancialPage({ config, provider, store, registries }) {
|
|
@@ -3885,9 +4001,11 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3885
4001
|
};
|
|
3886
4002
|
}, [onboardingComplete, provider]);
|
|
3887
4003
|
const t2 = core.useTranslation();
|
|
4004
|
+
const can = saas.usePermissionOptional();
|
|
4005
|
+
const canCreate = can("financial", "create");
|
|
3888
4006
|
const intent = parseIntent(view);
|
|
3889
4007
|
const isSettings = view === "settings";
|
|
3890
|
-
const nav = buildNav(config, view, navigate, t2);
|
|
4008
|
+
const nav = saas.useModuleNavAccess(buildNav(config, view, navigate, t2));
|
|
3891
4009
|
const [quickAddOpen, setQuickAddOpen] = React17.useState(false);
|
|
3892
4010
|
const [quickAddType, setQuickAddType] = React17.useState("expense");
|
|
3893
4011
|
const [quickAddReceipt, setQuickAddReceipt] = React17.useState(false);
|
|
@@ -3901,12 +4019,14 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3901
4019
|
if (!pendingQuickAdd) return;
|
|
3902
4020
|
const req = consumeQuickAdd();
|
|
3903
4021
|
if (!req) return;
|
|
4022
|
+
if (!canCreate) return;
|
|
3904
4023
|
setQuickAddType(req.type);
|
|
3905
4024
|
setQuickAddReceipt(req.receipt);
|
|
3906
4025
|
setQuickAddOpen(true);
|
|
3907
4026
|
}, [pendingQuickAdd]);
|
|
3908
4027
|
const quickActions = React17.useMemo(() => {
|
|
3909
4028
|
if (config.quickAdd) return [];
|
|
4029
|
+
if (!canCreate) return [];
|
|
3910
4030
|
const actions = [];
|
|
3911
4031
|
if (config.modules.payables) {
|
|
3912
4032
|
actions.push({
|
|
@@ -3936,7 +4056,7 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3936
4056
|
});
|
|
3937
4057
|
}
|
|
3938
4058
|
return actions;
|
|
3939
|
-
}, [config.modules, config.quickAdd]);
|
|
4059
|
+
}, [config.modules, config.quickAdd, canCreate, navigate, t2]);
|
|
3940
4060
|
function handleOnboardingComplete() {
|
|
3941
4061
|
setOnboardingComplete(true);
|
|
3942
4062
|
try {
|
|
@@ -3980,6 +4100,11 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3980
4100
|
return /* @__PURE__ */ jsxRuntime.jsx(SummaryView, {});
|
|
3981
4101
|
}
|
|
3982
4102
|
}
|
|
4103
|
+
function renderGatedView() {
|
|
4104
|
+
const feature = config.navFeatureMap[intent.view];
|
|
4105
|
+
if (!feature) return renderView();
|
|
4106
|
+
return /* @__PURE__ */ jsxRuntime.jsx(saas.EntitlementGate, { feature, fallback: /* @__PURE__ */ jsxRuntime.jsx(saas.UpgradePrompt, { feature }), children: renderView() });
|
|
4107
|
+
}
|
|
3983
4108
|
return /* @__PURE__ */ jsxRuntime.jsx(FinancialContextProvider, { config, provider, store, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3984
4109
|
ui.ModulePage,
|
|
3985
4110
|
{
|
|
@@ -3994,7 +4119,8 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
3994
4119
|
// add affordance in B2C mode. The receipt entry point lives inside the
|
|
3995
4120
|
// sheet's "Fotografar ou anexar recibo" attachment step, so no separate
|
|
3996
4121
|
// "Enviar recibo" button is needed here.
|
|
3997
|
-
|
|
4122
|
+
// Gated on the role's `create` action so read-only members don't see it.
|
|
4123
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.PermissionGate, { feature: "financial", action: "create", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3998
4124
|
ui.Button,
|
|
3999
4125
|
{
|
|
4000
4126
|
size: "sm",
|
|
@@ -4005,7 +4131,7 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
4005
4131
|
t2("financial.quickTx.newTransaction")
|
|
4006
4132
|
]
|
|
4007
4133
|
}
|
|
4008
|
-
),
|
|
4134
|
+
) }),
|
|
4009
4135
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4010
4136
|
saas.ModuleActionBar,
|
|
4011
4137
|
{
|
|
@@ -4016,7 +4142,7 @@ function FinancialPage({ config, provider, store, registries }) {
|
|
|
4016
4142
|
)
|
|
4017
4143
|
] }),
|
|
4018
4144
|
children: [
|
|
4019
|
-
|
|
4145
|
+
renderGatedView(),
|
|
4020
4146
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4021
4147
|
QuickTransactionForm,
|
|
4022
4148
|
{
|
|
@@ -5175,7 +5301,10 @@ function createSupabaseFinancialProvider() {
|
|
|
5175
5301
|
}
|
|
5176
5302
|
if (query.contactId) qb = qb.eq("party_id", query.contactId);
|
|
5177
5303
|
if (query.search) qb = qb.ilike("notes", `%${query.search}%`);
|
|
5178
|
-
if (query.dateRange)
|
|
5304
|
+
if (query.dateRange) {
|
|
5305
|
+
const to = query.dateRange.to.length === 10 ? `${query.dateRange.to}T23:59:59.999` : query.dateRange.to;
|
|
5306
|
+
qb = qb.gte("created_at", query.dateRange.from).lte("created_at", to);
|
|
5307
|
+
}
|
|
5179
5308
|
const page = query.page ?? 1;
|
|
5180
5309
|
const pageSize = query.pageSize ?? 50;
|
|
5181
5310
|
qb = qb.range((page - 1) * pageSize, page * pageSize - 1).order("created_at", { ascending: false });
|
|
@@ -6412,6 +6541,16 @@ var en = {
|
|
|
6412
6541
|
"financial.invoice.filterAllAccounts": "All accounts",
|
|
6413
6542
|
"financial.invoice.filterCostCenter": "Cost center",
|
|
6414
6543
|
"financial.invoice.filterAllCostCenters": "All cost centers",
|
|
6544
|
+
"financial.invoice.filterPeriod": "Period",
|
|
6545
|
+
"financial.invoice.filterAllPeriods": "All time",
|
|
6546
|
+
"financial.invoice.filterThisMonth": "This month",
|
|
6547
|
+
"financial.invoice.filterLastMonth": "Last month",
|
|
6548
|
+
"financial.invoice.filterLast30": "Last 30 days",
|
|
6549
|
+
"financial.invoice.filterThisYear": "This year",
|
|
6550
|
+
"financial.invoice.filterCustom": "Custom",
|
|
6551
|
+
"financial.invoice.filterFrom": "From",
|
|
6552
|
+
"financial.invoice.filterTo": "To",
|
|
6553
|
+
"financial.invoice.filterClear": "Clear",
|
|
6415
6554
|
"financial.reconciliation.title": "Reconciliation",
|
|
6416
6555
|
"financial.reconciliation.subtitle": "Match imported bank lines to your movements",
|
|
6417
6556
|
"financial.reconciliation.pending": "{{count}} to reconcile",
|
|
@@ -6895,6 +7034,16 @@ var ptBR = {
|
|
|
6895
7034
|
"financial.invoice.filterAllAccounts": "Todos os planos",
|
|
6896
7035
|
"financial.invoice.filterCostCenter": "Centro de custo",
|
|
6897
7036
|
"financial.invoice.filterAllCostCenters": "Todos os centros",
|
|
7037
|
+
"financial.invoice.filterPeriod": "Per\xEDodo",
|
|
7038
|
+
"financial.invoice.filterAllPeriods": "Todo o per\xEDodo",
|
|
7039
|
+
"financial.invoice.filterThisMonth": "Este m\xEAs",
|
|
7040
|
+
"financial.invoice.filterLastMonth": "M\xEAs passado",
|
|
7041
|
+
"financial.invoice.filterLast30": "\xDAltimos 30 dias",
|
|
7042
|
+
"financial.invoice.filterThisYear": "Este ano",
|
|
7043
|
+
"financial.invoice.filterCustom": "Personalizado",
|
|
7044
|
+
"financial.invoice.filterFrom": "De",
|
|
7045
|
+
"financial.invoice.filterTo": "At\xE9",
|
|
7046
|
+
"financial.invoice.filterClear": "Limpar",
|
|
6898
7047
|
"financial.reconciliation.title": "Concilia\xE7\xE3o",
|
|
6899
7048
|
"financial.reconciliation.subtitle": "Concilie as linhas importadas do banco com seus lan\xE7amentos",
|
|
6900
7049
|
"financial.reconciliation.pending": "{{count}} para conciliar",
|
|
@@ -7364,6 +7513,15 @@ var DEFAULT_LABELS = {
|
|
|
7364
7513
|
cardsReconciliation: "Reconciliation"
|
|
7365
7514
|
};
|
|
7366
7515
|
var DEFAULT_CURRENCY = { code: "BRL", locale: "pt-BR", symbol: "R$" };
|
|
7516
|
+
var FINANCIAL_NAV_FEATURES = {
|
|
7517
|
+
reconciliation: "fin_reconciliation",
|
|
7518
|
+
cards: "fin_cards",
|
|
7519
|
+
commissions: "fin_commissions",
|
|
7520
|
+
statements: "fin_statements",
|
|
7521
|
+
receivables: "fin_receivables",
|
|
7522
|
+
payables: "fin_payables",
|
|
7523
|
+
"cash-registers": "fin_cash_registers"
|
|
7524
|
+
};
|
|
7367
7525
|
var DEFAULT_ITEM_TYPES = [
|
|
7368
7526
|
{ value: "service", label: "Service", icon: "Briefcase" },
|
|
7369
7527
|
{ value: "product", label: "Product", icon: "Package" },
|
|
@@ -7394,7 +7552,8 @@ function resolveConfig(options) {
|
|
|
7394
7552
|
locations: options?.locations ?? [],
|
|
7395
7553
|
entityLookups: options?.entityLookups ?? {},
|
|
7396
7554
|
contactLookup: options?.contactLookup,
|
|
7397
|
-
onBookingClick: options?.onBookingClick
|
|
7555
|
+
onBookingClick: options?.onBookingClick,
|
|
7556
|
+
navFeatureMap: { ...options?.navFeatureMap }
|
|
7398
7557
|
};
|
|
7399
7558
|
}
|
|
7400
7559
|
function createFinancialPlugin(options) {
|
|
@@ -7422,6 +7581,70 @@ function createFinancialPlugin(options) {
|
|
|
7422
7581
|
...config.modules.payables ? [{ id: "financial.payables", label: config.labels.payables, group: config.labels.pageTitle }] : [],
|
|
7423
7582
|
...config.modules.commissions ? [{ id: "financial.commissions", label: config.labels.commissions, group: config.labels.pageTitle }] : []
|
|
7424
7583
|
],
|
|
7584
|
+
// Recurring monthly quota — counts financial movement rows created this
|
|
7585
|
+
// month (invoices write one per installment; quick transactions one, or two
|
|
7586
|
+
// for a transfer).
|
|
7587
|
+
declaredLimits: [
|
|
7588
|
+
{ key: "movements_month", label: "Financial movements this month", table: "plg_financial_movements", period: "month" }
|
|
7589
|
+
],
|
|
7590
|
+
queryEntities: [
|
|
7591
|
+
{
|
|
7592
|
+
key: "financial:invoices",
|
|
7593
|
+
entity: {
|
|
7594
|
+
name: "Invoice",
|
|
7595
|
+
namePlural: "Invoices (receipts REC- / payables PAG-)",
|
|
7596
|
+
icon: "Receipt",
|
|
7597
|
+
permission: { feature: "financial", action: "read" },
|
|
7598
|
+
fields: [
|
|
7599
|
+
{ key: "referenceNumber", label: "Reference (REC-/PAG-)", type: "text", searchable: true },
|
|
7600
|
+
{ key: "flow", label: "Flow (receivable/payable)", type: "text" },
|
|
7601
|
+
{ key: "amount", label: "Amount", type: "number" },
|
|
7602
|
+
{ key: "paid", label: "Paid", type: "number" },
|
|
7603
|
+
{ key: "balance", label: "Balance", type: "number" },
|
|
7604
|
+
{ key: "status", label: "Status", type: "text" },
|
|
7605
|
+
{ key: "createdAt", label: "Created at", type: "text" }
|
|
7606
|
+
],
|
|
7607
|
+
data: {
|
|
7608
|
+
table: "v_invoice_balances",
|
|
7609
|
+
tenantScoped: true,
|
|
7610
|
+
searchColumns: ["reference_number"]
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7613
|
+
},
|
|
7614
|
+
{
|
|
7615
|
+
key: "financial:movements",
|
|
7616
|
+
entity: {
|
|
7617
|
+
name: "Financial movement",
|
|
7618
|
+
namePlural: "Financial movements (installment ledger)",
|
|
7619
|
+
icon: "Banknote",
|
|
7620
|
+
permission: { feature: "financial", action: "read" },
|
|
7621
|
+
fields: [
|
|
7622
|
+
{ key: "direction", label: "Direction", type: "text" },
|
|
7623
|
+
{ key: "movementKind", label: "Kind (bill/payment)", type: "text" },
|
|
7624
|
+
{ key: "amount", label: "Amount", type: "number" },
|
|
7625
|
+
{ key: "paidAmount", label: "Paid amount", type: "number" },
|
|
7626
|
+
{ key: "status", label: "Status", type: "text" },
|
|
7627
|
+
{ key: "dueDate", label: "Due date", type: "text" },
|
|
7628
|
+
{ key: "paymentDate", label: "Payment date", type: "text" },
|
|
7629
|
+
{ key: "installmentNumber", label: "Installment", type: "number" },
|
|
7630
|
+
{ key: "cardInstallments", label: "Card installments", type: "number" },
|
|
7631
|
+
{ key: "createdAt", label: "Created at", type: "text" }
|
|
7632
|
+
],
|
|
7633
|
+
data: {
|
|
7634
|
+
table: "plg_financial_movements",
|
|
7635
|
+
tenantScoped: true
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
],
|
|
7640
|
+
declaredRpcs: [
|
|
7641
|
+
{
|
|
7642
|
+
name: "agent_financial_mark_payment_received",
|
|
7643
|
+
kind: "write",
|
|
7644
|
+
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.",
|
|
7645
|
+
audits: true
|
|
7646
|
+
}
|
|
7647
|
+
],
|
|
7425
7648
|
navigation: [
|
|
7426
7649
|
{
|
|
7427
7650
|
section: options?.navSection ?? "main",
|
|
@@ -7475,6 +7698,7 @@ function createFinancialPlugin(options) {
|
|
|
7475
7698
|
description: "Creates a new invoice/payable for a contact.",
|
|
7476
7699
|
icon: "FileText",
|
|
7477
7700
|
mode: "persist",
|
|
7701
|
+
limitKey: "movements_month",
|
|
7478
7702
|
category: "Finance",
|
|
7479
7703
|
parameters: {
|
|
7480
7704
|
type: "object",
|
|
@@ -7490,6 +7714,33 @@ function createFinancialPlugin(options) {
|
|
|
7490
7714
|
],
|
|
7491
7715
|
permission: { feature: "financial", action: "create" }
|
|
7492
7716
|
},
|
|
7717
|
+
{
|
|
7718
|
+
id: "financial.mark-payment-received",
|
|
7719
|
+
name: "markPaymentReceived",
|
|
7720
|
+
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.',
|
|
7721
|
+
icon: "BadgeDollarSign",
|
|
7722
|
+
mode: "persist",
|
|
7723
|
+
execution: { plane: "server", kind: "rpc", rpc: "agent_financial_mark_payment_received" },
|
|
7724
|
+
category: "Finance",
|
|
7725
|
+
parameters: {
|
|
7726
|
+
type: "object",
|
|
7727
|
+
properties: {
|
|
7728
|
+
booking_id: { type: "string", description: "Appointment id (alternative to order_id)" },
|
|
7729
|
+
order_id: { type: "string", description: "Order id" },
|
|
7730
|
+
method: {
|
|
7731
|
+
type: "string",
|
|
7732
|
+
description: "Payment method",
|
|
7733
|
+
enum: ["cash", "pix", "credit_card", "debit_card", "transfer"]
|
|
7734
|
+
},
|
|
7735
|
+
installments: { type: "number", description: 'Credit-card installments (1-12), e.g. 2 for "cart\xE3o 2x"' }
|
|
7736
|
+
},
|
|
7737
|
+
required: ["method"]
|
|
7738
|
+
},
|
|
7739
|
+
suggestions: [
|
|
7740
|
+
{ label: "Marcar o atendimento como pago no cart\xE3o em 2x" }
|
|
7741
|
+
],
|
|
7742
|
+
permission: { feature: "financial.receivables", action: "update" }
|
|
7743
|
+
},
|
|
7493
7744
|
{
|
|
7494
7745
|
id: "financial.list-payables",
|
|
7495
7746
|
name: "listPayables",
|
|
@@ -7537,6 +7788,7 @@ function createFinancialPlugin(options) {
|
|
|
7537
7788
|
};
|
|
7538
7789
|
}
|
|
7539
7790
|
|
|
7791
|
+
exports.FINANCIAL_NAV_FEATURES = FINANCIAL_NAV_FEATURES;
|
|
7540
7792
|
exports.createFinancialPlugin = createFinancialPlugin;
|
|
7541
7793
|
exports.createMockFinancialProvider = createMockFinancialProvider;
|
|
7542
7794
|
exports.createSafeFinancialProvider = createSafeFinancialProvider;
|