@fayz-ai/plugin-fiscal-br 0.5.1 → 0.7.0

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,9 +1,9 @@
1
- import { recordRailFields, RECORD_GROUP, activeFacet, getSupabaseClientOptional, connectionSettings, connectionStatus, definePlugin, createConnectionStore, settingsStatus, maskCpfCnpj } from '@fayz-ai/core';
2
- import React5, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
3
- import { ModalHeader, ModalTitle, ModalBody, Skeleton, AiActionButton, Card, Button, EmBreve, SearchSelect, Input, DatePicker, Hint, ModalFooter, SubpageHeader, useFilterDraft, PageHeaderActions, FilterPanel, FilterPanelField, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SearchField, DataTable, ListFilterBar, Modal, ModalContent, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ModalDescription, Badge, SegmentedControl, ComingSoon, toast, CardHeader, CardTitle, CardContent, CardDescription, Switch } from '@fayz-ai/ui';
1
+ import { recordRailFields, RECORD_GROUP, activeFacet, describeError, getSupabaseClientOptional, connectionSettings, connectionStatus, definePlugin, createConnectionStore, settingsStatus, maskCpfCnpj } from '@fayz-ai/core';
2
+ import React7, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
3
+ import { ModalHeader, ModalTitle, ModalBody, Skeleton, AiActionButton, Dropdown, DropdownTrigger, Button, DropdownContent, DropdownItem, Hint, Card, SearchSelect, Input, EmBreve, DatePicker, ModalFooter, SubpageHeader, useFilterDraft, RowActions, PageHeaderActions, FilterPanel, FilterPanelField, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SearchField, TooltipProvider, DataTable, Tooltip, TooltipTrigger, TooltipContent, ListFilterBar, Modal, ModalContent, ModalDescription, Badge, SegmentedControl, ComingSoon, toast, CardHeader, CardTitle, CardContent, CardDescription, Switch } from '@fayz-ai/ui';
4
4
  export { ComingSoon, EmBreve } from '@fayz-ai/ui';
5
- import { registerDocumentOpener, getActiveTenantId, useUnitFilter, usePermissionOptional, UNIT_DETAIL_ZONES, adminNavigateTo } from '@fayz-ai/admin';
6
- import { Gift, RefreshCw, Loader2, FileDown, AlertTriangle, ChevronRight, Check, Trash2, Plus, Inbox, UploadCloud, ShieldCheck, FileText, ExternalLink, CheckCircle2, FileCheck2, ChevronDown, XCircle, X, Info } from 'lucide-react';
5
+ import { registerDocumentOpener, PersonLink, getActiveTenantId, useUnitFilter, usePermissionOptional, UNIT_DETAIL_ZONES, adminNavigateTo } from '@fayz-ai/admin';
6
+ import { Loader2, Download, ChevronDown, FileCode2, FileText, RefreshCw, UserPlus, Gift, AlertTriangle, ChevronRight, Check, Trash2, Plus, Image, UploadCloud, Paperclip, ScanLine, Eye, ListChecks, Inbox, KeyRound, FileDown, ShieldCheck, ExternalLink, CheckCircle2, FileCheck2, XCircle, X, Info } from 'lucide-react';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import { FINANCIAL_INVOICE_ZONES } from '@fayz-ai/plugin-financial';
9
9
 
@@ -3771,6 +3771,164 @@ REVOKE ALL ON FUNCTION public.fiscalbr_note_apply(uuid, text, jsonb, jsonb) FROM
3771
3771
  GRANT EXECUTE ON FUNCTION public.fiscalbr_note_for_provider(uuid, text) TO authenticated;
3772
3772
  GRANT EXECUTE ON FUNCTION public.fiscalbr_note_apply(uuid, text, jsonb, jsonb) TO authenticated;
