@fayz-ai/plugin-financial 0.25.2 → 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 +254 -50
- package/dist/index.js.map +1 -1
- package/dist/lib/list-window.d.ts +3 -0
- package/dist/lib/list-window.d.ts.map +1 -0
- package/dist/lib/recent-transactions.d.ts +10 -0
- package/dist/lib/recent-transactions.d.ts.map +1 -0
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.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/ReceiptAgentSurface.d.ts +4 -1
- package/dist/views/ReceiptAgentSurface.d.ts.map +1 -1
- package/dist/views/dashboardWidgets.d.ts.map +1 -1
- package/dist/views/receipt-agent.d.ts +24 -2
- package/dist/views/receipt-agent.d.ts.map +1 -1
- package/package.json +5 -5
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
|
|
734
|
-
return isRealDate(
|
|
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(
|
|
741
|
-
const [y, m, d] =
|
|
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
|
|
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
|
-
|
|
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(
|
|
1540
|
-
const [y, m, d] =
|
|
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(
|
|
2184
|
-
if (!
|
|
2185
|
-
const [y, m, d] =
|
|
2186
|
-
return d && m && y ? `${d}/${m}/${y}` :
|
|
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: (
|
|
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 = (
|
|
5065
|
-
if (!
|
|
5066
|
-
const [y, m, d] =
|
|
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
|
-
|
|
5711
|
-
|
|
5881
|
+
const counterparty = draft.supplier ?? draft.customer;
|
|
5882
|
+
if (counterparty) {
|
|
5883
|
+
setContactName(counterparty);
|
|
5712
5884
|
void (async () => {
|
|
5713
|
-
const hit = (await contactLookup.search(
|
|
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(
|
|
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 ??
|
|
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: (
|
|
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 = (
|
|
6552
|
-
const [ano, mes, dia] =
|
|
6553
|
-
return ano && mes && dia ? `${dia}/${mes}/${ano}` :
|
|
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(
|
|
8142
|
-
if (!
|
|
8143
|
-
const d = new Date(
|
|
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(
|
|
9857
|
-
if (!
|
|
9858
|
-
const d = new Date(
|
|
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
|
|
10280
|
-
const fenced =
|
|
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 =
|
|
10283
|
-
const end =
|
|
10284
|
-
return start >= 0 && end > start ?
|
|
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
|
|
13169
|
-
const
|
|
13362
|
+
const provider = useFinancialProvider();
|
|
13363
|
+
const [invoices, setInvoices] = React29__default.useState([]);
|
|
13170
13364
|
useEffect(() => {
|
|
13171
|
-
|
|
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
|
|
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",
|