@fayz-ai/plugin-financial 0.9.1 → 0.9.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/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as React18 from 'react';
2
2
  import React18__default, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
3
- import { createSafeDataProvider, registerTranslations, registerMessageDefaults, useTranslation, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
3
+ import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, createSafeDataProvider, registerTranslations, registerMessageDefaults, useTranslation, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
4
4
  import { toast, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, DataTable, PageTransition, ModulePage, Button, Modal, ModalContent, DatePicker, DashboardCanvas, SubpageHeader, Skeleton, Card, FilterChip, Input, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, CurrencyInput, useSaveBar, SearchSelect, ListView, KpiCard, ChartWidget, CardHeader, CardTitle, CardContent, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
5
- import { createPluginContext, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, PermissionGate, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, useLimitGuard, invalidateLimit, PersonLink, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, OrderDetailSkeleton, OrderDetailBlock, OrderDetailView } from '@fayz-ai/admin';
5
+ import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, dedup, useModuleNavigation, usePermissionOptional, useModuleNavAccess, PermissionGate, ModuleActionBar, useTenantPluginSettings, SettingsGroup, ToggleRow, formatCurrency, parseViewId, EntitlementGate, UpgradePrompt, useLimitGuard, invalidateLimit, PersonLink, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, OrderDetailSkeleton, OrderDetailBlock, OrderDetailView } from '@fayz-ai/admin';
6
6
  import { Wallet, Plus, DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, X, CalendarDays, Paperclip, Camera, Tag, Repeat, ChevronDown, FileText, FileCheck2, ExternalLink, Send, Ban, CalendarClock, RefreshCw, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, ArrowRightLeft, Search, Download, Receipt, Clock, ArrowLeftRight, CheckCheck, Calculator, StickyNote, Play, Square, ChevronUp, MoreVertical, CircleCheckBig, CircleDashed, CircleAlert, CircleEllipsis, Pencil, LayoutList, Layers, Building2, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, FileCheck, QrCode, Banknote, ArrowUpRight, ArrowDownRight, AlertTriangle, EyeOff, Eye } from 'lucide-react';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import { createPortal } from 'react-dom';
@@ -3793,7 +3793,7 @@ function buildNav(config, activeView, navigate, t2) {
3793
3793
  }
3794
3794
  return items;
3795
3795
  }
3796
- function FinancialPage({ config, provider, store, registries }) {
3796
+ function FinancialPage({ config, provider, store: store2, registries }) {
3797
3797
  const { view, direction, navigate, previousView, back } = useModuleNavigation("/financial", {
3798
3798
  summary: 0,
3799
3799
  "payables-list": 0,
@@ -3891,10 +3891,10 @@ function FinancialPage({ config, provider, store, registries }) {
3891
3891
  }
3892
3892
  }
3893
3893
  if (!onboardingComplete) {
3894
- return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete: handleOnboardingComplete }) });
3894
+ return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store2, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete: handleOnboardingComplete }) });
3895
3895
  }