3773
3773
  `;
3774
+ var MIGRATION_020_A_NOTA_ACHA_O_FORNECEDOR_PELO_CNPJ = `-- A nota acha o fornecedor pelo CNPJ \u2014 e, se n\xE3o achar, cadastra.
3775
+ --
3776
+ -- O est\xE1gio (009) s\xF3 casava o CNPJ no momento do import. As notas que vieram
3777
+ -- do V1 atravessaram sem o v\xEDnculo (003 deixou para "a costura"), e um
3778
+ -- fornecedor cadastrado depois nunca era religado: 1029 notas com o fornecedor
3779
+ -- no cadastro e a tela dizendo "n\xE3o est\xE1 no cadastro", e o t\xEDtulo aceito no V2
3780
+ -- nascendo sem contraparte (o tra\xE7o em Contas a Pagar).
3781
+ --
3782
+ -- O V1 nunca pediu cadastro: ao aceitar, \`resolveSupplierId\` procura o
3783
+ -- fornecedor pelo CNPJ e, sem achar, cria com o nome e o CNPJ da nota. Esta
3784
+ -- fun\xE7\xE3o \xE9 esse gesto, com duas pontas:
3785
+ --
3786
+ -- p_create = false s\xF3 casa (ao abrir a nota)
3787
+ -- p_create = true casa ou cria (antes de aceitar)
3788
+ --
3789
+ -- O t\xEDtulo da nota, se j\xE1 existe e est\xE1 SEM contraparte, recebe a mesma pessoa.
3790
+ -- Nunca troca um v\xEDnculo gravado. Idempotente.
3791
+
3792
+ CREATE OR REPLACE FUNCTION public.plg_fiscalbr_supplier_for_cnpj(p_tenant uuid, p_cnpj text)
3793
+ RETURNS uuid
3794
+ LANGUAGE sql
3795
+ STABLE
3796
+ SET search_path TO ''
3797
+ AS $$
3798
+ SELECT p.id FROM public.people p
3799
+ WHERE p.tenant_id = p_tenant
3800
+ AND p_cnpj <> ''
3801
+ AND public.plg_fiscalbr_digits(p.document_number) = p_cnpj
3802
+ ORDER BY (p.kind = 'supplier') DESC, p.created_at
3803
+ LIMIT 1
3804
+ $$;
3805
+
3806
+ CREATE OR REPLACE FUNCTION public.fiscalbr_link_inbox_supplier(p_document_id uuid, p_create boolean DEFAULT false)
3807
+ RETURNS jsonb
3808
+ LANGUAGE plpgsql
3809
+ SECURITY DEFINER
3810
+ SET search_path TO ''
3811
+ AS $$
3812
+ DECLARE
3813
+ v_tenant uuid := public.plg_financial_require_tenant();
3814
+ v_doc public.plg_fiscalbr_inbox_documents%ROWTYPE;
3815
+ v_cnpj text;
3816
+ v_party uuid;
3817
+ v_created boolean := false;
3818
+ BEGIN
3819
+ SELECT * INTO v_doc FROM public.plg_fiscalbr_inbox_documents
3820
+ WHERE id = p_document_id AND tenant_id = v_tenant;
3821
+ IF NOT FOUND THEN
3822
+ RAISE EXCEPTION 'fiscal: inbox note not found' USING ERRCODE = 'P0002';
3823
+ END IF;
3824
+ PERFORM public.plg_financial_require_permission(
3825
+ CASE WHEN p_create THEN 'financial.create' ELSE 'financial.read' END, v_doc.unit_id);
3826
+
3827
+ v_party := v_doc.supplier_party_id;
3828
+ v_cnpj := public.plg_fiscalbr_digits(v_doc.supplier_cnpj);
3829
+
3830
+ IF v_party IS NULL THEN
3831
+ v_party := public.plg_fiscalbr_supplier_for_cnpj(v_tenant, v_cnpj);
3832
+ END IF;
3833
+
3834
+ -- Without a CNPJ there is nothing to match on; a name-only person would be a
3835
+ -- duplicate waiting to happen, so creation needs the document.
3836
+ IF v_party IS NULL AND p_create AND v_cnpj <> '' THEN
3837
+ INSERT INTO public.people (tenant_id, kind, name, document_number)
3838
+ VALUES (v_tenant, 'supplier', coalesce(nullif(trim(v_doc.supplier_name), ''), 'Fornecedor ' || v_cnpj), v_cnpj)
3839
+ RETURNING id INTO v_party;
3840
+ v_created := true;
3841
+ END IF;
3842
+
3843
+ IF v_party IS NOT NULL AND v_doc.supplier_party_id IS NULL THEN
3844
+ UPDATE public.plg_fiscalbr_inbox_documents
3845
+ SET supplier_party_id = v_party
3846
+ WHERE id = v_doc.id AND supplier_party_id IS NULL;
3847
+ END IF;
3848
+
3849
+ IF v_party IS NOT NULL AND v_doc.invoice_id IS NOT NULL THEN
3850
+ UPDATE public.plg_financial_invoices
3851
+ SET counterparty_person_id = v_party
3852
+ WHERE id = v_doc.invoice_id AND tenant_id = v_tenant AND counterparty_person_id IS NULL;
3853
+ END IF;
3854
+
3855
+ RETURN jsonb_build_object('supplier_party_id', v_party, 'created', v_created);
3856
+ END;
3857
+ $$;
3858
+
3859
+ REVOKE ALL ON FUNCTION public.fiscalbr_link_inbox_supplier(uuid, boolean) FROM PUBLIC;
3860
+ GRANT EXECUTE ON FUNCTION public.fiscalbr_link_inbox_supplier(uuid, boolean) TO authenticated;
3861
+
3862
+ -- Backfill: match only, NULLs only. Nothing is created here \u2014 creating a person
3863
+ -- is the operator's gesture (accepting), not a migration's.
3864
+ WITH p AS (
3865
+ SELECT DISTINCT ON (tenant_id, dg) tenant_id, dg, id
3866
+ FROM (SELECT tenant_id, id, kind, created_at, public.plg_fiscalbr_digits(document_number) AS dg
3867
+ FROM public.people
3868
+ WHERE document_number IS NOT NULL) x
3869
+ WHERE length(dg) = 14
3870
+ ORDER BY tenant_id, dg, (kind = 'supplier') DESC, created_at
3871
+ )
3872
+ UPDATE public.plg_fiscalbr_inbox_documents d
3873
+ SET supplier_party_id = p.id
3874
+ FROM p
3875
+ WHERE d.supplier_party_id IS NULL
3876
+ AND p.tenant_id = d.tenant_id
3877
+ AND p.dg = public.plg_fiscalbr_digits(d.supplier_cnpj);
3878
+
3879
+ UPDATE public.plg_financial_invoices i
3880
+ SET counterparty_person_id = d.supplier_party_id
3881
+ FROM public.plg_fiscalbr_inbox_documents d
3882
+ WHERE d.invoice_id = i.id
3883
+ AND d.tenant_id = i.tenant_id
3884
+ AND d.supplier_party_id IS NOT NULL
3885
+ AND i.counterparty_person_id IS NULL;
3886
+ `;
3887
+ var MIGRATION_021_USO_E_CONSUMO_ENTRA_NO_ESTOQUE = `-- "Materiais de Uso e Consumo" vira um tipo de registro da nota recebida.
3888
+ --
3889
+ -- \xC9 o que a casa gasta sem vender nem transformar \u2014 luva, descart\xE1vel, material
3890
+ -- de limpeza \u2014 e que ainda assim se conta no estoque. Entra no estoque como o
3891
+ -- insumo: precisa de produto vinculado e gera movimento ao aceitar.
3892
+ --
3893
+ -- Aditivo e idempotente. A fun\xE7\xE3o de aceite \xE9 REMENDADA, n\xE3o reescrita (mesmo
3894
+ -- padr\xE3o da 015): copiar o corpo da 010 apagaria o \`reason\` que a 015 enxertou.
3895
+
3896
+ ALTER TABLE public.plg_fiscalbr_inbox_items
3897
+ DROP CONSTRAINT IF EXISTS plg_fiscalbr_inbox_items_entry_type_chk;
3898
+ ALTER TABLE public.plg_fiscalbr_inbox_items
3899
+ ADD CONSTRAINT plg_fiscalbr_inbox_items_entry_type_chk
3900
+ CHECK (entry_type IS NULL OR entry_type IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico'));
3901
+
3902
+ ALTER TABLE public.plg_fiscalbr_supplier_product_map
3903
+ DROP CONSTRAINT IF EXISTS plg_fiscalbr_supplier_product_map_entry_type_chk;
3904
+ ALTER TABLE public.plg_fiscalbr_supplier_product_map
3905
+ ADD CONSTRAINT plg_fiscalbr_supplier_product_map_entry_type_chk
3906
+ CHECK (entry_type IS NULL OR entry_type IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico'));
3907
+
3908
+ DO $m$
3909
+ DECLARE
3910
+ v_def text;
3911
+ v_antes text := $q$entry_type IN ('insumo','revenda','patrimonio')$q$;
3912
+ v_depois text := $q$entry_type IN ('insumo','revenda','patrimonio','uso_consumo')$q$;
3913
+ BEGIN
3914
+ SELECT pg_get_functiondef(p.oid) INTO v_def
3915
+ FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
3916
+ WHERE n.nspname = 'public' AND p.proname = 'fiscalbr_accept_inbox_document';
3917
+
3918
+ IF v_def IS NULL THEN
3919
+ RAISE EXCEPTION 'fiscalbr/021: fiscalbr_accept_inbox_document n\xE3o existe \u2014 aplicar 009/010 antes';
3920
+ END IF;
3921
+
3922
+ IF position('uso_consumo' in v_def) > 0 THEN RETURN; END IF;
3923
+
3924
+ IF position(v_antes in v_def) = 0 THEN
3925
+ RAISE EXCEPTION 'fiscalbr/021: o corpo de fiscalbr_accept_inbox_document n\xE3o \xE9 o esperado \u2014 confira \xE0 m\xE3o em vez de deixar passar calado';
3926
+ END IF;
3927
+
3928
+ EXECUTE replace(v_def, v_antes, v_depois);
3929
+ END
3930
+ $m$;
3931
+ `;
3774
3932
  var MIGRATIONS = [
3775
3933
  { id: "000_baseline", sql: MIGRATION_000_BASELINE },
3776
3934
  { id: "001_preserve_terminal_results_and_recover_leases", sql: MIGRATION_001_PRESERVE_TERMINAL_RESULTS_AND_RECOVER_LEASES },
@@ -3791,7 +3949,9 @@ var MIGRATIONS = [
3791
3949
  { id: "016_a_nota_guarda_o_proprio_xml", sql: MIGRATION_016_A_NOTA_GUARDA_O_PROPRIO_XML },
3792
3950
  { id: "017_quem_emite_e_o_cnpj_nao_a_loja", sql: MIGRATION_017_QUEM_EMITE_E_O_CNPJ_NAO_A_LOJA },
3793
3951
  { id: "018_a_nota_do_v1_alcanca_a_conta", sql: MIGRATION_018_A_NOTA_DO_V1_ALCANCA_A_CONTA },
3794
- { id: "019_a_nota_emitida_se_cancela_pelo_v2", sql: MIGRATION_019_A_NOTA_EMITIDA_SE_CANCELA_PELO_V2 }
3952
+ { id: "019_a_nota_emitida_se_cancela_pelo_v2", sql: MIGRATION_019_A_NOTA_EMITIDA_SE_CANCELA_PELO_V2 },
3953
+ { id: "020_a_nota_acha_o_fornecedor_pelo_cnpj", sql: MIGRATION_020_A_NOTA_ACHA_O_FORNECEDOR_PELO_CNPJ },
3954
+ { id: "021_uso_e_consumo_entra_no_estoque", sql: MIGRATION_021_USO_E_CONSUMO_ENTRA_NO_ESTOQUE }
3795
3955
  ];
3796
3956
  var taxGroupEntity = {
3797
3957
  name: "Grupo fiscal",
@@ -4366,6 +4526,44 @@ async function stageNoteFromXml(xml, options = {}) {
4366
4526
  parsed
4367
4527
  };
4368
4528
  }
4529
+ var PullByKeyError = class extends Error {
4530
+ constructor(message, code, options) {
4531
+ super(message);
4532
+ this.code = code;
4533
+ this.options = options;
4534
+ }
4535
+ };
4536
+ async function pullNoteByKey(key, options = {}) {
4537
+ const { data, error } = await sb().functions.invoke("fiscalbr-dfe-pull", {
4538
+ body: { key: key.replace(/\D/g, ""), legalEntityId: options.legalEntityId, tenantId: getActiveTenantId() ?? null }
4539
+ });
4540
+ if (error) {
4541
+ let body = null;
4542
+ try {
4543
+ body = await error?.context?.json?.();
4544
+ } catch {
4545
+ }
4546
+ throw new PullByKeyError(body?.error || error.message || "Falha ao falar com a SEFAZ.", body?.code, body?.options);
4547
+ }
4548
+ if (data?.pending) return { status: "pending", message: String(data.message ?? "") };
4549
+ if (!data?.xml) throw new PullByKeyError("A SEFAZ n\xE3o devolveu o XML desta nota.");
4550
+ return { status: "staged", ...await stageNoteFromXml(String(data.xml), { unitId: options.unitId }) };
4551
+ }
4552
+ async function getInboxNotePdf(accessKey) {
4553
+ const { data, error } = await sb().functions.invoke("fiscalbr-dfe-pull", {
4554
+ body: { action: "pdf", key: accessKey, tenantId: getActiveTenantId() ?? null }
4555
+ });
4556
+ if (error) {
4557
+ let body = null;
4558
+ try {
4559
+ body = await error?.context?.json?.();
4560
+ } catch {
4561
+ }
4562
+ throw new PullByKeyError(body?.error || error.message || "Falha ao buscar o DANFE.", body?.code);
4563
+ }
4564
+ if (!(data instanceof Blob)) throw new PullByKeyError("O servi\xE7o fiscal n\xE3o devolveu um PDF.");
4565
+ return data;
4566
+ }
4369
4567
  async function saveNoteDecision(input) {
4370
4568
  const { data, error } = await sb().rpc("fiscalbr_save_inbox_decision", {
4371
4569
  p_document_id: input.documentId,
@@ -4404,7 +4602,16 @@ async function saveNoteDecision(input) {
4404
4602
  reconciled: Number(res.reconciled ?? 0)
4405
4603
  };
4406
4604
  }
4605
+ async function linkInboxSupplier(documentId, create = false) {
4606
+ const { data, error } = await sb().rpc("fiscalbr_link_inbox_supplier", { p_document_id: documentId, p_create: create });
4607
+ if (error) throw error;
4608
+ const res = data ?? {};
4609
+ return { supplierPartyId: res.supplier_party_id ?? null, created: Boolean(res.created) };
4610
+ }
4407
4611
  async function acceptNote(documentId, locationId) {
4612
+ await linkInboxSupplier(documentId, true).catch((e) => {
4613
+ if (e?.code !== "PGRST202" && e?.code !== "42883") throw e;
4614
+ });
4408
4615
  const { data, error } = await sb().rpc("fiscalbr_accept_inbox_document", {
4409
4616
  p_document_id: documentId,
4410
4617
  p_location_id: locationId ?? null
@@ -4531,6 +4738,222 @@ async function listSupplierMap(supplierCnpj) {
4531
4738
  lastUsedAt: m.last_used_at ?? null
4532
4739
  }));
4533
4740
  }
4741
+ var NOTE_SUBJECT = "fiscalbr_inbox_document";
4742
+ var INVOICE_SUBJECT = "financial_invoice";
4743
+ async function listNoteAttachments(noteId, invoiceId) {
4744
+ const subjects = [`and(subject_type.eq.${NOTE_SUBJECT},subject_id.eq.${noteId})`];
4745
+ if (invoiceId) subjects.push(`and(subject_type.eq.${INVOICE_SUBJECT},subject_id.eq.${invoiceId})`);
4746
+ const { data, error } = await sb().from("documents").select("id, file_name, file_size, mime_type, storage_bucket, storage_path, created_at").or(subjects.join(",")).eq("is_active", true).order("created_at");
4747
+ if (error) throw error;
4748
+ const rows = data ?? [];
4749
+ const signed = /* @__PURE__ */ new Map();
4750
+ const byBucket = /* @__PURE__ */ new Map();
4751
+ for (const r of rows) {
4752
+ if (!r.storage_bucket || !r.storage_path) continue;
4753
+ byBucket.set(r.storage_bucket, [...byBucket.get(r.storage_bucket) ?? [], r.storage_path]);
4754
+ }
4755
+ for (const [bucket, paths] of byBucket) {
4756
+ const { data: urls } = await sb().storage.from(bucket).createSignedUrls(paths, 60 * 30);
4757
+ for (const u of urls ?? []) if (u.path && u.signedUrl) signed.set(u.path, u.signedUrl);
4758
+ }
4759
+ return rows.map((r) => ({
4760
+ id: r.id,
4761
+ fileName: r.file_name ?? "Anexo",
4762
+ fileSize: r.file_size ?? void 0,
4763
+ mimeType: r.mime_type ?? void 0,
4764
+ url: signed.get(r.storage_path) ?? void 0,
4765
+ createdAt: r.created_at
4766
+ }));
4767
+ }
4768
+ async function addNoteAttachment(noteId, invoiceId, file) {
4769
+ const tenantId = getActiveTenantId();
4770
+ if (!tenantId) throw new Error("Conta sem empresa ativa");
4771
+ const subjectType = invoiceId ? INVOICE_SUBJECT : NOTE_SUBJECT;
4772
+ const subjectId = invoiceId ?? noteId;
4773
+ const path = `${tenantId}/${subjectType}/${subjectId}/${Date.now()}_${file.name.replace(/[^\w.\-]+/g, "_")}`;
4774
+ const { error: upErr } = await sb().storage.from("attachments").upload(path, file, { contentType: file.type || void 0 });
4775
+ if (upErr) throw upErr;
4776
+ const { error } = await sb().from("documents").insert({
4777
+ tenant_id: tenantId,
4778
+ kind: "attachment",
4779
+ status: "active",
4780
+ subject_type: subjectType,
4781
+ subject_id: subjectId,
4782
+ file_name: file.name,
4783
+ file_size: file.size,
4784
+ mime_type: file.type || null,
4785
+ storage_provider: "supabase",
4786
+ storage_bucket: "attachments",
4787
+ storage_path: path
4788
+ });
4789
+ if (error) {
4790
+ await sb().storage.from("attachments").remove([path]).catch(() => {
4791
+ });
4792
+ throw error;
4793
+ }
4794
+ }
4795
+ async function removeNoteAttachment(id) {
4796
+ const { error } = await sb().from("documents").update({ is_active: false }).eq("id", id);
4797
+ if (error) throw error;
4798
+ }
4799
+ async function moveNoteAttachmentsToInvoice(noteId, invoiceId) {
4800
+ const { error } = await sb().from("documents").update({ subject_type: INVOICE_SUBJECT, subject_id: invoiceId }).eq("subject_type", NOTE_SUBJECT).eq("subject_id", noteId);
4801
+ if (error) throw error;
4802
+ }
4803
+ async function extractPaymentCode(file) {
4804
+ const base64 = await new Promise((resolve, reject) => {
4805
+ const reader = new FileReader();
4806
+ reader.onload = () => {
4807
+ const r = String(reader.result);
4808
+ resolve(r.slice(r.indexOf(",") + 1));
4809
+ };
4810
+ reader.onerror = () => reject(reader.error);
4811
+ reader.readAsDataURL(file);
4812
+ });
4813
+ const { data, error } = await sb().functions.invoke("fiscalbr-payment-extract", {
4814
+ body: { name: file.name, mime: file.type, base64 }
4815
+ });
4816
+ if (error) {
4817
+ let body = null;
4818
+ try {
4819
+ body = await error?.context?.json?.();
4820
+ } catch {
4821
+ }
4822
+ throw new Error(body?.error || error.message || "Falha ao ler o boleto.");
4823
+ }
4824
+ if (!data?.ok || data.kind === "none" || !data.code) return null;
4825
+ const code = String(data.code);
4826
+ if (data.kind === "barcode") {
4827
+ const parsed = parseBoleto(code);
4828
+ return {
4829
+ kind: "barcode",
4830
+ code: code.replace(/\D/g, ""),
4831
+ amount: parsed?.amount ?? (typeof data.amount === "number" ? data.amount : null),
4832
+ dueDate: parsed?.dueDate ?? (data.due_date ?? null)
4833
+ };
4834
+ }
4835
+ return { kind: "pix", code, amount: typeof data.amount === "number" ? data.amount : null, dueDate: data.due_date ?? null };
4836
+ }
4837
+ function parseBoleto(raw) {
4838
+ const d = raw.replace(/\D/g, "");
4839
+ if (d.startsWith("8")) return null;
4840
+ let barcode;
4841
+ if (d.length === 44) barcode = d;
4842
+ else if (d.length === 47) barcode = d.slice(0, 4) + d[32] + d.slice(33, 47) + d.slice(4, 9) + d.slice(10, 20) + d.slice(21, 31);
4843
+ else return null;
4844
+ const factor = Number(barcode.slice(5, 9));
4845
+ const cents = Number(barcode.slice(9, 19));
4846
+ let dueDate = null;
4847
+ if (factor > 0) {
4848
+ const base = factor >= 1e3 && Date.now() >= Date.UTC(2025, 1, 22) ? Date.UTC(2025, 1, 22) - 1e3 * 864e5 : Date.UTC(1997, 9, 7);
4849
+ dueDate = new Date(base + factor * 864e5).toISOString().slice(0, 10);
4850
+ }
4851
+ return { amount: cents > 0 ? cents / 100 : null, dueDate };
4852
+ }
4853
+ var formatCnpj = (d) => `${d.slice(0, 2)}.${d.slice(2, 5)}.${d.slice(5, 8)}/${d.slice(8, 12)}-${d.slice(12)}`;
4854
+ var groupKey = (digits2) => digits2.replace(/(\d{4})(?=\d)/g, "$1 ");
4855
+ function PullByKeyDialog({ open, onOpenChange, unitId, onPulled }) {
4856
+ const [value, setValue] = useState("");
4857
+ const [busy, setBusy] = useState(false);
4858
+ const [error, setError] = useState(null);
4859
+ const [notice, setNotice] = useState(null);
4860
+ const [cnpjOptions, setCnpjOptions] = useState(null);
4861
+ const [legalEntityId, setLegalEntityId] = useState("");
4862
+ useEffect(() => {
4863
+ if (open) return;
4864
+ setValue("");
4865
+ setError(null);
4866
+ setNotice(null);
4867
+ setCnpjOptions(null);
4868
+ setLegalEntityId("");
4869
+ }, [open]);
4870
+ const digits2 = value.replace(/\D/g, "").slice(0, 44);
4871
+ const needsCnpj = !!cnpjOptions && !legalEntityId;
4872
+ const blocked = digits2.length !== 44 ? `Faltam ${44 - digits2.length} d\xEDgitos` : needsCnpj ? "Escolha o CNPJ" : null;
4873
+ const pull = async () => {
4874
+ setBusy(true);
4875
+ setError(null);
4876
+ setNotice(null);
4877
+ try {
4878
+ const result = await pullNoteByKey(digits2, { legalEntityId: legalEntityId || void 0, unitId });
4879
+ if (result.status === "pending") {
4880
+ setNotice(result.message);
4881
+ return;
4882
+ }
4883
+ onPulled(result);
4884
+ onOpenChange(false);
4885
+ } catch (e) {
4886
+ if (e instanceof PullByKeyError && e.code === "choose_cnpj" && e.options?.length) {
4887
+ setCnpjOptions(e.options);
4888
+ setError(null);
4889
+ return;
4890
+ }
4891
+ setError(e instanceof Error ? e.message : String(e));
4892
+ } finally {
4893
+ setBusy(false);
4894
+ }
4895
+ };
4896
+ return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (o) => {
4897
+ if (!busy) onOpenChange(o);
4898
+ }, children: /* @__PURE__ */ jsxs(ModalContent, { size: "md", children: [
4899
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
4900
+ /* @__PURE__ */ jsxs(ModalTitle, { className: "flex items-center gap-2", children: [
4901
+ /* @__PURE__ */ jsx(KeyRound, { className: "h-5 w-5", "aria-hidden": true }),
4902
+ "Puxar NF-e por chave de acesso"
4903
+ ] }),
4904
+ /* @__PURE__ */ jsx(ModalDescription, { children: "Para baixar uma nota que n\xE3o veio na coleta autom\xE1tica (por exemplo, emitida antes de ligar o recebimento de DANFes). Cole a chave de acesso de 44 d\xEDgitos." })
4905
+ ] }),
4906
+ /* @__PURE__ */ jsxs(ModalBody, { className: "space-y-3", children: [
4907
+ /* @__PURE__ */ jsx(
4908
+ Input,
4909
+ {
4910
+ value: groupKey(digits2),
4911
+ onChange: (e) => {
4912
+ setValue(e.target.value);
4913
+ setError(null);
4914
+ setNotice(null);
4915
+ },
4916
+ onKeyDown: (e) => {
4917
+ if (e.key === "Enter" && !blocked && !busy) void pull();
4918
+ },
4919
+ placeholder: "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000",
4920
+ inputMode: "numeric",
4921
+ autoFocus: true,
4922
+ className: "font-mono tabular-nums",
4923
+ "aria-label": "Chave de acesso",
4924
+ "data-testid": "pull-by-key-input"
4925
+ }
4926
+ ),
4927
+ /* @__PURE__ */ jsxs("p", { className: `text-xs tabular-nums ${digits2.length === 44 ? "text-success-soft-foreground" : "text-muted-foreground"}`, children: [
4928
+ digits2.length,
4929
+ "/44 d\xEDgitos"
4930
+ ] }),
4931
+ cnpjOptions && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
4932
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "De qual CNPJ \xE9 esta nota?" }),
4933
+ /* @__PURE__ */ jsxs(Select, { value: legalEntityId, onValueChange: setLegalEntityId, children: [
4934
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Escolha o CNPJ destinat\xE1rio" }) }),
4935
+ /* @__PURE__ */ jsx(SelectContent, { children: cnpjOptions.map((o) => /* @__PURE__ */ jsxs(SelectItem, { value: o.id, children: [
4936
+ o.name,
4937
+ " \xB7 ",
4938
+ formatCnpj(o.cnpj)
4939
+ ] }, o.id)) })
4940
+ ] })
4941
+ ] }),
4942
+ error && /* @__PURE__ */ jsx("p", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", role: "alert", children: error }),
4943
+ notice && /* @__PURE__ */ jsx("p", { className: "rounded-md bg-warning-soft px-3 py-2 text-sm text-warning-soft-foreground", role: "status", children: notice }),
4944
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Puxar n\xE3o manifesta a nota na SEFAZ \u2014 a manifesta\xE7\xE3o continua sendo feita no sistema antigo." })
4945
+ ] }),
4946
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
4947
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: busy, children: "Cancelar" }),
4948
+ /* @__PURE__ */ jsxs(Button, { onClick: () => {
4949
+ void pull();
4950
+ }, disabled: busy || !!blocked, "data-testid": "pull-by-key-submit", children: [
4951
+ busy ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(KeyRound, { className: "mr-1.5 h-4 w-4" }),
4952
+ busy ? "Buscando na SEFAZ\u2026" : blocked ?? "Puxar nota"
4953
+ ] })
4954
+ ] })
4955
+ ] }) });
4956
+ }
4534
4957
  var money = (value) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(value || 0);
