@fayz-ai/plugin-financial 0.25.3 → 0.25.4

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
@@ -704,6 +704,85 @@ var OPEN_DRAFT = {
704
704
  required: ["amount"]
705
705
  }
706
706
  };
707
+ var RECEIVABLE_RECEIPT_INTRO = {
708
+ role: "finance",
709
+ persona: "Leitor de notas e comprovantes",
710
+ pitch: "Envie a nota que voc\xEA emitiu e eu preencho a conta a receber.",
711
+ description: "Leio o que est\xE1 no documento \u2014 cliente, valor, data, n\xFAmero e chave da NF-e \u2014, digo o que entendi e abro a conta a receber j\xE1 preenchida para voc\xEA conferir. N\xE3o gravo nada sozinho: quem confirma \xE9 voc\xEA.",
712
+ suggestions: [
713
+ { label: "O que voc\xEA consegue ler de uma nota fiscal?" },
714
+ { label: "E se a foto estiver torta ou escura?" }
715
+ ]
716
+ };
717
+ var RECEIVABLE_RECEIPT_INSTRUCTIONS = [
718
+ "The person will send a PHOTO or PDF of a document for money they are going to RECEIVE: usually an NF-e/NFS-e",
719
+ "they ISSUED to a customer, or a receipt they gave. Read it and extract only what is written on it.",
720
+ "End the turn by CALLING `openReceivableDraft` with what you read. Describing the document in text opens nothing.",
721
+ "Do not announce that you will open it \u2014 open it. Talk to the person in Brazilian Portuguese.",
722
+ "THE COUNTERPARTY IS THE CUSTOMER. On an NF-e the person issued, the EMITENTE (issuer) is the person's own",
723
+ "company: never send it as the customer. Send the DESTINAT\xC1RIO / TOMADOR (recipient) name in `customer`.",
724
+ 'On a receipt, the customer is the one who PAID ("Recebi de ..."). Without a clear customer name, omit `customer`.',
725
+ 'Copy `amount` exactly as printed ("1.234,56"), never converted \u2014 the app parses the punctuation.',
726
+ "If the paper spells the amount out in words, copy it into `amountInWords`.",
727
+ "If it is an NF-e, copy the note NUMBER into `fiscalNumber` and the 44-digit ACCESS KEY (chave de acesso) into",
728
+ "`accessKey`, digits only. Omit either one when the paper does not show it.",
729
+ "Use `date` for the issue date, `description` for what was sold, and `notes` for anything else written that has",
730
+ "no field of its own. Do not invent fields the paper does not answer.",
731
+ "The only reason NOT to call is an illegible photo or an ambiguous total: then ask only that."
732
+ ].join(" ");
733
+ var OPEN_RECEIVABLE_DRAFT = {
734
+ id: "financial.receipt.open-receivable-draft",
735
+ name: "openReceivableDraft",
736
+ description: 'Opens the "new receivable" form pre-filled with what was read from an issued invoice (NF-e) or receipt. Call this as soon as you can read an amount. Writes nothing: the person reviews the form and saves it. Send ONLY fields the document actually states \u2014 omit anything you would be guessing at.',
737
+ icon: "ReceiptText",
738
+ mode: "read",
739
+ execution: { plane: "client" },
740
+ category: "Finance",
741
+ role: "finance",
742
+ parameters: {
743
+ type: "object",
744
+ properties: {
745
+ customer: {
746
+ type: "string",
747
+ description: `Who OWES / PAID the money: the DESTINAT\xC1RIO or TOMADOR on an NF-e, or "Recebi de ..." on a receipt. NEVER the EMITENTE of an NF-e \u2014 that is the person's own company. Omit when not named.`
748
+ },
749
+ amount: {
750
+ type: "string",
751
+ description: 'The total COPIED as printed, punctuation included ("50,00", "1.234,56"). Do not convert it.'
752
+ },
753
+ amountInWords: {
754
+ type: "string",
755
+ description: "The amount spelled out in words, if the paper writes it. Copy the words as they are."
756
+ },
757
+ date: {
758
+ type: "string",
759
+ description: 'The issue date as written ("20/01/2026", "2026-01-20"). Omit if absent.'
760
+ },
761
+ description: {
762
+ type: "string",
763
+ description: "What was sold or provided, in a few words. It becomes the item name on the receivable."
764
+ },
765
+ fiscalNumber: {
766
+ type: "string",
767
+ description: 'The NF-e / NFS-e number (e.g. "1234" or "000.001.234"), when the document is a fiscal note.'
768
+ },
769
+ accessKey: {
770
+ type: "string",
771
+ description: 'The NF-e 44-digit access key ("chave de acesso"), when printed. Digits only.'
772
+ },
773
+ notes: {
774
+ type: "string",
775
+ description: "Everything ELSE written that has no field above. Do not repeat the amount, date, description, number or key."
776
+ }
777
+ },
778
+ required: ["amount"]
779
+ }
780
+ };
781
+ function parseAccessKey(raw) {
782
+ if (typeof raw !== "string") return void 0;
783
+ const digits2 = raw.replace(/\D/g, "");
784
+ return digits2.length === 44 ? digits2 : void 0;
785
+ }
707
786
  function parseReceiptAmount(raw) {
708
787
  if (typeof raw === "number") return Number.isFinite(raw) && raw > 0 ? raw : null;
709
788
  if (typeof raw !== "string") return null;
@@ -730,22 +809,23 @@ function parseReceiptDate(raw) {
730
809
  if (!br) return void 0;
731
810
  const [, d, m, y] = br;
732
811
  const year = y.length === 2 ? `20${y}` : y;
733
- const iso = `${year}-${m.padStart(2, "0")}-${d.padStart(2, "0")}`;
734
- return isRealDate(iso) ? iso : void 0;
812
+ const iso2 = `${year}-${m.padStart(2, "0")}-${d.padStart(2, "0")}`;
813
+ return isRealDate(iso2) ? iso2 : void 0;
735
814
  }
736
815
  function findDateIn(text2) {
737
816
  if (typeof text2 !== "string") return void 0;
738
817
  return text2.match(/\b\d{1,2}[/.-]\d{1,2}[/.-](?:\d{4}|\d{2})\b/)?.[0];
739
818
  }
740
- function isRealDate(iso) {
741
- const [y, m, d] = iso.split("-").map(Number);
819
+ function isRealDate(iso2) {
820
+ const [y, m, d] = iso2.split("-").map(Number);
742
821
  const date = new Date(Date.UTC(y, m - 1, d));
743
822
  return date.getUTCFullYear() === y && date.getUTCMonth() === m - 1 && date.getUTCDate() === d;
744
823
  }
745
824
  var pending = null;
746
- function consumePayableDraft() {
825
+ function consumePayableDraft(direction) {
747
826
  const draft = pending;
748
827
  pending = null;
828
+ if (draft && direction && (draft.direction ?? "debit") !== direction) return null;
749
829
  return draft;
750
830
  }
751
831
  var lastPhoto;
@@ -755,7 +835,74 @@ function rememberReceiptPhoto(dataUrl) {
755
835
  function lastReceiptPhoto() {
756
836
  return lastPhoto;
757
837
  }
758
- function createReceiptTools({ onDraft, getReceiptUrl = lastReceiptPhoto }) {
838
+ function readAmount(args) {
839
+ if (typeof args.amount !== "string") {
840
+ return {
841
+ ok: false,
842
+ error: "amount_must_be_literal",
843
+ message: 'Send `amount` as the TEXT printed on the receipt, punctuation included ("50,00", "1.234,56") \u2014 never as a number. A decimal comma is a thousands separator in English, so converting it yourself turns fifty into five thousand. Read the paper again and resend.'
844
+ };
845
+ }
846
+ const figures = parseReceiptAmount(args.amount);
847
+ const spelled = parseAmountInWords(args.amountInWords);
848
+ const amount = spelled ?? figures;
849
+ if (amount === null) {
850
+ return {
851
+ ok: false,
852
+ error: "invalid_amount",
853
+ message: "That is not a readable amount. If the total is illegible on the photo, ask the person."
854
+ };
855
+ }
856
+ return { ok: true, amount, spelled, figures };
857
+ }
858
+ function trimmed(value) {
859
+ const s = typeof value === "string" ? value.trim() : "";
860
+ return s || void 0;
861
+ }
862
+ function createReceivableReceiptTools({ onDraft, getReceiptUrl }) {
863
+ return [
864
+ {
865
+ tool: OPEN_RECEIVABLE_DRAFT,
866
+ // The payable path's `rememberedDocument` is NOT read here: it is a
867
+ // supplier-side reading, and seeding a receivable from it would bring the
868
+ // issuer back as the counterparty.
869
+ execute: async (args) => {
870
+ const read = readAmount(args);
871
+ if (!read.ok) return read;
872
+ const accessKey = parseAccessKey(args.accessKey);
873
+ const notes = [
874
+ trimmed(args.notes),
875
+ accessKey ? `Chave de acesso NF-e: ${accessKey}` : void 0
876
+ ].filter(Boolean).join(" \xB7 ") || void 0;
877
+ pending = {
878
+ direction: "credit",
879
+ amount: read.amount,
880
+ customer: trimmed(args.customer),
881
+ description: trimmed(args.description),
882
+ fiscalNumber: trimmed(args.fiscalNumber),
883
+ accessKey,
884
+ notes,
885
+ date: parseReceiptDate(args.date) ?? parseReceiptDate(findDateIn(args.notes)),
886
+ receiptUrl: getReceiptUrl()
887
+ };
888
+ onDraft();
889
+ revealPage();
890
+ return {
891
+ ok: true,
892
+ opened: true,
893
+ ...read.spelled !== null && read.figures !== null && Math.abs(read.spelled - read.figures) > 5e-3 ? {
894
+ amountFromWords: read.spelled,
895
+ amountFromFigures: read.figures,
896
+ note: `The figures looked like ${read.figures} and the words say ${read.spelled}. The words were used \u2014 tell the person.`
897
+ } : {},
898
+ filled: Object.entries(pending).filter(([, v]) => v !== void 0 && v !== "").map(([k]) => k)
899
+ };
900
+ }
901
+ }
902
+ ];
903
+ }
904
+ function createReceiptTools({ onDraft, getReceiptUrl = lastReceiptPhoto, direction = "payable" }) {
905
+ if (direction === "receivable") return createReceivableReceiptTools({ onDraft, getReceiptUrl });
759
906
  return [
760
907
  {
761
908
  tool: OPEN_DRAFT,
@@ -837,7 +984,7 @@ function createReceiptTools({ onDraft, getReceiptUrl = lastReceiptPhoto }) {
837
984
  }
838
985
  function startReceiptCapture(labels) {
839
986
  rememberReceiptPhoto(void 0);
840
- askAI({ hint: labels.ask, intro: RECEIPT_INTRO });
987
+ askAI({ hint: labels.ask, intro: labels.direction === "receivable" ? RECEIVABLE_RECEIPT_INTRO : RECEIPT_INTRO });
841
988
  requestChatUpload({
842
989
  label: labels.uploadLabel,
843
990
  description: labels.uploadHint,
@@ -854,10 +1001,26 @@ function startReceiptCapture(labels) {
854
1001
  }
855
1002
  });
856
1003
  }
857
- function ReceiptAgentSurface({ onOpenDraft }) {
1004
+
1005
+ // src/lib/list-window.ts
1006
+ function windowReachesToday(from, to, now = /* @__PURE__ */ new Date()) {
1007
+ const today4 = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
1008
+ return from <= today4 && today4 <= to;
1009
+ }
1010
+ function ReceiptAgentSurface({ onOpenDraft, direction = "payable" }) {
858
1011
  const live = React29.useRef(onOpenDraft);
859
1012
  live.current = onOpenDraft;
860
- const contribution = React29.useMemo(() => ({
1013
+ const contribution = React29.useMemo(() => direction === "receivable" ? {
1014
+ id: "financial.receivables.receipt",
1015
+ pluginId: "financial",
1016
+ role: "finance",
1017
+ instructions: RECEIVABLE_RECEIPT_INSTRUCTIONS,
1018
+ describe: () => ({
1019
+ title: "Lista \u2014 Contas a receber",
1020
+ state: { screen: "/financial/receivables", canOpenDraft: true }
1021
+ }),
1022
+ tools: createReceiptTools({ onDraft: () => live.current(), direction })
1023
+ } : {
861
1024
  id: "financial.payables.receipt",
862
1025
  pluginId: "financial",
863
1026
  role: "finance",
@@ -867,7 +1030,7 @@ function ReceiptAgentSurface({ onOpenDraft }) {
867
1030
  state: { screen: "/financial/payables", canOpenDraft: true }
868
1031
  }),
869
1032
  tools: createReceiptTools({ onDraft: () => live.current() })
870
- }), []);
1033
+ }, [direction]);
871
1034
  useAgentSurface(contribution);
872
1035
  return null;
873
1036
  }
@@ -1536,8 +1699,8 @@ var DESTINATIONS = [
1536
1699
  { value: "perda", label: "Baixar como perda" },
1537
1700
  { value: "manter", label: "Manter no estoque" }
1538
1701
  ];
1539
- function day(iso) {
1540
- const [y, m, d] = iso.slice(0, 10).split("-");
1702
+ function day(iso2) {
1703
+ const [y, m, d] = iso2.slice(0, 10).split("-");
1541
1704
  return `${d}/${m}/${y}`;
1542
1705
  }
1543
1706
  function CancelReasonFields({ reasons, reasonId, onReason, detail, onDetail }) {
@@ -2180,10 +2343,10 @@ function StatusBadge({ status }) {
2180
2343
  opt ? t(opt.labelKey) : status
2181
2344
  ] });
2182
2345
  }
2183
- function formatDueDate(iso) {
2184
- if (!iso) return "\u2014";
2185
- const [y, m, d] = iso.slice(0, 10).split("-");
2186
- return d && m && y ? `${d}/${m}/${y}` : iso;
2346
+ function formatDueDate(iso2) {
2347
+ if (!iso2) return "\u2014";
2348
+ const [y, m, d] = iso2.slice(0, 10).split("-");
2349
+ return d && m && y ? `${d}/${m}/${y}` : iso2;
2187
2350
  }
2188
2351
  function useInvoiceColumns(currency, paymentMethods2, paymentMethodTypes, bankAccounts, consolidar, consolidating, openAttachments, rowMenu) {
2189
2352
  const t = useTranslation();
@@ -2434,7 +2597,13 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
2434
2597
  accountId: emVigor.accountId || void 0,
2435
2598
  costCenterId: emVigor.costCenterId || void 0,
2436
2599
  unitId: effectiveUnitId,
2437
- dateRange: { from: emVigor.from, to: emVigor.to }
2600
+ dateRange: { from: emVigor.from, to: emVigor.to },
2601
+ // A window that reaches today is "what is on my plate now", and the
2602
+ // most urgent part of that is last month's unpaid bill: without this, the
2603
+ // default month view hid every overdue line and people took them for lost.
2604
+ // A window entirely in the past or future is a deliberate look at that
2605
+ // period, and stays exactly that period.
2606
+ includeOverdue: windowReachesToday(emVigor.from, emVigor.to) || void 0
2438
2607
  }), [direction, emVigor, search, effectiveUnitId]);
2439
2608
  useEffect(() => {
2440
2609
  fetchInvoices(query);
@@ -2820,10 +2989,11 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
2820
2989
  // formulário: o assistente lê o papel e devolve o cadastro
2821
2990
  // preenchido. Ver receipt-agent.ts.
2822
2991
  onSelect: () => startReceiptCapture({
2823
- ask: t("financial.receipt.ask"),
2992
+ ask: t(direction === "debit" ? "financial.receipt.ask" : "financial.receipt.askReceivable"),
2824
2993
  uploadLabel: t(direction === "debit" ? "financial.quickTx.digitizeExpense" : "financial.quickTx.digitizeReceipt"),
2825
2994
  uploadHint: t("financial.quickTx.receiptCapture"),
2826
- prompt: t("financial.receipt.prompt")
2995
+ prompt: t(direction === "debit" ? "financial.receipt.prompt" : "financial.receipt.promptReceivable"),
2996
+ direction: direction === "debit" ? "payable" : "receivable"
2827
2997
  })
2828
2998
  }] : [],
2829
2999
  ...direction === "debit" ? [{
@@ -2841,7 +3011,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
2841
3011
  }] : []
2842
3012
  ] : [];
2843
3013
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2844
- onNewGated && /* @__PURE__ */ jsx(ReceiptAgentSurface, { onOpenDraft: () => onNewGated() }),
3014
+ onNewGated && /* @__PURE__ */ jsx(ReceiptAgentSurface, { direction: direction === "debit" ? "payable" : "receivable", onOpenDraft: () => onNewGated() }),
2845
3015
  filtersPanel,
2846
3016
  /* @__PURE__ */ jsx(
2847
3017
  ListView,
@@ -3973,7 +4143,7 @@ function PayableDraftSurface({ config, draft }) {
3973
4143
  liveDraft = {
3974
4144
  describe: () => live.current.describe(),
3975
4145
  setSupplier: (name, id) => live.current.setSupplier(name, id),
3976
- setDueDate: (iso) => live.current.setDueDate(iso),
4146
+ setDueDate: (iso2) => live.current.setDueDate(iso2),
3977
4147
  submit: () => live.current.submit()
3978
4148
  };
3979
4149
  return () => {
@@ -5061,9 +5231,9 @@ function rebalanceInstallments(input) {
5061
5231
  });
5062
5232
  return next.map((c) => c / 100);
5063
5233
  }
5064
- var day2 = (iso) => {
5065
- if (!iso) return "";
5066
- const [y, m, d] = iso.slice(0, 10).split("-");
5234
+ var day2 = (iso2) => {
5235
+ if (!iso2) return "";
5236
+ const [y, m, d] = iso2.slice(0, 10).split("-");
5067
5237
  return `${d}/${m}/${y}`;
5068
5238
  };
5069
5239
  var qty = (n) => n.toLocaleString("pt-BR");
@@ -5702,19 +5872,21 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
5702
5872
  const draftSeeded = useRef(false);
5703
5873
  useEffect(() => {
5704
5874
  if (isEdit || draftSeeded.current) return;
5705
- const draft = consumePayableDraft();
5875
+ const draft = consumePayableDraft(direction);
5706
5876
  if (!draft) return;
5707
5877
  draftSeeded.current = true;
5708
5878
  if (draft.date) setInvoiceDate(draft.date);
5879
+ if (draft.fiscalNumber) setFiscalNumber(draft.fiscalNumber);
5709
5880
  if (draft.notes) setObservations(draft.notes);
5710
- if (draft.supplier) {
5711
- setContactName(draft.supplier);
5881
+ const counterparty = draft.supplier ?? draft.customer;
5882
+ if (counterparty) {
5883
+ setContactName(counterparty);
5712
5884
  void (async () => {
5713
- const hit = (await contactLookup.search(draft.supplier).catch(() => []))?.find((r) => r.label.trim().toLowerCase() === draft.supplier.trim().toLowerCase());
5885
+ const hit = (await contactLookup.search(counterparty).catch(() => []))?.find((r) => r.label.trim().toLowerCase() === counterparty.trim().toLowerCase());
5714
5886
  if (hit) {
5715
5887
  setContactId(hit.id);
5716
5888
  setContactName(hit.label);
5717
- } else setPendingContact(draft.supplier);
5889
+ } else setPendingContact(counterparty);
5718
5890
  })();
5719
5891
  }
5720
5892
  setItems(
@@ -5730,7 +5902,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
5730
5902
  ] : [{
5731
5903
  _id: fid(),
5732
5904
  itemKind: "other",
5733
- description: draft.description ?? draft.supplier ?? "",
5905
+ description: draft.description ?? counterparty ?? "",
5734
5906
  quantity: 1,
5735
5907
  unitPrice: draft.amount - (draft.shipping ?? 0)
5736
5908
  }]
@@ -6282,7 +6454,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
6282
6454
  setContactId(id);
6283
6455
  setPendingContact(id ? null : name);
6284
6456
  },
6285
- setDueDate: (iso) => setInstallments((prev) => prev.map((inst, i) => i === 0 ? { ...inst, dueDate: iso } : inst)),
6457
+ setDueDate: (iso2) => setInstallments((prev) => prev.map((inst, i) => i === 0 ? { ...inst, dueDate: iso2 } : inst)),
6286
6458
  submit: async () => {
6287
6459
  await handleSave();
6288
6460
  }
@@ -6548,9 +6720,9 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
6548
6720
  ` })
6549
6721
  ] });
6550
6722
  }
6551
- var dataBR = (iso) => {
6552
- const [ano, mes, dia] = iso.slice(0, 10).split("-");
6553
- return ano && mes && dia ? `${dia}/${mes}/${ano}` : iso;
6723
+ var dataBR = (iso2) => {
6724
+ const [ano, mes, dia] = iso2.slice(0, 10).split("-");
6725
+ return ano && mes && dia ? `${dia}/${mes}/${ano}` : iso2;
6554
6726
  };
6555
6727
  function Campo({ icon: Icon, label, children, tone }) {
6556
6728
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
@@ -8138,9 +8310,9 @@ function CashClosingPanel({
8138
8310
  )
8139
8311
  ] });
8140
8312
  }
8141
- function dayKeyOf(iso) {
8142
- if (!iso) return "";
8143
- const d = new Date(iso);
8313
+ function dayKeyOf(iso2) {
8314
+ if (!iso2) return "";
8315
+ const d = new Date(iso2);
8144
8316
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
8145
8317
  }
8146
8318
  function groupByClosingDay(rows) {
@@ -9853,9 +10025,9 @@ var STATE_META = {
9853
10025
  function formatCode(code) {
9854
10026
  return code.replace(/(.{4})(?=.)/g, "$1 ");
9855
10027
  }
9856
- function formatDate(iso) {
9857
- if (!iso) return null;
9858
- const d = new Date(iso);
10028
+ function formatDate(iso2) {
10029
+ if (!iso2) return null;
10030
+ const d = new Date(iso2);
9859
10031
  return Number.isNaN(d.getTime()) ? null : d.toLocaleDateString("pt-BR");
9860
10032
  }
9861
10033
  function Dash() {
@@ -10276,12 +10448,12 @@ var PROMPT = [
10276
10448
  "- `warnings` diz o que ficou ileg\xEDvel ou foi chute. Vazio quando leu tudo."
10277
10449
  ].join("\n");
10278
10450
  function stripFence(text2) {
10279
- const trimmed = text2.trim();
10280
- const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/);
10451
+ const trimmed2 = text2.trim();
10452
+ const fenced = trimmed2.match(/```(?:json)?\s*([\s\S]*?)```/);
10281
10453
  if (fenced) return fenced[1].trim();
10282
- const start = trimmed.indexOf("{");
10283
- const end = trimmed.lastIndexOf("}");
10284
- return start >= 0 && end > start ? trimmed.slice(start, end + 1) : trimmed;
10454
+ const start = trimmed2.indexOf("{");
10455
+ const end = trimmed2.lastIndexOf("}");
10456
+ return start >= 0 && end > start ? trimmed2.slice(start, end + 1) : trimmed2;
10285
10457
  }
10286
10458
  var num2 = (v, fallback = 0) => {
10287
10459
  const n = typeof v === "string" ? Number(v.replace(",", ".")) : Number(v);
@@ -13039,6 +13211,28 @@ function FinancialPage({ config: appConfig, provider, store: store6, registries
13039
13211
  }
13040
13212
  ) });
13041
13213
  }
13214
+
13215
+ // src/lib/recent-transactions.ts
13216
+ var RECENT_TRANSACTIONS_QUERY = { sort: "recent", pageSize: 20 };
13217
+ var iso = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
13218
+ function recentWindow(now = /* @__PURE__ */ new Date()) {
13219
+ return {
13220
+ from: iso(new Date(now.getFullYear(), now.getMonth(), now.getDate() - 90)),
13221
+ to: iso(new Date(now.getFullYear(), now.getMonth() + 2, 0))
13222
+ };
13223
+ }
13224
+ function latestPerInvoice(lines, limit) {
13225
+ const seen = /* @__PURE__ */ new Set();
13226
+ const out = [];
13227
+ for (const line of lines) {
13228
+ const key = line.invoiceId ?? line.id;
13229
+ if (seen.has(key)) continue;
13230
+ seen.add(key);
13231
+ out.push(line);
13232
+ if (out.length === limit) break;
13233
+ }
13234
+ return out;
13235
+ }
13042
13236
  function useEnsureSummary() {
13043
13237
  const fetchSummary = useFinancialStore((s) => s.fetchSummary);
13044
13238
  useEffect(() => {
@@ -13165,11 +13359,17 @@ function OverdueAlerts() {
13165
13359
  function RecentTransactions() {
13166
13360
  const t = useTranslation();
13167
13361
  const { currency } = useFinancialConfig();
13168
- const invoices = useFinancialStore((s) => s.invoices);
13169
- const fetchInvoices = useFinancialStore((s) => s.fetchInvoices);
13362
+ const provider = useFinancialProvider();
13363
+ const [invoices, setInvoices] = React29__default.useState([]);
13170
13364
  useEffect(() => {
13171
- void fetchInvoices({ pageSize: 5 });
13172
- }, []);
13365
+ let alive = true;
13366
+ void provider.getInvoices({ ...RECENT_TRANSACTIONS_QUERY, dateRange: recentWindow() }).then((res) => {
13367
+ if (alive) setInvoices(latestPerInvoice(res.data, 5));
13368
+ }).catch((error) => console.warn("[financial] recent transactions unavailable", error));
13369
+ return () => {
13370
+ alive = false;
13371
+ };
13372
+ }, [provider]);
13173
13373
  const columns = [
13174
13374
  { accessorKey: "contactName", header: t("financial.summary.recentTransactions"), cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "font-medium", children: row.original.contactName || "Unknown" }) },
13175
13375
  { id: "date", header: "", cell: ({ row }) => /* @__PURE__ */ jsxs("span", { className: "text-xs capitalize text-muted-foreground", children: [
@@ -13182,7 +13382,7 @@ function RecentTransactions() {
13182
13382
  formatCurrency(row.original.totalAmount, currency)
13183
13383
  ] }) }
13184
13384
  ];
13185
- return /* @__PURE__ */ jsx(TableWidget, { title: t("financial.summary.recentTransactions"), icon: "CircleDollarSign", columns, data: invoices.slice(0, 5), emptyMessage: t("financial.summary.noTransactions") });
13385
+ return /* @__PURE__ */ jsx(TableWidget, { title: t("financial.summary.recentTransactions"), icon: "CircleDollarSign", columns, data: invoices, emptyMessage: t("financial.summary.noTransactions") });
13186
13386
  }
13187
13387
  function useDueLabel() {
13188
13388
  const t = useTranslation();
@@ -14497,6 +14697,8 @@ var en = {
14497
14697
  "financial.receipt.fromReceipt": "From the receipt",
14498
14698
  "financial.receipt.finishContact": "Finish adding",
14499
14699
  "financial.receipt.prompt": "Read this receipt and open the payable form with whatever it states.",
14700
+ "financial.receipt.askReceivable": "Send the photo or PDF of the invoice you issued\u2026",
14701
+ "financial.receipt.promptReceivable": "Read this invoice and open the receivable form with the customer (recipient), amount, NF-e number and access key it states.",
14500
14702
  "financial.quickTx.descriptionPlaceholder": "e.g. Groceries, Salary\u2026",
14501
14703
  "financial.quickTx.enterAmount": "Enter an amount",
14502
14704
  "financial.quickTx.save": "Save",
@@ -15500,6 +15702,8 @@ var ptBR = {
15500
15702
  "financial.receipt.fromReceipt": "Do recibo",
15501
15703
  "financial.receipt.finishContact": "Concluir cadastro",
15502
15704
  "financial.receipt.prompt": "Leia este recibo e abra o cadastro da conta com o que estiver escrito nele.",
15705
+ "financial.receipt.askReceivable": "Mande a foto ou o PDF da nota que voc\xEA emitiu\u2026",
15706
+ "financial.receipt.promptReceivable": "Leia esta nota e abra a conta a receber com o cliente (destinat\xE1rio), o valor, o n\xFAmero e a chave da NF-e que estiverem nela.",
15503
15707
  "financial.quickTx.descriptionPlaceholder": "ex.: Mercado, Sal\xE1rio\u2026",
15504
15708
  "financial.quickTx.enterAmount": "Informe um valor",
15505
15709
  "financial.quickTx.save": "Salvar",