3896
3896
  if (isSettings && registries && registries.length > 0) {
3897
- return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store, children: /* @__PURE__ */ jsx(PageTransition, { transitionKey: "settings", direction, children: /* @__PURE__ */ jsx("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx(
3897
+ return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store2, children: /* @__PURE__ */ jsx(PageTransition, { transitionKey: "settings", direction, children: /* @__PURE__ */ jsx("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx(
3898
3898
  PluginSettingsPanel,
3899
3899
  {
3900
3900
  title: t2("financial.settingsPage.title"),
@@ -3935,7 +3935,7 @@ function FinancialPage({ config, provider, store, registries }) {
3935
3935
  if (!feature) return renderView();
3936
3936
  return /* @__PURE__ */ jsx(EntitlementGate, { feature, fallback: /* @__PURE__ */ jsx(UpgradePrompt, { feature }), children: renderView() });
3937
3937
  }
3938
- return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store, children: /* @__PURE__ */ jsxs(
3938
+ return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store2, children: /* @__PURE__ */ jsxs(
3939
3939
  ModulePage,
3940
3940
  {
3941
3941
  title: config.labels.pageTitle,
@@ -4445,7 +4445,7 @@ function createStore(options) {
4445
4445
  };
4446
4446
  }
4447
4447
  function createMockFinancialProvider(options) {
4448
- const store = createStore(options);
4448
+ const store2 = createStore(options);
4449
4449
  const tenantId = "mock-tenant";
4450
4450
  function recalcInvoiceStatus(invoice) {
4451
4451
  if (invoice.paidAmount >= invoice.totalAmount) {
@@ -4453,7 +4453,7 @@ function createMockFinancialProvider(options) {
4453
4453
  } else if (invoice.paidAmount > 0) {
4454
4454
  invoice.status = "partial";
4455
4455
  } else {
4456
- const movements = store.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4456
+ const movements = store2.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4457
4457
  const hasOverdue = movements.some((m) => m.status === "overdue" || m.status === "pending" && m.dueDate < today());
4458
4458
  invoice.status = hasOverdue ? "overdue" : "open";
4459
4459
  }
@@ -4462,16 +4462,16 @@ function createMockFinancialProvider(options) {
4462
4462
  const provider = {
4463
4463
  // --- Invoices ---
4464
4464
  async getInvoices(query) {
4465
- let results = [...store.invoices];
4465
+ let results = [...store2.invoices];
4466
4466
  if (query.direction) results = results.filter((i) => i.direction === query.direction);
4467
4467
  if (query.status) results = results.filter((i) => matchesStatus(i.status, query.status));
4468
4468
  if (query.contactId) results = results.filter((i) => i.contactId === query.contactId);
4469
4469
  if (query.accountId) {
4470
- const invoiceIds = new Set(store.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
4470
+ const invoiceIds = new Set(store2.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
4471
4471
  results = results.filter((invoice) => invoiceIds.has(invoice.id));
4472
4472
  }
4473
4473
  if (query.costCenterId) {
4474
- const invoiceIds = new Set(store.invoiceItems.filter((item) => item.costCenterId === query.costCenterId).map((item) => item.invoiceId));
4474
+ const invoiceIds = new Set(store2.invoiceItems.filter((item) => item.costCenterId === query.costCenterId).map((item) => item.invoiceId));
4475
4475
  results = results.filter((invoice) => invoiceIds.has(invoice.id));
4476
4476
  }
4477
4477
  if (query.dateRange) results = results.filter((i) => matchesDateRange(i.invoiceDate, query.dateRange));
@@ -4485,10 +4485,10 @@ function createMockFinancialProvider(options) {
4485
4485
  return paginate(results, query.page, query.pageSize);
4486
4486
  },
4487
4487
  async getInvoiceById(id) {
4488
- return store.invoices.find((i) => i.id === id) ?? null;
4488
+ return store2.invoices.find((i) => i.id === id) ?? null;
4489
4489
  },
4490
4490
  async getInvoiceItems(invoiceId) {
4491
- return store.invoiceItems.filter((i) => i.invoiceId === invoiceId);
4491
+ return store2.invoiceItems.filter((i) => i.invoiceId === invoiceId);
4492
4492
  },
4493
4493
  async createInvoice(input) {
4494
4494
  const invoiceId = uid();
@@ -4517,7 +4517,7 @@ function createMockFinancialProvider(options) {
4517
4517
  createdAt: now(),
4518
4518
  updatedAt: now()
4519
4519
  };
4520
- store.invoices.push(invoice);
4520
+ store2.invoices.push(invoice);
4521
4521
  for (const itemInput of input.items) {
4522
4522
  const itemTotal = itemInput.quantity * itemInput.unitPrice - (itemInput.discount ?? 0) + (itemInput.surcharge ?? 0);
4523
4523
  const item = {
@@ -4536,7 +4536,7 @@ function createMockFinancialProvider(options) {
4536
4536
  metadata: itemInput.metadata,
4537
4537
  createdAt: now()
4538
4538
  };
4539
- store.invoiceItems.push(item);
4539
+ store2.invoiceItems.push(item);
4540
4540
  }
4541
4541
  for (const movInput of input.installments) {
4542
4542
  const movement = {
@@ -4557,29 +4557,29 @@ function createMockFinancialProvider(options) {
4557
4557
  createdAt: now(),
4558
4558
  updatedAt: now()
4559
4559
  };
4560
- store.movements.push(movement);
4560
+ store2.movements.push(movement);
4561
4561
  }
4562
4562
  return invoice;
4563
4563
  },
4564
4564
  async updateInvoice(id, data) {
4565
- const invoice = store.invoices.find((i) => i.id === id);
4565
+ const invoice = store2.invoices.find((i) => i.id === id);
4566
4566
  if (!invoice) throw new Error(`Invoice ${id} not found`);
4567
4567
  Object.assign(invoice, data, { updatedAt: now() });
4568
4568
  return invoice;
4569
4569
  },
4570
4570
  async cancelInvoice(id) {
4571
- const invoice = store.invoices.find((i) => i.id === id);
4571
+ const invoice = store2.invoices.find((i) => i.id === id);
4572
4572
  if (!invoice) throw new Error(`Invoice ${id} not found`);
4573
4573
  invoice.status = "cancelled";
4574
4574
  invoice.updatedAt = now();
4575
- store.movements.filter((m) => m.invoiceId === id && m.status === "pending").forEach((m) => {
4575
+ store2.movements.filter((m) => m.invoiceId === id && m.status === "pending").forEach((m) => {
4576
4576
  m.status = "cancelled";
4577
4577
  m.updatedAt = now();
4578
4578
  });
4579
4579
  },
4580
4580
  // --- Movements ---
4581
4581
  async getMovements(query) {
4582
- let results = [...store.movements];
4582
+ let results = [...store2.movements];
4583
4583
  if (query.direction) results = results.filter((m) => m.direction === query.direction);
4584
4584
  if (query.status) results = results.filter((m) => matchesStatus(m.status, query.status));
4585
4585
  if (query.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
@@ -4589,9 +4589,9 @@ function createMockFinancialProvider(options) {
4589
4589
  return paginate(results, query.page, query.pageSize);
4590
4590
  },
4591
4591
  async payMovement(input) {
4592
- const bill = store.movements.find((m) => m.id === input.movementId);
4592
+ const bill = store2.movements.find((m) => m.id === input.movementId);
4593
4593
  if (!bill) throw new Error(`Movement ${input.movementId} not found`);
4594
- const method = input.paymentMethodId ? store.paymentMethods.find((pm) => pm.id === input.paymentMethodId) : void 0;
4594
+ const method = input.paymentMethodId ? store2.paymentMethods.find((pm) => pm.id === input.paymentMethodId) : void 0;
4595
4595
  const feePct = bill.direction === "credit" ? method?.discountValue ?? 0 : 0;
4596
4596
  const thisFee = round2(input.amount * (feePct / 100));
4597
4597
  const payment = {
@@ -4617,14 +4617,14 @@ function createMockFinancialProvider(options) {
4617
4617
  createdAt: now(),
4618
4618
  updatedAt: now()
4619
4619
  };
4620
- store.movements.push(payment);
4620
+ store2.movements.push(payment);
4621
4621
  bill.paidAmount = round2(bill.paidAmount + input.amount);
4622
4622
  bill.status = bill.paidAmount >= bill.amount ? "paid" : "partial";
4623
4623
  bill.paymentDate = void 0;
4624
4624
  bill.bankAccountId = void 0;
4625
4625
  bill.updatedAt = now();
4626
4626
  if (input.bankAccountId) {
4627
- const account = store.bankAccounts.find((a) => a.id === input.bankAccountId);
4627
+ const account = store2.bankAccounts.find((a) => a.id === input.bankAccountId);
4628
4628
  if (account) {
4629
4629
  if (bill.direction === "credit") {
4630
4630
  account.currentBalance = round2(account.currentBalance + (input.amount - thisFee));
@@ -4635,9 +4635,9 @@ function createMockFinancialProvider(options) {
4635
4635
  }
4636
4636
  }
4637
4637
  if (bill.invoiceId) {
4638
- const invoice = store.invoices.find((i) => i.id === bill.invoiceId);
4638
+ const invoice = store2.invoices.find((i) => i.id === bill.invoiceId);
4639
4639
  if (invoice) {
4640
- const bills = store.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4640
+ const bills = store2.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
4641
4641
  invoice.paidAmount = bills.reduce((sum, m) => sum + m.paidAmount, 0);
4642
4642
  recalcInvoiceStatus(invoice);
4643
4643
  }
@@ -4645,7 +4645,7 @@ function createMockFinancialProvider(options) {
4645
4645
  return payment;
4646
4646
  },
4647
4647
  async cancelMovement(id) {
4648
- const movement = store.movements.find((m) => m.id === id);
4648
+ const movement = store2.movements.find((m) => m.id === id);
4649
4649
  if (!movement) throw new Error(`Movement ${id} not found`);
4650
4650
  movement.status = "cancelled";
4651
4651
  movement.updatedAt = now();
@@ -4672,22 +4672,22 @@ function createMockFinancialProvider(options) {
4672
4672
  createdAt: now(),
4673
4673
  updatedAt: now()
4674
4674
  };
4675
- store.movements.push({
4675
+ store2.movements.push({
4676
4676
  ...base,
4677
4677
  id: debitMovementId,
4678
4678
  direction: "debit",
4679
4679
  bankAccountId: input.fromAccountId,
4680
4680
  metadata: { transferId, transferRole: "out", counterAccountId: input.toAccountId }
4681
4681
  });
4682
- store.movements.push({
4682
+ store2.movements.push({
4683
4683
  ...base,
4684
4684
  id: creditMovementId,
4685
4685
  direction: "credit",
4686
4686
  bankAccountId: input.toAccountId,
4687
4687
  metadata: { transferId, transferRole: "in", counterAccountId: input.fromAccountId }
4688
4688
  });
4689
- const from = store.bankAccounts.find((a) => a.id === input.fromAccountId);
4690
- const to = store.bankAccounts.find((a) => a.id === input.toAccountId);
4689
+ const from = store2.bankAccounts.find((a) => a.id === input.fromAccountId);
4690
+ const to = store2.bankAccounts.find((a) => a.id === input.toAccountId);
4691
4691
  if (from) {
4692
4692
  from.currentBalance = round2(from.currentBalance - input.amount);
4693
4693
  from.updatedAt = now();
@@ -4700,7 +4700,7 @@ function createMockFinancialProvider(options) {
4700
4700
  },
4701
4701
  // --- Bank Accounts ---
4702
4702
  async getBankAccounts() {
4703
- return store.bankAccounts.filter((a) => a.isActive);
4703
+ return store2.bankAccounts.filter((a) => a.isActive);
4704
4704
  },
4705
4705
  async createBankAccount(data) {
4706
4706
  const account = {
@@ -4721,26 +4721,26 @@ function createMockFinancialProvider(options) {
4721
4721
  createdAt: now(),
4722
4722
  updatedAt: now()
4723
4723
  };
4724
- store.bankAccounts.push(account);
4724
+ store2.bankAccounts.push(account);
4725
4725
  return account;
4726
4726
  },
4727
4727
  async updateBankAccount(id, data) {
4728
- const account = store.bankAccounts.find((a) => a.id === id);
4728
+ const account = store2.bankAccounts.find((a) => a.id === id);
4729
4729
  if (!account) throw new Error(`Bank account ${id} not found`);
4730
4730
  Object.assign(account, data, { updatedAt: now() });
4731
4731
  return account;
4732
4732
  },
4733
4733
  // --- Cash Sessions ---
4734
4734
  async getCashSessions(bankAccountId) {
4735
- let results = [...store.cashSessions];
4735
+ let results = [...store2.cashSessions];
4736
4736
  if (bankAccountId) results = results.filter((s) => s.bankAccountId === bankAccountId);
4737
4737
  return results.sort((a, b) => b.openedAt.localeCompare(a.openedAt));
4738
4738
  },
4739
4739
  async getOpenSession(bankAccountId) {
4740
- return store.cashSessions.find((s) => s.bankAccountId === bankAccountId && s.status === "open") ?? null;
4740
+ return store2.cashSessions.find((s) => s.bankAccountId === bankAccountId && s.status === "open") ?? null;
4741
4741
  },
4742
4742
  async openCashSession(input) {
4743
- const account = store.bankAccounts.find((a) => a.id === input.bankAccountId);
4743
+ const account = store2.bankAccounts.find((a) => a.id === input.bankAccountId);
4744
4744
  const session = {
4745
4745
  id: uid(),
4746
4746
  bankAccountId: input.bankAccountId,
@@ -4754,13 +4754,13 @@ function createMockFinancialProvider(options) {
4754
4754
  tenantId,
4755
4755
  createdAt: now()
4756
4756
  };
4757
- store.cashSessions.push(session);
4757
+ store2.cashSessions.push(session);
4758
4758
  return session;
4759
4759
  },
4760
4760
  async closeCashSession(input) {
4761
- const session = store.cashSessions.find((s) => s.id === input.sessionId);
4761
+ const session = store2.cashSessions.find((s) => s.id === input.sessionId);
4762
4762
  if (!session) throw new Error(`Cash session ${input.sessionId} not found`);
4763
- const sessionMovements = store.movements.filter((m) => m.cashSessionId === session.id && m.status === "paid");
4763
+ const sessionMovements = store2.movements.filter((m) => m.cashSessionId === session.id && m.status === "paid");
4764
4764
  const inflow = sessionMovements.filter((m) => m.direction === "credit").reduce((s, m) => s + m.paidAmount, 0);
4765
4765
  const outflow = sessionMovements.filter((m) => m.direction === "debit").reduce((s, m) => s + m.paidAmount, 0);
4766
4766
  const expectedBalance = session.openingBalance + inflow - outflow;
@@ -4775,9 +4775,9 @@ function createMockFinancialProvider(options) {
4775
4775
  return session;
4776
4776
  },
4777
4777
  async getCashSessionSummary(sessionId) {
4778
- const session = store.cashSessions.find((s) => s.id === sessionId);
4778
+ const session = store2.cashSessions.find((s) => s.id === sessionId);
4779
4779
  if (!session) throw new Error(`Cash session ${sessionId} not found`);
4780
- const sessionMovements = store.movements.filter((m) => m.cashSessionId === sessionId && m.status === "paid");
4780
+ const sessionMovements = store2.movements.filter((m) => m.cashSessionId === sessionId && m.status === "paid");
4781
4781
  return {
4782
4782
  session,
4783
4783
  movementCount: sessionMovements.length,
@@ -4787,21 +4787,21 @@ function createMockFinancialProvider(options) {
4787
4787
  },
4788
4788
  // --- Payment Methods ---
4789
4789
  async getPaymentMethods() {
4790
- return store.paymentMethods.filter((pm) => pm.isActive);
4790
+ return store2.paymentMethods.filter((pm) => pm.isActive);
4791
4791
  },
4792
4792
  async getPaymentMethodTypes() {
4793
- return store.paymentMethodTypes.filter((t2) => t2.isActive);
4793
+ return store2.paymentMethodTypes.filter((t2) => t2.isActive);
4794
4794
  },
4795
4795
  // --- Chart of Accounts ---
4796
4796
  async getChartOfAccounts() {
4797
- return store.chartOfAccounts.filter((n) => n.isActive);
4797
+ return store2.chartOfAccounts.filter((n) => n.isActive);
4798
4798
  },
4799
4799
  async getCostCenters() {
4800
- return store.costCenters.filter((c) => c.isActive);
4800
+ return store2.costCenters.filter((c) => c.isActive);
4801
4801
  },
4802
4802
  // --- Card Transactions ---
4803
4803
  async getCardTransactions(dateRange) {
4804
- let results = [...store.cardTransactions];
4804
+ let results = [...store2.cardTransactions];
4805
4805
  if (dateRange) results = results.filter((t2) => matchesDateRange(t2.expectedDate, dateRange));
4806
4806
  return results.sort((a, b) => a.expectedDate.localeCompare(b.expectedDate));
4807
4807
  },
@@ -4810,15 +4810,15 @@ function createMockFinancialProvider(options) {
4810
4810
  const todayStr = today();
4811
4811
  const isRealized = (m) => m.status !== "cancelled" && !!m.paymentDate;
4812
4812
  const consolidated = !query.bankAccountId;
4813
- const scopeAccounts = consolidated ? store.bankAccounts.filter((a) => a.isActive) : store.bankAccounts.filter((a) => a.id === query.bankAccountId);
4813
+ const scopeAccounts = consolidated ? store2.bankAccounts.filter((a) => a.isActive) : store2.bankAccounts.filter((a) => a.id === query.bankAccountId);
4814
4814
  const accountIds = new Set(scopeAccounts.map((a) => a.id));
4815
- const accountNameById = new Map(store.bankAccounts.map((a) => [a.id, a.name]));
4815
+ const accountNameById = new Map(store2.bankAccounts.map((a) => [a.id, a.name]));
4816
4816
  const initialSum = scopeAccounts.reduce((s, a) => s + a.initialBalance, 0);
4817
4817
  if (!consolidated && accountIds.size === 0) {
4818
4818
  return { entries: [], openingBalance: 0, closingBalance: 0, totalCredits: 0, totalDebits: 0, totalFees: 0, net: 0, accountId: query.bankAccountId };
4819
4819
  }
4820
4820
  const netOf = (m) => m.direction === "credit" ? m.paidAmount - (m.feeAmount ?? 0) : -m.paidAmount;
4821
- const inScope = store.movements.filter((m) => isRealized(m) && (consolidated || m.bankAccountId && accountIds.has(m.bankAccountId)));
4821
+ const inScope = store2.movements.filter((m) => isRealized(m) && (consolidated || m.bankAccountId && accountIds.has(m.bankAccountId)));
4822
4822
  const openingBalance = round2(initialSum + inScope.filter((m) => (m.paymentDate ?? "") < query.dateRange.from).reduce((s, m) => s + netOf(m), 0));
4823
4823
  const rangeMovs = inScope.filter((m) => matchesDateRange(m.paymentDate, query.dateRange)).sort((a, b) => (a.paymentDate ?? "").localeCompare(b.paymentDate ?? "") || a.createdAt.localeCompare(b.createdAt));
4824
4824
  let balance = openingBalance;
@@ -4840,7 +4840,7 @@ function createMockFinancialProvider(options) {
4840
4840
  }
4841
4841
  return {
4842
4842
  movement,
4843
- invoice: movement.invoiceId ? store.invoices.find((i) => i.id === movement.invoiceId) : void 0,
4843
+ invoice: movement.invoiceId ? store2.invoices.find((i) => i.id === movement.invoiceId) : void 0,
4844
4844
  runningBalance: balance,
4845
4845
  gross,
4846
4846
  fee,
@@ -4852,9 +4852,9 @@ function createMockFinancialProvider(options) {
4852
4852
  });
4853
4853
  for (const e of entries) {
4854
4854
  const mid = e.movement.matchedMovementId;
4855
- const mm = mid ? store.movements.find((m) => m.id === mid) : void 0;
4855
+ const mm = mid ? store2.movements.find((m) => m.id === mid) : void 0;
4856
4856
  if (!mm) continue;
4857
- const inv = mm.invoiceId ? store.invoices.find((i) => i.id === mm.invoiceId) : void 0;
4857
+ const inv = mm.invoiceId ? store2.invoices.find((i) => i.id === mm.invoiceId) : void 0;
4858
4858
  e.reconciledWith = {
4859
4859
  movementId: mm.id,
4860
4860
  description: mm.notes ?? void 0,
@@ -4878,11 +4878,11 @@ function createMockFinancialProvider(options) {
4878
4878
  },
4879
4879
  // --- Summary ---
4880
4880
  async getSummary(dateRange) {
4881
- const totalBalance = store.bankAccounts.filter((a) => a.isActive).reduce((sum, a) => sum + a.currentBalance, 0);
4882
- const pendingReceivable = store.movements.filter(
4881
+ const totalBalance = store2.bankAccounts.filter((a) => a.isActive).reduce((sum, a) => sum + a.currentBalance, 0);
4882
+ const pendingReceivable = store2.movements.filter(
4883
4883
  (m) => m.direction === "credit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
4884
4884
  );
4885
- const pendingPayable = store.movements.filter(
4885
+ const pendingPayable = store2.movements.filter(
4886
4886
  (m) => m.direction === "debit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
4887
4887
  );
4888
4888
  const todayStr = today();
@@ -4890,7 +4890,7 @@ function createMockFinancialProvider(options) {
4890
4890
  monthAgo.setDate(monthAgo.getDate() - 30);
4891
4891
  const monthStart = monthAgo.toISOString().slice(0, 10);
4892
4892
  const monthEnd = todayStr;
4893
- const monthMovements = store.movements.filter(
4893
+ const monthMovements = store2.movements.filter(
4894
4894
  (m) => m.movementKind === "payment" && matchesDateRange(m.paymentDate, dateRange ?? { from: monthStart, to: monthEnd })
4895
4895
  );
4896
4896
  const overdueReceivable = pendingReceivable.filter((m) => m.dueDate < todayStr);
@@ -4911,12 +4911,12 @@ function createMockFinancialProvider(options) {
4911
4911
  async importBankTransactions(input) {
4912
4912
  let imported = 0, duplicates = 0;
4913
4913
  for (const line of input.lines) {
4914
- const dup = store.movements.some((m) => m.externalSource === line.externalSource && m.externalId === line.externalId);
4914
+ const dup = store2.movements.some((m) => m.externalSource === line.externalSource && m.externalId === line.externalId);
4915
4915
  if (dup) {
4916
4916
  duplicates++;
4917
4917
  continue;
4918
4918
  }
4919
- store.movements.push({
4919
+ store2.movements.push({
4920
4920
  id: uid(),
4921
4921
  direction: line.direction,
4922
4922
  movementKind: "payment",
@@ -4938,38 +4938,38 @@ function createMockFinancialProvider(options) {
4938
4938
  return { imported, duplicates };
4939
4939
  },
4940
4940
  async getUnreconciled(query) {
4941
- let results = store.movements.filter((m) => m.externalSource && !m.reconciledAt);
4941
+ let results = store2.movements.filter((m) => m.externalSource && !m.reconciledAt);
4942
4942
  if (query?.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
4943
4943
  if (query?.dateRange) results = results.filter((m) => matchesDateRange(m.paymentDate, query.dateRange));
4944
4944
  return results.sort((a, b) => (b.paymentDate ?? "").localeCompare(a.paymentDate ?? ""));
4945
4945
  },
4946
4946
  async suggestReconciliation(bankMovementId) {
4947
- const line = store.movements.find((m) => m.id === bankMovementId);
4947
+ const line = store2.movements.find((m) => m.id === bankMovementId);
4948
4948
  if (!line) return [];
4949
4949
  const amount = line.amount;
4950
4950
  const tol = Math.max(0.01, round2(amount * 0.02));
4951
4951
  const lineDate = line.paymentDate ?? line.dueDate;
4952
- return store.movements.filter((m) => !m.externalSource && !m.reconciledAt && m.direction === line.direction && m.id !== bankMovementId && Math.abs(m.amount - amount) <= tol).map((m) => {
4952
+ return store2.movements.filter((m) => !m.externalSource && !m.reconciledAt && m.direction === line.direction && m.id !== bankMovementId && Math.abs(m.amount - amount) <= tol).map((m) => {
4953
4953
  const amtScore = amount > 0 ? Math.max(0, 1 - Math.abs(m.amount - amount) / amount) : 0;
4954
4954
  const refDate = m.dueDate ?? m.paymentDate;
4955
4955
  const days = lineDate && refDate ? Math.abs((new Date(lineDate).getTime() - new Date(refDate).getTime()) / 864e5) : 30;
4956
4956
  const dateScore = Math.max(0, 1 - days / 30);
4957
4957
  return {
4958
4958
  movement: m,
4959
- invoice: m.invoiceId ? store.invoices.find((i) => i.id === m.invoiceId) : void 0,
4959
+ invoice: m.invoiceId ? store2.invoices.find((i) => i.id === m.invoiceId) : void 0,
4960
4960
  score: round2(0.7 * amtScore + 0.3 * dateScore)
4961
4961
  };
4962
4962
  }).sort((a, b) => b.score - a.score).slice(0, 25);
4963
4963
  },
4964
4964
  async reconcileMovement(input) {
4965
- const line = store.movements.find((m) => m.id === input.bankMovementId);
4965
+ const line = store2.movements.find((m) => m.id === input.bankMovementId);
4966
4966
  if (line) {
4967
4967
  line.reconciledAt = now();
4968
4968
  line.matchedMovementId = input.matchedMovementId;
4969
4969
  line.updatedAt = now();
4970
4970
  }
4971
4971
  if (input.matchedMovementId) {
4972
- const internal = store.movements.find((m) => m.id === input.matchedMovementId);
4972
+ const internal = store2.movements.find((m) => m.id === input.matchedMovementId);
4973
4973
  if (internal) {
4974
4974
  internal.reconciledAt = now();
4975
4975
  internal.updatedAt = now();
@@ -4977,14 +4977,14 @@ function createMockFinancialProvider(options) {
4977
4977
  }
4978
4978
  },
4979
4979
  async unreconcileMovement(bankMovementId) {
4980
- const line = store.movements.find((m) => m.id === bankMovementId);
4980
+ const line = store2.movements.find((m) => m.id === bankMovementId);
4981
4981
  if (!line) return;
4982
4982
  const matchedId = line.matchedMovementId;
4983
4983
  line.reconciledAt = void 0;
4984
4984
  line.matchedMovementId = void 0;
4985
4985
  line.updatedAt = now();
4986
4986
  if (matchedId) {
4987
- const internal = store.movements.find((m) => m.id === matchedId);
4987
+ const internal = store2.movements.find((m) => m.id === matchedId);
4988
4988
  if (internal) {
4989
4989
  internal.reconciledAt = void 0;
4990
4990
  internal.updatedAt = now();
@@ -4993,7 +4993,7 @@ function createMockFinancialProvider(options) {
4993
4993
  },
4994
4994
  // --- Notas fiscais (NF-e / NFC-e / NFS-e) ---
4995
4995
  async getNotas(query) {
4996
- let list = [...store.notas].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
4996
+ let list = [...store2.notas].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
4997
4997
  if (query.status) list = list.filter((n) => matchesStatus(n.status, query.status));
4998
4998
  if (query.model) list = list.filter((n) => n.model === query.model);
4999
4999
  if (query.orderId) list = list.filter((n) => n.orderId === query.orderId);
@@ -5004,7 +5004,7 @@ function createMockFinancialProvider(options) {
5004
5004
  return paginate(list, query.page, query.pageSize);
5005
5005
  },
5006
5006
  async getNotaById(id) {
5007
- return store.notas.find((n) => n.id === id) ?? null;
5007
+ return store2.notas.find((n) => n.id === id) ?? null;
5008
5008
  },
5009
5009
  async createNota(input) {
5010
5010
  const nota = {
@@ -5022,19 +5022,19 @@ function createMockFinancialProvider(options) {
5022
5022
  createdAt: now(),
5023
5023
  updatedAt: now()
5024
5024
  };
5025
- store.notas.unshift(nota);
5025
+ store2.notas.unshift(nota);
5026
5026
  return nota;
5027
5027
  },
5028
5028
  async updateNota(id, patch) {
5029
- const nota = store.notas.find((n) => n.id === id);
5029
+ const nota = store2.notas.find((n) => n.id === id);
5030
5030
  if (!nota) throw new Error("Nota fiscal not found");
5031
5031
  Object.assign(nota, patch, { updatedAt: now() });
5032
5032
  return nota;
5033
5033
  },
5034
5034
  async issueNota(id) {
5035
- const nota = store.notas.find((n) => n.id === id);
5035
+ const nota = store2.notas.find((n) => n.id === id);
5036
5036
  if (!nota) throw new Error("Nota fiscal not found");
5037
- const issuedCount = store.notas.filter((n) => n.status === "issued").length;
5037
+ const issuedCount = store2.notas.filter((n) => n.status === "issued").length;
5038
5038
  nota.status = "issued";
5039
5039
  nota.number = String(issuedCount + 1).padStart(6, "0");
5040
5040
  nota.series = nota.series ?? "1";
@@ -5044,7 +5044,7 @@ function createMockFinancialProvider(options) {
5044
5044
  return nota;
5045
5045
  },
5046
5046
  async cancelNota(id, reason) {
5047
- const nota = store.notas.find((n) => n.id === id);
5047
+ const nota = store2.notas.find((n) => n.id === id);
5048
5048
  if (!nota) throw new Error("Nota fiscal not found");
5049
5049
  nota.status = "cancelled";
5050
5050
  nota.errorMessage = reason;
@@ -7507,6 +7507,156 @@ var financialLocales = {
7507
7507
  en,
7508
7508
  "pt-BR": ptBR
7509
7509
  };
7510
+ var STRIPE_CONNECTOR_ID = "stripe";
7511
+ var STRIPE_SETTINGS = [
7512
+ {
7513
+ key: "paymentMethods",
7514
+ label: "Formas de pagamento",
7515
+ description: "O que o cliente pode escolher para pagar a fatura.",
7516
+ type: "multi-select",
7517
+ options: [
7518
+ { value: "card", label: "Cart\xE3o de cr\xE9dito" },
7519
+ { value: "pix", label: "Pix" },
7520
+ { value: "boleto", label: "Boleto" }
7521
+ ],
7522
+ requiredToOperate: true
7523
+ },
7524
+ {
7525
+ key: "daysUntilDue",
7526
+ label: "Prazo de vencimento",
7527
+ description: "Dias entre a emiss\xE3o e o vencimento da fatura.",
7528
+ type: "text",
7529
+ placeholder: "7",
7530
+ pattern: "\\d{1,3}",
7531
+ patternHint: "S\xF3 n\xFAmeros, at\xE9 tr\xEAs d\xEDgitos.",
7532
+ defaultValue: "7"
7533
+ },
7534
+ {
7535
+ // The difference between "a fatura foi emitida" and "o cliente recebeu a
7536
+ // cobrança" is a decision, not a default: finalizing at Stripe sends the
7537
+ // e-mail, and a tenant that reviews before sending needs the draft.
7538
+ key: "finalizeAutomatically",
7539
+ label: "Enviar a cobran\xE7a automaticamente",
7540
+ description: "Desligado, a fatura fica como rascunho na Stripe at\xE9 voc\xEA enviar.",
7541
+ type: "toggle",
7542
+ defaultValue: true
7543
+ }
7544
+ ];
7545
+ var store = createConnectionStore(STRIPE_CONNECTOR_ID, {
7546
+ settings: STRIPE_SETTINGS
7547
+ });
7548
+ var METHOD_LABELS = { card: "Cart\xE3o", pix: "Pix", boleto: "Boleto" };
7549
+ function methodsDetail(value) {
7550
+ const list = Array.isArray(value) ? value : [];
7551
+ const labels = list.map((v) => METHOD_LABELS[String(v)] ?? String(v)).filter(Boolean);
7552
+ return labels.length > 0 ? labels.join(", ") : void 0;
7553
+ }
7554
+ var stripeConnectorDef = {
7555
+ id: STRIPE_CONNECTOR_ID,
7556
+ hostPluginId: "financial",
7557
+ name: "Stripe",
7558
+ description: "Emita as faturas do Financeiro na Stripe e receba a baixa quando o cliente pagar.",
7559
+ icon: "CreditCard",
7560
+ category: "payments",
7561
+ authKind: "oauth",
7562
+ // Issuing the invoice is an effect that files a run; the settlement arrives by
7563
+ // webhook. Neither happens while a buyer waits, so nothing here is `request`.
7564
+ planes: ["sync"],
7565
+ direction: "outbound",
7566
+ docsUrl: "https://docs.stripe.com/invoicing",
7567
+ // Declared here and not as the legacy `entities`/`subscribesTo` pair: those are
7568
+ // mirrors this list already derives (connectorEntities/connectorSubscribesTo),
7569
+ // and two spellings of the same claim is one that can disagree with itself.
7570
+ //
7571
+ // `invoice_due_soon` and `invoice_overdue` are deliberately absent. Stripe has
7572
+ // its own dunning and neither fact changes anything we would send it; asking to
7573
+ // hear them would be asking for reach this connector does not use.
7574
+ permissions: [
7575
+ {
7576
+ kind: "data",
7577
+ resource: "invoice",
7578
+ actions: ["read", "write"],
7579
+ reason: "Ler a fatura a receber para emiti-la na Stripe, e gravar de volta o n\xFAmero da fatura e o link de pagamento. Nome do cliente e itens v\xE3o na cobran\xE7a.",
7580
+ sensitive: true
7581
+ },
7582
+ {
7583
+ kind: "data",
7584
+ resource: "payment",
7585
+ actions: ["write"],
7586
+ reason: "Lan\xE7ar a baixa no receb\xEDvel quando a Stripe confirmar que o cliente pagou.",
7587
+ sensitive: true
7588
+ },
7589
+ {
7590
+ kind: "event",
7591
+ resource: "financial.invoice_created",
7592
+ actions: ["listen"],
7593
+ reason: "Saber que uma fatura foi emitida, para criar a cobran\xE7a na Stripe."
7594
+ },
7595
+ {
7596
+ kind: "event",
7597
+ resource: "financial.invoice_cancelled",
7598
+ actions: ["listen"],
7599
+ reason: "Saber que uma fatura foi cancelada, para anular a cobran\xE7a na Stripe."
7600
+ },
7601
+ {
7602
+ // The connector is what makes this fact appear, so the tenant should agree
7603
+ // to it here: the app's own automations (recibo, mensagem) react to it.
7604
+ kind: "event",
7605
+ resource: "financial.invoice_paid",
7606
+ actions: ["emit"],
7607
+ reason: "Anunciar dentro do app que a fatura foi paga, para o que depende disso acontecer."
7608
+ }
7609
+ ],
7610
+ // Consent is the whole form: an OAuth connector declares no fields, and the
7611
+ // grant never reaches this code.
7612
+ fields: [],
7613
+ settings: STRIPE_SETTINGS,
7614
+ invokedAt: [
7615
+ {
7616
+ key: "financial.invoice.issue",
7617
+ label: "Uma fatura a receber \xE9 emitida na Stripe com o link de pagamento",
7618
+ plane: "sync",
7619
+ trigger: "manual"
7620
+ },
7621
+ {
7622
+ key: "financial.payment.settle",
7623
+ label: "A Stripe avisa que o cliente pagou e a baixa \xE9 lan\xE7ada no receb\xEDvel",
7624
+ plane: "sync",
7625
+ trigger: "webhook"
7626
+ }
7627
+ ],
7628
+ ...connectionStatus(
7629
+ STRIPE_CONNECTOR_ID,
7630
+ (connection) => ({
7631
+ connected: !!connection?.active,
7632
+ detail: methodsDetail(connection?.settings?.paymentMethods),
7633
+ plane: "sync",
7634
+ ...settingsStatus(STRIPE_SETTINGS, connection)
7635
+ }),
7636
+ { store: () => store }
7637
+ ),
7638
+ ...connectionSettings(store),
7639
+ /**
7640
+ * The whole connect step, through the platform broker (FAY-1390).
7641
+ *
7642
+ * It used to call an edge function's `oauth_start` — a function that was
7643
+ * never written, so pressing Conectar reached a URL that did not exist and
7644
+ * the browser reported it as a CORS failure. The broker replaces it
7645
+ * end-to-end: consent starts at the platform, the callback lands there, and
7646
+ * the grant is stored in the platform vault.
7647
+ *
7648
+ * `saveConnection` is deliberately absent, like every other OAuth connector
7649
+ * here: `active = true` is written by the redirect route AFTER the grant.
7650
+ * Nothing in a browser is in a position to know an account was linked.
7651
+ */
7652
+ startOAuth: fayzConnectorOAuth(STRIPE_CONNECTOR_ID),
7653
+ // Local only. Handing the grant back to Stripe is the platform's job — it
7654
+ // holds the client pair and the connected account id, and its deauthorize is
7655
+ // not the same call every other provider takes.
7656
+ disconnect: async () => {
7657
+ await store.disconnect();
7658
+ }
7659
+ };
7510
7660
  var STATUS_STYLE = {
7511
7661
  paid: "bg-success/15 text-success",
7512
7662
  partial: "bg-warning/15 text-warning",
@@ -7665,6 +7815,7 @@ function resolveConfig(options) {
7665
7815
  quickAdd: options?.quickAdd === true,
7666
7816
  labels: { ...DEFAULT_LABELS, ...options?.labels },
7667
7817
  currency: { ...DEFAULT_CURRENCY, ...options?.currency },
7818
+ moduleNav: options?.moduleNav,
7668
7819
  itemTypes: options?.itemTypes ?? DEFAULT_ITEM_TYPES,
7669
7820
  enableServiceExecution: options?.enableServiceExecution ?? false,
7670
7821
  contactEntity: {
@@ -7683,12 +7834,12 @@ function createFinancialPlugin(options) {
7683
7834
  registerTranslations(financialLocales);
7684
7835
  registerMessageDefaults(buildFinancialMessageDefaults());
7685
7836
  const provider = options?.dataProvider ?? createSafeFinancialProvider();
7686
- const store = createFinancialStore(provider);
7687
- const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store });
7837
+ const store2 = createFinancialStore(provider);
7838
+ const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store: store2 });
7688
7839
  const registries = [...financialRegistries, ...options?.settingsRegistries ?? []];
7689
7840
  const PersonStatementBound = (props) => React18__default.createElement(PersonStatementWidget, { person: props.item, provider, config });
7690
7841
  PersonStatementBound.displayName = "FinancialPersonStatement";
7691
- const PageComponent = () => React18__default.createElement(FinancialPage, { config, provider, store, registries });
7842
+ const PageComponent = () => React18__default.createElement(FinancialPage, { config, provider, store: store2, registries });
7692
7843
  return {
7693
7844
  id: "financial",
7694
7845
  name: config.labels.pageTitle,
@@ -7909,6 +8060,15 @@ function createFinancialPlugin(options) {
7909
8060
  permission: { feature: "financial", action: "read" }
7910
8061
  }
7911
8062
  ],
8063
+ // Stripe is the first connector this module implements rather than merely
8064
+ // shelves — it stays in `integrations` too, because the union is what the
8065
+ // catalogue row joins on.
8066
+ connectors: [stripeConnectorDef],
8067
+ // TecnoSpeed is live already — the openbanking addon's `plugbank`
8068
+ // connector IS this slug. AbacatePay and Pagar.me were named as what
8069
+ // having Financeiro should put on the shelf; Mercado Pago, Bling and Stone
8070
+ // come from the Fayz plugin page.
8071
+ integrations: ["tecnospeed", "abacatepay", "pagarme", "stripe", "mercadopago", "bling", "stone"],
7912
8072
  locales: financialLocales
7913
8073
  };
7914
8074
  }