4535
4958
  var day = (iso) => iso ? iso.slice(0, 10).split("-").reverse().join("/") : "\u2014";
4536
4959
  var cnpj = (digits2) => {
@@ -4554,6 +4977,7 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4554
4977
  const [busy, setBusy] = useState(false);
4555
4978
  const [report, setReport] = useState(null);
4556
4979
  const [dragging, setDragging] = useState(false);
4980
+ const [pullOpen, setPullOpen] = useState(false);
4557
4981
  const fileInput = useRef(null);
4558
4982
  const unidades = useUnitFilter({ scope: "list" });
4559
4983
  const { draft, applied, patch, setDraft, dirty, apply, reset } = useFilterDraft({
@@ -4614,6 +5038,36 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4614
5038
  setReport(next);
4615
5039
  await reload();
4616
5040
  }, [reload]);
5041
+ const [rowError, setRowError] = useState(null);
5042
+ const downloadXml = useCallback(async (note) => {
5043
+ setRowError(null);
5044
+ try {
5045
+ const xml = await getInboxNoteXml(note.id);
5046
+ if (!xml) {
5047
+ setRowError(`O XML da nota ${note.number} n\xE3o est\xE1 guardado.`);
5048
+ return;
5049
+ }
5050
+ const url = URL.createObjectURL(new Blob([xml], { type: "application/xml" }));
5051
+ const a = document.createElement("a");
5052
+ a.href = url;
5053
+ a.download = `${note.accessKey || note.number || "nfe"}.xml`;
5054
+ a.click();
5055
+ URL.revokeObjectURL(url);
5056
+ } catch (e) {
5057
+ setRowError(e instanceof Error ? e.message : String(e));
5058
+ }
5059
+ }, []);
5060
+ const openPdf = useCallback(async (note) => {
5061
+ setRowError(null);
5062
+ const tab = window.open("", "_blank");
5063
+ try {
5064
+ const pdf = await getInboxNotePdf(note.accessKey ?? "");
5065
+ if (tab) tab.location.href = URL.createObjectURL(pdf);
5066
+ } catch (e) {
5067
+ tab?.close();
5068
+ setRowError(e instanceof Error ? e.message : String(e));
5069
+ }
5070
+ }, []);
4617
5071
  const remove = useCallback(async (note) => {
4618
5072
  await rejectNote(note.id, "Recusada na lista de entrada");
4619
5073
  await reload();
@@ -4653,17 +5107,12 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4653
5107
  id: "supplier",
4654
5108
  header: "Fornecedor",
4655
5109
  accessorFn: (note) => note.supplierName,
5110
+ meta: { truncate: true },
4656
5111
  cell: ({ row }) => /* @__PURE__ */ jsxs("div", { children: [
4657
5112
  /* @__PURE__ */ jsx("p", { className: "font-medium leading-tight", children: row.original.supplierName || "\u2014" }),
4658
5113
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground tabular-nums", children: cnpj(row.original.supplierCnpj) })
4659
5114
  ] })
4660
5115
  },
4661
- {
4662
- id: "items",
4663
- header: () => /* @__PURE__ */ jsx("span", { className: "block text-right", children: "Itens" }),
4664
- accessorFn: (note) => note.itemCount,
4665
- cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "block text-right tabular-nums text-muted-foreground", children: row.original.itemCount })
4666
- },
4667
5116
  {
4668
5117
  id: "reconciled",
4669
5118
  header: () => /* @__PURE__ */ jsx("span", { className: "block text-center", children: "Concilia\xE7\xE3o" }),
@@ -4697,24 +5146,44 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4697
5146
  enableSorting: false,
4698
5147
  cell: ({ row }) => {
4699
5148
  const note = row.original;
4700
- return /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1", children: [
4701
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: note.stockedAt ? "ghost" : "outline", onClick: () => onOpenNote(note.id), children: note.stockedAt ? "Ver" : "Conciliar" }),
4702
- !note.stockedAt && !note.invoiceId && /* @__PURE__ */ jsx(
4703
- Button,
4704
- {
4705
- size: "sm",
4706
- variant: "ghost",
4707
- "aria-label": `Recusar a nota ${note.number}`,
4708
- onClick: () => {
4709
- void remove(note);
4710
- },
4711
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
4712
- }
4713
- )
4714
- ] });
5149
+ const done = !!note.stockedAt || note.status === "rejected";
5150
+ return (
5151
+ // The row itself opens the note; the buttons must not fire it a second time.
5152
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1", onClick: (e) => e.stopPropagation(), children: [
5153
+ /* @__PURE__ */ jsx(ActionIcon, { label: done ? "Ver nota" : "Conciliar", children: /* @__PURE__ */ jsx(
5154
+ Button,
5155
+ {
5156
+ size: "icon",
5157
+ variant: "ghost",
5158
+ "aria-label": done ? `Ver a nota ${note.number}` : `Conciliar a nota ${note.number}`,
5159
+ className: done ? "" : "bg-foreground text-background hover:bg-foreground/85 hover:text-background",
5160
+ onClick: () => onOpenNote(note.id),
5161
+ children: done ? /* @__PURE__ */ jsx(Eye, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ListChecks, { className: "h-4 w-4" })
5162
+ }
5163
+ ) }),
5164
+ /* @__PURE__ */ jsx(
5165
+ RowActions,
5166
+ {
5167
+ inlineLimit: 0,
5168
+ actions: [
5169
+ { id: "xml", label: "Baixar XML", icon: FileCode2, onSelect: () => downloadXml(note) },
5170
+ { id: "pdf", label: "Abrir PDF", icon: FileText, onSelect: () => openPdf(note), hidden: !note.accessKey },
5171
+ {
5172
+ id: "reject",
5173
+ label: "Recusar nota",
5174
+ icon: Trash2,
5175
+ intent: "danger",
5176
+ onSelect: () => remove(note),
5177
+ hidden: done || !!note.invoiceId
5178
+ }
5179
+ ]
5180
+ }
5181
+ )
5182
+ ] })
5183
+ );
4715
5184
  }
4716
5185
  }
4717
- ], [onOpenNote, remove]);
5186
+ ], [onOpenNote, remove, downloadXml, openPdf]);
4718
5187
  const emptyMessage = query || activeCount > 0 ? /* @__PURE__ */ jsxs("div", { children: [
4719
5188
  /* @__PURE__ */ jsx(Inbox, { className: "mx-auto h-8 w-8 text-muted-foreground", "aria-hidden": true }),
4720
5189
  /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm font-medium", children: "Nenhuma nota encontrada" }),
@@ -4730,6 +5199,10 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4730
5199
  /* @__PURE__ */ jsx(Inbox, { className: "h-4 w-4 mr-1.5" }),
4731
5200
  "Fila da SEFAZ"
4732
5201
  ] }),
5202
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setPullOpen(true), "data-testid": "pull-by-key", children: [
5203
+ /* @__PURE__ */ jsx(KeyRound, { className: "h-4 w-4 mr-1.5" }),
5204
+ "Puxar por chave"
5205
+ ] }),
4733
5206
  /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => fileInput.current?.click(), disabled: busy, children: [
4734
5207
  busy ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 mr-1.5 animate-spin" }) : /* @__PURE__ */ jsx(UploadCloud, { className: "h-4 w-4 mr-1.5" }),
4735
5208
  "Importar XML"
@@ -4770,6 +5243,21 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4770
5243
  ]
4771
5244
  }
4772
5245
  ),
5246
+ /* @__PURE__ */ jsx(
5247
+ PullByKeyDialog,
5248
+ {
5249
+ open: pullOpen,
5250
+ onOpenChange: setPullOpen,
5251
+ unitId: applied.unitId || null,
5252
+ onPulled: (r) => {
5253
+ setReport({
5254
+ ok: [{ file: "chave", number: r.parsed.nfeNumber, items: r.itemCount, matched: r.matchedCount, repeated: r.alreadyAccepted }],
5255
+ failed: []
5256
+ });
5257
+ void reload();
5258
+ }
5259
+ }
5260
+ ),
4773
5261
  report && /* @__PURE__ */ jsxs(Card, { className: "p-4 space-y-2", children: [
4774
5262
  report.ok.map((r) => /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
4775
5263
  /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
@@ -4822,6 +5310,7 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4822
5310
  ]
4823
5311
  }
4824
5312
  ),
5313
+ rowError && /* @__PURE__ */ jsx("p", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", role: "alert", children: rowError }),
4825
5314
  /* @__PURE__ */ jsxs(Card, { className: "overflow-hidden", children: [
4826
5315
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 border-b border-border px-3 py-2", children: /* @__PURE__ */ jsx("div", { className: "min-w-[10rem] flex-1", children: /* @__PURE__ */ jsx(
4827
5316
  SearchField,
@@ -4832,7 +5321,7 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4832
5321
  "data-testid": "inbound-notes-search"
4833
5322
  }
4834
5323
  ) }) }),
4835
- /* @__PURE__ */ jsx(
5324
+ /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsx(
4836
5325
  DataTable,
4837
5326
  {
4838
5327
  variant: "bare",
@@ -4840,12 +5329,19 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
4840
5329
  data: loading ? [] : shown,
4841
5330
  loading,
4842
5331
  getRowId: (note) => note.id,
5332
+ onRowClick: (note) => onOpenNote(note.id),
4843
5333
  emptyMessage
4844
5334
  }
4845
- )
5335
+ ) })
4846
5336
  ] })
4847
5337
  ] });
4848
5338
  }
5339
+ function ActionIcon({ label, children }) {
5340
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
5341
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
5342
+ /* @__PURE__ */ jsx(TooltipContent, { children: label })
5343
+ ] });
5344
+ }
4849
5345
  function ReconcileBadge({ done, total }) {
4850
5346
  const full = total > 0 && done === total;
4851
5347
  return /* @__PURE__ */ jsxs(
@@ -4869,9 +5365,185 @@ function StockBadge({ stockedAt, status }) {
4869
5365
  "Estocado"
4870
5366
  ] }) : /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-warning-soft px-2 py-0.5 text-[11px] font-medium text-warning-soft-foreground", children: "Pendente" });
4871
5367
  }
5368
+ var MAX_BYTES = 15 * 1024 * 1024;
5369
+ var money2 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(v || 0);
5370
+ var day2 = (iso) => iso ? iso.slice(0, 10).split("-").reverse().join("/") : "\u2014";
5371
+ var size = (b) => !b ? "" : b < 1024 * 1024 ? `${Math.max(1, Math.round(b / 1024))} KB` : `${(b / 1024 / 1024).toFixed(1)} MB`;
5372
+ function applyPaymentCode(list, code) {
5373
+ const field = code.kind === "pix" ? "pixKey" : "barcode";
5374
+ const free = list.map((p, i) => ({ p, i })).filter(({ p }) => !p[field]);
5375
+ const sameAmount = code.amount != null ? free.find(({ p }) => Math.abs((Number(p.amount) || 0) - code.amount) < 0.01) : void 0;
5376
+ const target = sameAmount ?? free[0];
5377
+ const patch = (p) => ({
5378
+ ...p,
5379
+ [field]: code.code,
5380
+ paymentType: code.kind === "pix" ? "pix" : "boleto",
5381
+ dueDate: p.dueDate || code.dueDate,
5382
+ amount: Number(p.amount) > 0 ? p.amount : code.amount ?? 0
5383
+ });
5384
+ if (target) return { next: list.map((p, i) => i === target.i ? patch(p) : p), index: target.i };
5385
+ const fresh = patch({ index: list.length, amount: 0 });
5386
+ return { next: [...list, fresh], index: list.length };
5387
+ }
5388
+ function NoteAttachmentsCard({ noteId, invoiceId, readOnly, instalments, onInstalments }) {
5389
+ const input = useRef(null);
5390
+ const [items, setItems] = useState(null);
5391
+ const [busy, setBusy] = useState(null);
5392
+ const [over, setOver] = useState(false);
5393
+ const [confirming, setConfirming] = useState(null);
5394
+ const [messages, setMessages] = useState([]);
5395
+ const current = useRef(instalments);
5396
+ current.current = instalments;
5397
+ const load = useCallback(async () => {
5398
+ try {
5399
+ setItems(await listNoteAttachments(noteId, invoiceId));
5400
+ } catch {
5401
+ setItems([]);
5402
+ }
5403
+ }, [noteId, invoiceId]);
5404
+ useEffect(() => {
5405
+ void load();
5406
+ }, [load]);
5407
+ const upload = async (files) => {
5408
+ const out = [];
5409
+ for (const file of files) {
5410
+ if (!(file.type.startsWith("image/") || file.type === "application/pdf") || file.size > MAX_BYTES) {
5411
+ out.push({ tone: "error", text: `${file.name}: envie imagem ou PDF de at\xE9 15 MB.` });
5412
+ continue;
5413
+ }
5414
+ setBusy(`Anexando ${file.name}\u2026`);
5415
+ try {
5416
+ await addNoteAttachment(noteId, invoiceId, file);
5417
+ } catch (e) {
5418
+ out.push({ tone: "error", text: `${file.name}: ${e instanceof Error ? e.message : String(e)}` });
5419
+ continue;
5420
+ }
5421
+ if (readOnly) continue;
5422
+ setBusy(`Lendo ${file.name}\u2026`);
5423
+ try {
5424
+ const code = await extractPaymentCode(file);
5425
+ if (!code) {
5426
+ out.push({ tone: "warn", text: `${file.name}: anexado. N\xE3o achei c\xF3digo de boleto nem Pix nele.` });
5427
+ } else {
5428
+ const { next, index } = applyPaymentCode(current.current, code);
5429
+ current.current = next;
5430
+ onInstalments(next);
5431
+ const p = next[index];
5432
+ out.push({
5433
+ tone: "ok",
5434
+ text: `${code.kind === "pix" ? "Pix" : "Boleto"} lido \u2192 parcela ${index + 1}: ${money2(Number(p.amount) || 0)}, vence ${day2(p.dueDate ?? null)}. Confira e salve.`
5435
+ });
5436
+ }
5437
+ } catch (e) {
5438
+ out.push({ tone: "warn", text: `${file.name}: anexado, mas n\xE3o consegui ler o c\xF3digo (${e instanceof Error ? e.message : String(e)}).` });
5439
+ }
5440
+ }
5441
+ setBusy(null);
5442
+ setMessages(out);
5443
+ await load();
5444
+ };
5445
+ const remove = async (a) => {
5446
+ setBusy(`Removendo ${a.fileName}\u2026`);
5447
+ try {
5448
+ await removeNoteAttachment(a.id);
5449
+ setMessages([{ tone: "ok", text: `${a.fileName} removido.` }]);
5450
+ } catch (e) {
5451
+ setMessages([{ tone: "error", text: `${a.fileName}: ${e instanceof Error ? e.message : String(e)}` }]);
5452
+ } finally {
5453
+ setBusy(null);
5454
+ await load();
5455
+ }
5456
+ };
5457
+ return /* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
5458
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
5459
+ /* @__PURE__ */ jsxs("div", { children: [
5460
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Anexos da conta" }),
5461
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Boleto, comprovante, foto da nota. Boleto e Pix anexados s\xE3o lidos e preenchem a parcela." })
5462
+ ] }),
5463
+ items && items.length > 0 && /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
5464
+ items.length,
5465
+ " ",
5466
+ items.length === 1 ? "anexo" : "anexos"
5467
+ ] })
5468
+ ] }),
5469
+ items === null ? /* @__PURE__ */ jsx("div", { className: "mt-3 h-10 animate-pulse rounded-md bg-muted" }) : items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-3 divide-y rounded-md border", children: items.map((a) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2 px-3 py-2 text-sm", children: [
5470
+ a.mimeType?.startsWith("image/") ? /* @__PURE__ */ jsx(Image, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true }) : /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
5471
+ a.url ? /* @__PURE__ */ jsx("a", { href: a.url, target: "_blank", rel: "noreferrer", className: "min-w-0 flex-1 truncate hover:underline", children: a.fileName }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-muted-foreground", children: a.fileName }),
5472
+ confirming === a.id ? (
5473
+ // Inline, not a dialog: this card already lives inside a modal.
5474
+ /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [
5475
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Remover?" }),
5476
+ /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: () => {
5477
+ setConfirming(null);
5478
+ void remove(a);
5479
+ }, children: "Remover" }),
5480
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setConfirming(null), children: "Cancelar" })
5481
+ ] })
5482
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
5483
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: size(a.fileSize) }),
5484
+ /* @__PURE__ */ jsx(Hint, { label: "Remover anexo", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": `Remover ${a.fileName}`, disabled: !!busy, onClick: () => setConfirming(a.id), children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" }) }) })
5485
+ ] })
5486
+ ] }, a.id)) }),
5487
+ /* @__PURE__ */ jsxs(
5488
+ "div",
5489
+ {
5490
+ className: `mt-3 flex flex-col items-center gap-1 rounded-md border border-dashed px-4 py-5 text-center transition-colors ${over ? "border-primary bg-primary/5" : "border-input"}`,
5491
+ onDragOver: (e) => {
5492
+ e.preventDefault();
5493
+ setOver(true);
5494
+ },
5495
+ onDragLeave: () => setOver(false),
5496
+ onDrop: (e) => {
5497
+ e.preventDefault();
5498
+ setOver(false);
5499
+ void upload(Array.from(e.dataTransfer.files));
5500
+ },
5501
+ children: [
5502
+ busy ? /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
5503
+ /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
5504
+ busy
5505
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5506
+ /* @__PURE__ */ jsx(UploadCloud, { className: "h-6 w-6 text-muted-foreground", "aria-hidden": true }),
5507
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: "Arraste o boleto ou o comprovante aqui" }),
5508
+ /* @__PURE__ */ jsx("div", { className: "mt-1 flex gap-2", children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => input.current?.click(), "data-testid": "note-attach", children: [
5509
+ /* @__PURE__ */ jsx(Paperclip, { className: "mr-1.5 h-4 w-4" }),
5510
+ "Anexar arquivo"
5511
+ ] }) }),
5512
+ !readOnly && /* @__PURE__ */ jsxs("p", { className: "mt-1 flex items-center gap-1 text-[11px] text-muted-foreground", children: [
5513
+ /* @__PURE__ */ jsx(ScanLine, { className: "h-3 w-3", "aria-hidden": true }),
5514
+ " o c\xF3digo de barras e o Pix s\xE3o lidos sozinhos"
5515
+ ] })
5516
+ ] }),
5517
+ /* @__PURE__ */ jsx(
5518
+ "input",
5519
+ {
5520
+ ref: input,
5521
+ type: "file",
5522
+ multiple: true,
5523
+ accept: "image/*,application/pdf",
5524
+ className: "hidden",
5525
+ onChange: (e) => {
5526
+ void upload(Array.from(e.target.files ?? []));
5527
+ e.target.value = "";
5528
+ }
5529
+ }
5530
+ )
5531
+ ]
5532
+ }
5533
+ ),
5534
+ messages.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-3 space-y-1", children: messages.map((m, i) => /* @__PURE__ */ jsx(
5535
+ "li",
5536
+ {
5537
+ className: `text-xs ${m.tone === "ok" ? "text-success-soft-foreground" : m.tone === "warn" ? "text-warning-soft-foreground" : "text-destructive"}`,
5538
+ children: m.text
5539
+ },
5540
+ i
5541
+ )) })
5542
+ ] });
5543
+ }
4872
5544
 
4873
5545
  // src/types.ts
4874
- var STOCK_BEARING_ENTRY_TYPES = ["insumo", "revenda", "patrimonio"];
5546
+ var STOCK_BEARING_ENTRY_TYPES = ["insumo", "revenda", "patrimonio", "uso_consumo"];
4875
5547
  var isStockBearing = (type) => STOCK_BEARING_ENTRY_TYPES.includes(type);
4876
5548
 
4877
5549
  // src/lib/note-suggestion.ts
@@ -4891,13 +5563,14 @@ var norm = (u) => (u ?? "").trim().toUpperCase().replace(/\.$/, "");
4891
5563
  function packagingCount(commercialUnit) {
4892
5564
  return PACKAGING_COUNT[norm(commercialUnit)] ?? null;
4893
5565
  }
4894
- var money2 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(v || 0);
5566
+ var money3 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(v || 0);
4895
5567
  var qty = (v) => new Intl.NumberFormat("pt-BR", { maximumFractionDigits: 4 }).format(v || 0);
4896
- var day2 = (iso) => iso ? iso.slice(0, 10).split("-").reverse().join("/") : "\u2014";
5568
+ var day3 = (iso) => iso ? iso.slice(0, 10).split("-").reverse().join("/") : "\u2014";
4897
5569
  var ENTRY_TYPES = [
4898
5570
  { value: "insumo", label: "Insumo" },
4899
5571
  { value: "revenda", label: "Produto Revenda" },
4900
5572
  { value: "patrimonio", label: "Patrim\xF4nio" },
5573
+ { value: "uso_consumo", label: "Uso e Consumo", hint: "com estoque" },
4901
5574
  { value: "despesa", label: "Despesa", hint: "n\xE3o vira estoque" },
4902
5575
  { value: "servico", label: "Servi\xE7o", hint: "n\xE3o vira estoque" }
4903
5576
  ];
@@ -4945,7 +5618,11 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
4945
5618
  const load = useCallback(async () => {
4946
5619
  setLoading(true);
4947
5620
  try {
4948
- const detail = await getInboxNote(noteId);
5621
+ let detail = await getInboxNote(noteId);
5622
+ if (detail && !detail.supplierPartyId && detail.supplierCnpj) {
5623
+ const linked = await linkInboxSupplier(noteId).catch(() => null);
5624
+ if (linked?.supplierPartyId) detail = { ...detail, supplierPartyId: linked.supplierPartyId };
5625
+ }
4949
5626
  setNote(detail);
4950
5627
  const failed = [];
4951
5628
  const [u, a, l] = await Promise.all([
@@ -5121,18 +5798,54 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5121
5798
  const moldura = (node, titulo, rodape2) => emModal ? /* @__PURE__ */ jsxs(Fragment, { children: [
5122
5799
  /* @__PURE__ */ jsxs(ModalHeader, { className: "shrink-0 flex-row items-center justify-between gap-3 space-y-0 border-b px-6 py-3 pr-14 text-left sm:flex-row", children: [
5123
5800
  /* @__PURE__ */ jsx(ModalTitle, { className: "min-w-0 truncate", children: titulo }),
5124
- botaoIA
5801
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
5802
+ botaoIA,
5803
+ ferramentas
5804
+ ] })
5125
5805
  ] }),
5126
5806
  /* @__PURE__ */ jsx(ModalBody, { className: "px-6 py-4", children: node }),
5127
- rodape2 && /* @__PURE__ */ jsx(ModalFooter, { className: "shrink-0 flex-row items-center justify-between gap-3 space-x-0 border-t px-6 py-3 pr-16", children: rodape2 })
5807
+ rodape2 && /* @__PURE__ */ jsx(ModalFooter, { variant: "bar", start: rodape2.start, children: rodape2.end })
5128
5808
  ] }) : (
5129
5809
  // Em tela cheia não há moldura onde pendurar o rodapé: ele volta para dentro
5130
5810
  // do conteúdo, grudado na base da janela enquanto a página rola.
5131
5811
  /* @__PURE__ */ jsxs(Fragment, { children: [
5132
5812
  node,
5133
- rodape2 && /* @__PURE__ */ jsx("div", { className: `sticky bottom-0 ${sangria} flex items-center justify-between gap-3 border-t bg-card/95 py-3 pr-16 backdrop-blur`, children: rodape2 })
5813
+ rodape2 && /* @__PURE__ */ jsxs("div", { className: `sticky bottom-0 ${sangria} flex items-center justify-between gap-3 border-t bg-card/95 py-3 pr-16 backdrop-blur`, children: [
5814
+ /* @__PURE__ */ jsx("div", { className: "flex min-w-0 items-center gap-3", children: rodape2.start }),
5815
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: rodape2.end })
5816
+ ] })
5134
5817
  ] })
5135
5818
  );
5819
+ const [cadastrando, setCadastrando] = useState(false);
5820
+ const cadastrarFornecedor = useCallback(async () => {
5821
+ if (!note) return;
5822
+ setCadastrando(true);
5823
+ setError(null);
5824
+ try {
5825
+ const { supplierPartyId } = await linkInboxSupplier(note.id, true);
5826
+ if (supplierPartyId) setNote({ ...note, supplierPartyId });
5827
+ } catch (e) {
5828
+ setError(e instanceof Error ? e.message : String(e));
5829
+ } finally {
5830
+ setCadastrando(false);
5831
+ }
5832
+ }, [note]);
5833
+ const [abrindoPdf, setAbrindoPdf] = useState(false);
5834
+ const abrirPdf = useCallback(async () => {
5835
+ if (!note?.accessKey) return;
5836
+ const tab = window.open("", "_blank");
5837
+ setAbrindoPdf(true);
5838
+ setError(null);
5839
+ try {
5840
+ const pdf = await getInboxNotePdf(note.accessKey);
5841
+ if (tab) tab.location.href = URL.createObjectURL(pdf);
5842
+ } catch (e) {
5843
+ tab?.close();
5844
+ setError(e instanceof Error ? e.message : String(e));
5845
+ } finally {
5846
+ setAbrindoPdf(false);
5847
+ }
5848
+ }, [note]);
5136
5849
  const baixarXml = useCallback(async () => {
5137
5850
  if (!note) return;
5138
5851
  setBaixandoXml(true);
@@ -5155,6 +5868,32 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5155
5868
  setBaixandoXml(false);
5156
5869
  }
5157
5870
  }, [note]);
5871
+ const ferramentas = note ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
5872
+ /* @__PURE__ */ jsxs(Dropdown, { children: [
5873
+ /* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: baixandoXml || abrindoPdf, children: [
5874
+ baixandoXml || abrindoPdf ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Download, { className: "mr-1.5 h-4 w-4" }),
5875
+ "Baixar",
5876
+ /* @__PURE__ */ jsx(ChevronDown, { className: "ml-1 h-3.5 w-3.5 opacity-60" })
5877
+ ] }) }),
5878
+ /* @__PURE__ */ jsxs(DropdownContent, { align: "end", children: [
5879
+ /* @__PURE__ */ jsxs(DropdownItem, { className: "gap-2", disabled: !note.hasXml, onSelect: () => {
5880
+ void baixarXml();
5881
+ }, children: [
5882
+ /* @__PURE__ */ jsx(FileCode2, { className: "h-4 w-4" }),
5883
+ note.hasXml ? "XML da nota" : "XML n\xE3o guardado \u2014 reimporte o .xml"
5884
+ ] }),
5885
+ /* @__PURE__ */ jsxs(DropdownItem, { className: "gap-2", disabled: !note.accessKey, onSelect: () => {
5886
+ void abrirPdf();
5887
+ }, children: [
5888
+ /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4" }),
5889
+ "DANFE (PDF)"
5890
+ ] })
5891
+ ] })
5892
+ ] }),
5893
+ /* @__PURE__ */ jsx(Hint, { label: "Atualizar", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Atualizar a nota", onClick: () => {
5894
+ void load();
5895
+ }, children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" }) }) })
5896
+ ] }) : null;
5158
5897
  const save = async () => {
5159
5898
  if (!note) return;
5160
5899
  setSaving(true);
@@ -5186,7 +5925,10 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5186
5925
  defaultLocationId: location,
5187
5926
  instalments
5188
5927
  });
5189
- setAccepted(await acceptNote(note.id, location));
5928
+ const result = await acceptNote(note.id, location);
5929
+ if (result.invoiceId) await moveNoteAttachmentsToInvoice(note.id, result.invoiceId).catch(() => {
5930
+ });
5931
+ setAccepted(result);
5190
5932
  await load();
5191
5933
  } catch (e) {
5192
5934
  setError(e instanceof Error ? e.message : String(e));
@@ -5231,61 +5973,68 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5231
5973
  ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Nota n\xE3o encontrada." }) })
5232
5974
  ] }), "Concilia\xE7\xE3o");
5233
5975
  }
5234
- const rodape = readOnly ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
5235
- /* @__PURE__ */ jsxs(
5236
- Button,
5237
- {
5238
- variant: "ghost",
5239
- onClick: async () => {
5240
- await rejectNote(note.id);
5241
- onBack();
5242
- },
5243
- disabled: saving || accepting,
5244
- children: [
5245
- /* @__PURE__ */ jsx(X, { className: "mr-1.5 h-4 w-4" }),
5246
- "Rejeitar"
5247
- ]
5248
- }
5249
- ),
5250
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5251
- (missingType > 0 || missingProduct > 0) && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
5252
- unverifiedFactor > 0 && `${unverifiedFactor} com fator por conferir`,
5253
- unverifiedFactor > 0 && (missingType > 0 || missingProduct > 0) && " \xB7 ",
5254
- missingType > 0 && `${missingType} sem tipo`,
5255
- missingType > 0 && missingProduct > 0 && " \xB7 ",
5256
- missingProduct > 0 && `${missingProduct} sem produto`
5257
- ] }),
5258
- /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: () => {
5976
+ const pendencias = [
5977
+ unverifiedFactor > 0 && `${unverifiedFactor} com fator por conferir`,
5978
+ missingType > 0 && `${missingType} sem tipo`,
5979
+ missingProduct > 0 && `${missingProduct} sem produto`
5980
+ ].filter(Boolean).join(" \xB7 ");
5981
+ const rodape = readOnly ? null : {
5982
+ start: /* @__PURE__ */ jsxs(Fragment, { children: [
5983
+ /* @__PURE__ */ jsxs(
5984
+ Button,
5985
+ {
5986
+ variant: "ghost",
5987
+ size: "sm",
5988
+ className: "text-destructive hover:bg-destructive/10 hover:text-destructive",
5989
+ onClick: async () => {
5990
+ await rejectNote(note.id);
5991
+ onBack();
5992
+ },
5993
+ disabled: saving || accepting,
5994
+ children: [
5995
+ /* @__PURE__ */ jsx(X, { className: "mr-1.5 h-3.5 w-3.5" }),
5996
+ "Rejeitar"
5997
+ ]
5998
+ }
5999
+ ),
6000
+ pendencias && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: pendencias })
6001
+ ] }),
6002
+ end: /* @__PURE__ */ jsxs(Fragment, { children: [
6003
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => {
5259
6004
  void save();
5260
6005
  }, disabled: saving || accepting, children: [
5261
- saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }),
6006
+ saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
5262
6007
  "Salvar concilia\xE7\xE3o"
5263
6008
  ] }),
5264
6009
  /* @__PURE__ */ jsxs(
5265
6010
  Button,
5266
6011
  {
6012
+ size: "sm",
5267
6013
  onClick: () => {
5268
6014
  void accept();
5269
6015
  },
5270
6016
  disabled: accepting || saving || missingType > 0 || missingProduct > 0 || unverifiedFactor > 0,
5271
6017
  children: [
5272
- accepting ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Check, { className: "mr-1.5 h-4 w-4" }),
6018
+ accepting ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Check, { className: "mr-1.5 h-3.5 w-3.5" }),
5273
6019
  "Aceitar"
5274
6020
  ]
5275
6021
  }
5276
6022
  )
5277
6023
  ] })
5278
- ] });
6024
+ };
5279
6025
  return moldura(/* @__PURE__ */ jsxs("div", { className: `space-y-4 ${emModal ? "" : "pb-24"}`, children: [
5280
6026
  cabecalho(`Concilia\xE7\xE3o \xB7 NF-e ${note.number}`),
5281
6027
  accepted && /* @__PURE__ */ jsxs(Card, { className: "border-success bg-success-soft p-4", children: [
5282
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-success-soft-foreground", children: accepted.alreadyAccepted ? "Esta nota j\xE1 havia sido aceita \u2014 nada foi lan\xE7ado de novo." : `Aceita: ${accepted.stockLines} ${accepted.stockLines === 1 ? "linha" : "linhas"} de estoque e um t\xEDtulo de ${money2(accepted.invoiceTotal)} em ${accepted.instalments} ${accepted.instalments === 1 ? "parcela" : "parcelas"}.` }),
6028
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-success-soft-foreground", children: accepted.alreadyAccepted ? "Esta nota j\xE1 havia sido aceita \u2014 nada foi lan\xE7ado de novo." : `Aceita: ${accepted.stockLines} ${accepted.stockLines === 1 ? "linha" : "linhas"} de estoque e um t\xEDtulo de ${money3(accepted.invoiceTotal)} em ${accepted.instalments} ${accepted.instalments === 1 ? "parcela" : "parcelas"}.` }),
5283
6029
  accepted.invoiceId && onOpenInvoice && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-2", onClick: () => onOpenInvoice(accepted.invoiceId), children: "Abrir a conta a pagar" })
5284
6030
  ] }),
5285
6031
  /* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
5286
6032
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Situa\xE7\xE3o fiscal" }),
5287
6033
  /* @__PURE__ */ jsxs("dl", { className: "mt-3 grid gap-x-6 gap-y-2 text-sm sm:grid-cols-2 lg:grid-cols-3", children: [
5288
- /* @__PURE__ */ jsx(Field, { label: "Fornecedor", value: note.supplierName || "\u2014" }),
6034
+ /* @__PURE__ */ jsxs("div", { children: [
6035
+ /* @__PURE__ */ jsx("dt", { className: "text-xs text-muted-foreground", children: "Fornecedor" }),
6036
+ /* @__PURE__ */ jsx("dd", { className: "text-sm", children: note.supplierPartyId ? /* @__PURE__ */ jsx(PersonLink, { personId: note.supplierPartyId, name: note.supplierName || "\u2014", kind: "supplier" }) : note.supplierName || "\u2014" })
6037
+ ] }),
5289
6038
  /* @__PURE__ */ jsx(Field, { label: "CNPJ", value: note.supplierCnpj ?? "\u2014", mono: true }),
5290
6039
  /* @__PURE__ */ jsx(Field, { label: "Natureza da opera\xE7\xE3o", value: note.natureOperation ?? "\u2014" }),
5291
6040
  /* @__PURE__ */ jsx(
@@ -5295,15 +6044,22 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5295
6044
  value: `${note.manifestationStatus ?? "n\xE3o manifestada"}${note.manifestationAttempts ? ` \xB7 ${note.manifestationAttempts} tentativa(s)` : ""}`
5296
6045
  }
5297
6046
  ),
5298
- /* @__PURE__ */ jsx(Field, { label: "Manifestada em", value: day2(note.manifestedAt) }),
5299
- /* @__PURE__ */ jsx(Field, { label: "Recebida em", value: day2(note.receivedAt) })
6047
+ /* @__PURE__ */ jsx(Field, { label: "Manifestada em", value: day3(note.manifestedAt) }),
6048
+ /* @__PURE__ */ jsx(Field, { label: "Recebida em", value: day3(note.receivedAt) })
6049
+ ] }),
6050
+ !note.supplierPartyId && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center justify-between gap-2 rounded-md border border-input bg-muted/40 px-3 py-2", children: [
6051
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: note.supplierCnpj ? /* @__PURE__ */ jsx(Fragment, { children: "Este fornecedor ainda n\xE3o est\xE1 no cadastro. Ao aceitar, ele \xE9 cadastrado com o nome e o CNPJ da nota." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6052
+ "A nota n\xE3o traz o CNPJ do fornecedor, ent\xE3o a conta a pagar vai nascer ",
6053
+ /* @__PURE__ */ jsx("strong", { children: "sem fornecedor" }),
6054
+ "."
6055
+ ] }) }),
6056
+ note.supplierCnpj && !readOnly && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: cadastrando, onClick: () => {
6057
+ void cadastrarFornecedor();
6058
+ }, children: [
6059
+ cadastrando ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(UserPlus, { className: "mr-1.5 h-4 w-4" }),
6060
+ "Cadastrar agora"
6061
+ ] })
5300
6062
  ] }),
5301
- !note.supplierPartyId && /* @__PURE__ */ jsx("div", { className: "mt-3 rounded-md border border-warning bg-warning-soft px-3 py-2", children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-warning-soft-foreground", children: [
5302
- "Este fornecedor n\xE3o est\xE1 no cadastro de pessoas, ent\xE3o o t\xEDtulo a pagar vai nascer",
5303
- " ",
5304
- /* @__PURE__ */ jsx("strong", { children: "sem contraparte" }),
5305
- ". Cadastre o CNPJ antes de aceitar para a conta saber a quem se deve."
5306
- ] }) }),
5307
6063
  /bonific|brinde|doacao|doação/i.test(note.natureOperation ?? "") && /* @__PURE__ */ jsx("div", { className: "mt-3 rounded-md border border-dashed border-input px-3 py-2", children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
5308
6064
  /* @__PURE__ */ jsx(Gift, { className: "mr-1 inline h-3.5 w-3.5", "aria-hidden": true }),
5309
6065
  "A natureza desta nota \xE9 ",
@@ -5315,41 +6071,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5315
6071
  " ",
5316
6072
  /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-1.5 py-0.5 text-[10px]", children: "Em breve" })
5317
6073
  ] }) }),
5318
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [
5319
- /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => {
5320
- void load();
5321
- }, children: [
5322
- /* @__PURE__ */ jsx(RefreshCw, { className: "mr-1.5 h-4 w-4" }),
5323
- "Atualizar status"
5324
- ] }),
5325
- note.hasXml ? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: baixandoXml, onClick: () => {
5326
- void baixarXml();
5327
- }, children: [
5328
- baixandoXml ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(FileDown, { className: "mr-1.5 h-4 w-4" }),
5329
- "Baixar XML"
5330
- ] }) : /* @__PURE__ */ jsx(
5331
- EmBreve,
5332
- {
5333
- label: "Baixar XML",
5334
- o_que: "O arquivo desta nota n\xE3o foi guardado \u2014 ela entrou antes de o est\xE1gio passar a gravar o XML. Reimportar o .xml preenche"
5335
- }
5336
- ),
5337
- /* @__PURE__ */ jsx(
5338
- EmBreve,
5339
- {
5340
- label: "DANFE (PDF)",
5341
- o_que: "O espelho impresso da nota. Tem layout fixado pela SEFAZ e vinha renderizado da PlugNotas no V1 \u2014 n\xE3o sai do XML sozinho"
5342
- }
5343
- ),
5344
- /* @__PURE__ */ jsx(
5345
- EmBreve,
5346
- {
5347
- label: "Cadastrar fornecedor",
5348
- o_que: "Criar a pessoa do fornecedor aqui, sem sair da nota \u2014 \xE9 o que preenche a contraparte do t\xEDtulo",
5349
- quantas: "a Laissa cadastrou 37 fornecedores no V1 enquanto importava"
5350
- }
5351
- )
5352
- ] })
6074
+ !emModal && /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: ferramentas })
5353
6075
  ] }),
5354
6076
  /* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
5355
6077
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Padr\xF5es desta nota" }),
@@ -5479,11 +6201,11 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5479
6201
  " ",
5480
6202
  item.unit ?? "",
5481
6203
  " \xB7 ",
5482
- money2(item.unitPrice),
6204
+ money3(item.unitPrice),
5483
6205
  typeLabel ? ` \xB7 ${typeLabel}` : " \xB7 sem tipo"
5484
6206
  ] })
5485
6207
  ] }),
5486
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm tabular-nums", children: money2(item.total) }),
6208
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm tabular-nums", children: money3(item.total) }),
5487
6209
  d.suggestedBy && !done && /* @__PURE__ */ jsx(AuthorBadge, { draft: d }),
5488
6210
  /* @__PURE__ */ jsx(ItemState, { done: done && ready, ready })
5489
6211
  ]
@@ -5499,8 +6221,8 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5499
6221
  ] }),
5500
6222
  /* @__PURE__ */ jsxs("dl", { className: "mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-xs sm:grid-cols-4", children: [
5501
6223
  /* @__PURE__ */ jsx(Field, { small: true, label: "Qtd", value: `${qty(item.quantity)} ${item.unit ?? ""}` }),
5502
- /* @__PURE__ */ jsx(Field, { small: true, label: "Vl. unit.", value: money2(item.unitPrice) }),
5503
- /* @__PURE__ */ jsx(Field, { small: true, label: "Total", value: money2(item.total) }),
6224
+ /* @__PURE__ */ jsx(Field, { small: true, label: "Vl. unit.", value: money3(item.unitPrice) }),
6225
+ /* @__PURE__ */ jsx(Field, { small: true, label: "Total", value: money3(item.total) }),
5504
6226
  /* @__PURE__ */ jsx(Field, { small: true, label: "C\xF3d", value: item.supplierProductCode || "\u2014", mono: true }),
5505
6227
  /* @__PURE__ */ jsx(Field, { small: true, label: "EAN", value: item.eanBarcode ?? "\u2014", mono: true }),
5506
6228
  /* @__PURE__ */ jsx(Field, { small: true, label: "NCM", value: item.ncm ?? "\u2014", mono: true }),
@@ -5617,7 +6339,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5617
6339
  ] }),
5618
6340
  /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between text-sm", children: [
5619
6341
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Novo custo unit\xE1rio" }),
5620
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: money2(unitCost) })
6342
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: money3(unitCost) })
5621
6343
  ] })
5622
6344
  ] }),
5623
6345
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
@@ -5720,18 +6442,18 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5720
6442
  instalments.length === 1 ? "parcela" : "parcelas",
5721
6443
  " \xB7",
5722
6444
  " ",
5723
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: money2(instalmentSum) })
6445
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: money3(instalmentSum) })
5724
6446
  ] })
5725
6447
  ] }),
5726
6448
  note.declaredPayments.length > 0 && /* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [
5727
6449
  "A nota declarou: ",
5728
- note.declaredPayments.map((p) => `${p.label ?? p.code} ${money2(p.value)}`).join(" \xB7 ")
6450
+ note.declaredPayments.map((p) => `${p.label ?? p.code} ${money3(p.value)}`).join(" \xB7 ")
5729
6451
  ] }),
5730
6452
  /* @__PURE__ */ jsx("div", { className: "mt-3 space-y-2", children: instalments.map((inst, i) => /* @__PURE__ */ jsxs("div", { className: "grid items-end gap-2 sm:grid-cols-[2rem_1fr_1fr_1fr_2rem]", children: [
5731
6453
  /* @__PURE__ */ jsx("span", { className: "pb-2 text-xs tabular-nums text-muted-foreground", children: inst.index }),
5732
6454
  /* @__PURE__ */ jsxs("label", { className: "block", children: [
5733
6455
  /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: "Vencimento" }),
5734
- readOnly ? /* @__PURE__ */ jsx("p", { className: "py-2 text-sm tabular-nums", children: day2(inst.dueDate) }) : /* @__PURE__ */ jsx(
6456
+ readOnly ? /* @__PURE__ */ jsx("p", { className: "py-2 text-sm tabular-nums", children: day3(inst.dueDate) }) : /* @__PURE__ */ jsx(
5735
6457
  DatePicker,
5736
6458
  {
5737
6459
  value: inst.dueDate ?? "",
@@ -5795,41 +6517,24 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5795
6517
  Math.abs(instalmentSum - note.total) > 0.01 && /* @__PURE__ */ jsx(Hint, { className: "mt-3", children: /* @__PURE__ */ jsxs("span", { children: [
5796
6518
  /* @__PURE__ */ jsx(AlertTriangle, { className: "mr-1 inline h-3.5 w-3.5", "aria-hidden": true }),
5797
6519
  "A nota diz ",
5798
- money2(note.total),
6520
+ money3(note.total),
5799
6521
  " e as parcelas somam ",
5800
- money2(instalmentSum),
6522
+ money3(instalmentSum),
5801
6523
  ". O t\xEDtulo vai nascer com ",
5802
- /* @__PURE__ */ jsx("strong", { children: money2(instalmentSum) }),
6524
+ /* @__PURE__ */ jsx("strong", { children: money3(instalmentSum) }),
5803
6525
  " \u2014 \xE9 a soma das parcelas que define o saldo a pagar."
5804
6526
  ] }) })
5805
6527
  ] }),
5806
- /* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
5807
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Anexos da conta" }),
5808
- /* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [
5809
- "Boleto, comprovante, foto da nota. No sistema antigo, anexar o boleto tamb\xE9m",
5810
- " ",
5811
- /* @__PURE__ */ jsx("strong", { children: "preenchia as parcelas sozinho" }),
5812
- " \u2014 \xE9 a automa\xE7\xE3o mais \xFAtil e a mais invis\xEDvel de l\xE1."
5813
- ] }),
5814
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [
5815
- /* @__PURE__ */ jsx(
5816
- EmBreve,
5817
- {
5818
- label: "Anexar boleto",
5819
- o_que: "Guardar o arquivo no t\xEDtulo que esta nota cria",
5820
- quantas: "25 anexos no V1, o \xFAltimo hoje"
5821
- }
5822
- ),
5823
- /* @__PURE__ */ jsx(
5824
- EmBreve,
5825
- {
5826
- label: "Ler o c\xF3digo de barras do boleto",
5827
- o_que: "Preencher vencimento e valor da parcela a partir do boleto anexado",
5828
- quantas: "automa\xE7\xE3o do V1 que ningu\xE9m documentou"
5829
- }
5830
- )
5831
- ] })
5832
- ] }),
6528
+ /* @__PURE__ */ jsx(
6529
+ NoteAttachmentsCard,
6530
+ {
6531
+ noteId: note.id,
6532
+ invoiceId: note.invoiceId,
6533
+ readOnly,
6534
+ instalments,
6535
+ onInstalments: setInstalments
6536
+ }
6537
+ ),
5833
6538
  error && /* @__PURE__ */ jsx(Card, { className: "border-destructive bg-destructive/5 p-4", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }) })
5834
6539
  ] }), `Concilia\xE7\xE3o \xB7 NF-e ${note.number}`, rodape);
5835
6540
  }
@@ -5858,8 +6563,8 @@ function Field({ label, value, mono, small }) {
5858
6563
  /* @__PURE__ */ jsx("dd", { className: `${mono ? "tabular-nums" : ""} ${small ? "text-xs" : "text-sm"}`, children: value })
5859
6564
  ] });
5860
6565
  }
5861
- var money3 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(v || 0);
5862
- var day3 = (iso) => iso ? iso.slice(0, 10).split("-").reverse().join("/") : "\u2014";
6566
+ var money4 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(v || 0);
6567
+ var day4 = (iso) => iso ? iso.slice(0, 10).split("-").reverse().join("/") : "\u2014";
5863
6568
  var MANIFEST_TONE = {
5864
6569
  confirmada: "bg-success-soft text-success-soft-foreground",
5865
6570
  processando: "bg-info-soft text-info-soft-foreground",
@@ -5869,6 +6574,7 @@ function ManifestView({ onOpenNote, onBack }) {
5869
6574
  const [notes, setNotes] = useState([]);
5870
6575
  const [loading, setLoading] = useState(true);
5871
6576
  const [tab, setTab] = useState("pendentes");
6577
+ const [pullOpen, setPullOpen] = useState(false);
5872
6578
  const reload = useCallback(async () => {
5873
6579
  setLoading(true);
5874
6580
  try {
@@ -5918,12 +6624,19 @@ function ManifestView({ onOpenNote, onBack }) {
5918
6624
  /* @__PURE__ */ jsx(Hint, { className: "mt-3", children: /* @__PURE__ */ jsx("span", { children: "A manifesta\xE7\xE3o continua sendo feita no sistema antigo por enquanto. Aqui voc\xEA l\xEA a fila e o que a SEFAZ respondeu; a entrada no estoque e a conta a pagar j\xE1 s\xE3o feitas nesta tela nova." }) }),
5919
6625
  /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [
5920
6626
  /* @__PURE__ */ jsx(EmBreve, { label: "Aceitar / Recusar", o_que: "Responder \xE0 SEFAZ \u2014 \xE9 obriga\xE7\xE3o com prazo. Hoje a manifesta\xE7\xE3o \xE9 feita no sistema antigo" }),
5921
- /* @__PURE__ */ jsx(EmBreve, { label: "Puxar por chave", o_que: "Buscar uma NF-e espec\xEDfica pela chave de acesso de 44 d\xEDgitos" }),
6627
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setPullOpen(true), children: [
6628
+ /* @__PURE__ */ jsx(KeyRound, { className: "mr-1.5 h-4 w-4" }),
6629
+ "Puxar por chave"
6630
+ ] }),
5922
6631
  /* @__PURE__ */ jsx(EmBreve, { label: "Sincronizar agora", o_que: "Pedir \xE0 SEFAZ o que chegou desde a \xFAltima varredura" }),
5923
6632
  /* @__PURE__ */ jsx(EmBreve, { label: "Resincronizar tudo", o_que: "Refazer a varredura inteira do per\xEDodo" }),
5924
6633
  /* @__PURE__ */ jsx(EmBreve, { label: "Diagnosticar", o_que: "Por que uma nota n\xE3o chegou: certificado, credencial ou cursor parado" })
5925
6634
  ] })
5926
6635
  ] }),
6636
+ /* @__PURE__ */ jsx(PullByKeyDialog, { open: pullOpen, onOpenChange: setPullOpen, onPulled: (r) => {
6637
+ void reload();
6638
+ onOpenNote(r.documentId);
6639
+ } }),
5927
6640
  /* @__PURE__ */ jsx(ListFilterBar, { filters, onClear: () => setTab("todas"), activeCount: tab === "todas" ? 0 : 1 }),
5928
6641
  loading ? /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-full" }, i)) }) : shown.length === 0 ? /* @__PURE__ */ jsxs(Card, { className: "p-10 text-center", children: [
5929
6642
  /* @__PURE__ */ jsx(FileText, { className: "mx-auto h-8 w-8 text-muted-foreground", "aria-hidden": true }),
@@ -5939,10 +6652,10 @@ function ManifestView({ onOpenNote, onBack }) {
5939
6652
  /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: "A\xE7\xF5es" })
5940
6653
  ] }) }),
5941
6654
  /* @__PURE__ */ jsx("tbody", { children: shown.map((note) => /* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0 hover:bg-muted/30", children: [
5942
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums text-muted-foreground", children: day3(note.issueDate) }),
6655
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums text-muted-foreground", children: day4(note.issueDate) }),
5943
6656
  /* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums font-medium", children: note.number }),
5944
6657
  /* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: note.supplierName || "\u2014" }),
5945
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: money3(note.total) }),
6658
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: money4(note.total) }),
5946
6659
  /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums text-muted-foreground", children: note.instalmentCount }),
5947
6660
  /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-center", children: /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${MANIFEST_TONE[note.manifestationStatus ?? ""] ?? "bg-muted text-muted-foreground"}`, children: note.manifestationStatus ?? "n\xE3o manifestada" }) }),
5948
6661
  /* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1.5", children: [
@@ -6078,9 +6791,9 @@ var COLUMNS = [
6078
6791
  ].join(", ");
6079
6792
  var sb2 = () => getSupabaseClientOptional();
6080
6793
  function useEmitterSettings(legalEntityId) {
6081
- const [row, setRow] = React5.useState(null);
6082
- const [loading, setLoading] = React5.useState(true);
6083
- React5.useEffect(() => {
6794
+ const [row, setRow] = React7.useState(null);
6795
+ const [loading, setLoading] = React7.useState(true);
6796
+ React7.useEffect(() => {
6084
6797
  if (!legalEntityId) {
6085
6798
  setLoading(false);
6086
6799
  return;
@@ -6096,7 +6809,7 @@ function useEmitterSettings(legalEntityId) {
6096
6809
  alive = false;
6097
6810
  };
6098
6811
  }, [legalEntityId]);
6099
- const save = React5.useCallback(async (tenantId, patch) => {
6812
+ const save = React7.useCallback(async (tenantId, patch) => {
6100
6813
  if (!legalEntityId) return false;
6101
6814
  const payload = { ...patch, tenant_id: tenantId, legal_entity_id: legalEntityId, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
6102
6815
  const { data, error } = await sb2().from("plg_fiscalbr_emitter_settings").upsert(payload, { onConflict: "legal_entity_id" }).select(COLUMNS).single();
@@ -6149,7 +6862,7 @@ function SummaryCard({ title, lines, badge, onConfigure }) {
6149
6862
  return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardHeader, { className: "flex-row items-start justify-between gap-3 space-y-0", children: [
6150
6863
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
6151
6864
  /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: title }),
6152
- /* @__PURE__ */ jsx("dl", { className: "mt-2 grid gap-x-6 gap-y-1 text-sm", style: { gridTemplateColumns: "auto 1fr" }, children: lines.map((l) => /* @__PURE__ */ jsxs(React5.Fragment, { children: [
6865
+ /* @__PURE__ */ jsx("dl", { className: "mt-2 grid gap-x-6 gap-y-1 text-sm", style: { gridTemplateColumns: "auto 1fr" }, children: lines.map((l) => /* @__PURE__ */ jsxs(React7.Fragment, { children: [
6153
6866
  /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: l.label }),
6154
6867
  /* @__PURE__ */ jsx("dd", { className: "text-foreground", children: l.value })
6155
6868
  ] }, l.label)) })
@@ -6170,7 +6883,7 @@ function SummaryCard({ title, lines, badge, onConfigure }) {
6170
6883
  var fmtDateTime = (v) => v ? new Date(v).toLocaleString("pt-BR") : null;
6171
6884
  function UnitCertificateWidget({ item }) {
6172
6885
  const { row, loading, save } = useEmitterSettings(item?.legalEntityId);
6173
- const [open, setOpen] = React5.useState(false);
6886
+ const [open, setOpen] = React7.useState(false);
6174
6887
  if (!item?.id) return null;
6175
6888
  if (!item.legalEntityId) return /* @__PURE__ */ jsx(NeedsLegalEntity, { item, what: "O certificado digital" });
6176
6889
  if (loading || !row) return /* @__PURE__ */ jsx(LoadingCard, {});
@@ -6273,11 +6986,11 @@ function EnvSelect({ value, onChange, disabled }) {
6273
6986
  }
6274
6987
  function UnitFiscalWidget({ item }) {
6275
6988
  const { row, loading, save } = useEmitterSettings(item?.legalEntityId);
6276
- const [draft, setDraft] = React5.useState({});
6277
- const [cscToken, setCscToken] = React5.useState("");
6278
- const [saving, setSaving] = React5.useState(false);
6279
- const [open, setOpen] = React5.useState(false);
6280
- React5.useEffect(() => {
6989
+ const [draft, setDraft] = React7.useState({});
6990
+ const [cscToken, setCscToken] = React7.useState("");
6991
+ const [saving, setSaving] = React7.useState(false);
6992
+ const [open, setOpen] = React7.useState(false);
6993
+ React7.useEffect(() => {
6281
6994
  if (row) setDraft(row);
6282
6995
  }, [row]);
6283
6996
  if (!item?.id) return null;
@@ -6645,7 +7358,7 @@ var dayOrDateTime = (v) => {
6645
7358
  const d = new Date(v);
6646
7359
  return d.getHours() === 0 && d.getMinutes() === 0 && d.getSeconds() === 0 ? d.toLocaleDateString("pt-BR") : dateTime(v);
6647
7360
  };
6648
- var money4 = (v) => v == null ? "\u2014" : v.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
7361
+ var money5 = (v) => v == null ? "\u2014" : v.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
6649
7362
  function buttonHint(s) {
6650
7363
  const parts = [s.hasValid ? "Notas fiscais desta conta \u2014 h\xE1 uma nota v\xE1lida" : "Notas fiscais desta conta"];
6651
7364
  if (s.withoutEffect > 0) parts.push(`${s.withoutEffect} cancelada(s), rejeitada(s) ou com erro`);
@@ -6713,7 +7426,7 @@ function NoteRow({ note, canCancel, onChanged }) {
6713
7426
  else window.location.assign(url);
6714
7427
  } catch (e) {
6715
7428
  w?.close();
6716
- toast.error("PDF indispon\xEDvel", { description: e instanceof Error ? e.message : String(e) });
7429
+ toast.error("PDF indispon\xEDvel", { description: describeError(e) });
6717
7430
  } finally {
6718
7431
  setBusy(null);
6719
7432
  }
@@ -6725,7 +7438,7 @@ function NoteRow({ note, canCancel, onChanged }) {
6725
7438
  await onChanged();
6726
7439
  toast(status === note.status ? "Ainda em processamento na SEFAZ/prefeitura" : `Nota ${statusLabel({ status, cancelledAt: null }).toLowerCase()}`);
6727
7440
  } catch (e) {
6728
- toast.error("N\xE3o foi poss\xEDvel consultar", { description: e instanceof Error ? e.message : String(e) });
7441
+ toast.error("N\xE3o foi poss\xEDvel consultar", { description: describeError(e) });
6729
7442
  } finally {
6730
7443
  setBusy(null);
6731
7444
  }
@@ -6813,7 +7526,7 @@ function CancelNotePanel({ note, onClose, onDone }) {
6813
7526
  else toast("Cancelamento solicitado", { description: "A SEFAZ/prefeitura ainda est\xE1 processando. Use \xABAtualizar\xBB para conferir." });
6814
7527
  await onDone();
6815
7528
  } catch (e) {
6816
- setError(e instanceof Error ? e.message : String(e));
7529
+ setError(describeError(e));
6817
7530
  setWorking(null);
6818
7531
  }
6819
7532
  };
@@ -6884,7 +7597,7 @@ function NoteDetails({ note }) {
6884
7597
  ["Refer\xEAncia", note.integrationId && /* @__PURE__ */ jsx("span", { className: "break-all font-mono text-xs", children: note.integrationId })],
6885
7598
  ["Chave de acesso", note.accessKey && /* @__PURE__ */ jsx("span", { className: "break-all font-mono text-xs", children: note.accessKey })],
6886
7599
  ["C\xF3digo de verifica\xE7\xE3o", note.verificationCode],
6887
- ["Valor", money4(note.totalValue)],
7600
+ ["Valor", money5(note.totalValue)],
6888
7601
  ["Tomador", recipientLabel(note.recipient)],
6889
7602
  ["Criada em", dateTime(note.createdAt)],
6890
7603
  ["Autorizada em", note.authorizedAt && dayOrDateTime(note.authorizedAt)],
@@ -7099,9 +7812,9 @@ function inboundNotesInventoryView(options = {}) {
7099
7812
  };
7100
7813
  }
7101
7814
  function InboundNoteDocumentModal({ movement, onClose }) {
7102
- const [noteId, setNoteId] = React5.useState(null);
7103
- const [erro, setErro] = React5.useState(null);
7104
- React5.useEffect(() => {
7815
+ const [noteId, setNoteId] = React7.useState(null);
7816
+ const [erro, setErro] = React7.useState(null);
7817
+ React7.useEffect(() => {
7105
7818
  let vivo = true;
7106
7819
  const numero = nfeNumberOf(movement);
7107
7820
  const busca = movement.operationId ? findInboxNoteByOperation(movement.operationId).then((id) => id ?? (numero ? findInboxNoteByNumber(numero) : null)) : numero ? findInboxNoteByNumber(numero) : Promise.resolve(null);
@@ -7110,7 +7823,7 @@ function InboundNoteDocumentModal({ movement, onClose }) {
7110
7823
  if (id) setNoteId(id);
7111
7824
  else setErro("N\xE3o achei a nota desta entrada nesta conta.");
7112
7825
  }).catch((e) => {
7113
- if (vivo) setErro(e instanceof Error ? e.message : String(e));
7826
+ if (vivo) setErro(describeError(e));
7114
7827
  });
7115
7828
  return () => {
7116
7829
  vivo = false;
@@ -7283,6 +7996,6 @@ function createPlugNotasPlugin(options = {}) {
7283
7996
  });
7284
7997
  }
7285
7998
 
7286
- export { EVENT_LABEL, InboundNoteDocumentModal, InboundNotesPage, InboundNotesView, InvoiceNotesButton, IssuedNotesView, MODEL_LABEL, ManifestView, NfeParseError, NoteReconcileView, PLUGNOTAS_CONNECTOR_ID, PLUGNOTAS_FIELDS, PLUGNOTAS_SETTINGS, ReturnedNotesView, STATUS_META, STOCK_BEARING_ENTRY_TYPES, TaxGroupsView, acceptNote, cancelNote, createPlugNotasConnector, createPlugNotasFinancialProvider, createPlugNotasPlugin, eventStatusLabel, explainRejection, fetchNotePdf, findInboxNoteByNumber, findInboxNoteByOperation, getInboxNote, getInboxNoteXml, inboundNoteDocumentModal, inboundNotesInventoryView, isStockBearing, isValidCnpj, isWithoutEffect, listChartAccounts, listInboxNotes, listInvoiceNotes, listMeasurementUnits, listNoteEvents, listProductsByIds, listStockLocations, listSupplierMap, markPlugNotasEmitterReady, notesButtonState, onlyDigits, parseNfeXml, paymentLabel, paymentTypeFromCode, recipientLabel, refreshNote, rejectNote, saveNoteDecision, searchProducts, stageNoteFromXml, statusLabel, suggestNoteItems, summarizeNotes, toStagePayload };
7999
+ export { EVENT_LABEL, InboundNoteDocumentModal, InboundNotesPage, InboundNotesView, InvoiceNotesButton, IssuedNotesView, MODEL_LABEL, ManifestView, NfeParseError, NoteReconcileView, PLUGNOTAS_CONNECTOR_ID, PLUGNOTAS_FIELDS, PLUGNOTAS_SETTINGS, PullByKeyError, ReturnedNotesView, STATUS_META, STOCK_BEARING_ENTRY_TYPES, TaxGroupsView, acceptNote, addNoteAttachment, cancelNote, createPlugNotasConnector, createPlugNotasFinancialProvider, createPlugNotasPlugin, eventStatusLabel, explainRejection, extractPaymentCode, fetchNotePdf, findInboxNoteByNumber, findInboxNoteByOperation, getInboxNote, getInboxNotePdf, getInboxNoteXml, inboundNoteDocumentModal, inboundNotesInventoryView, isStockBearing, isValidCnpj, isWithoutEffect, linkInboxSupplier, listChartAccounts, listInboxNotes, listInvoiceNotes, listMeasurementUnits, listNoteAttachments, listNoteEvents, listProductsByIds, listStockLocations, listSupplierMap, markPlugNotasEmitterReady, moveNoteAttachmentsToInvoice, notesButtonState, onlyDigits, parseBoleto, parseNfeXml, paymentLabel, paymentTypeFromCode, pullNoteByKey, recipientLabel, refreshNote, rejectNote, removeNoteAttachment, saveNoteDecision, searchProducts, stageNoteFromXml, statusLabel, suggestNoteItems, summarizeNotes, toStagePayload };
7287
8000
  //# sourceMappingURL=index.js.map
7288
8001
  //# sourceMappingURL=index.js.map