@fayz-ai/plugin-fiscal-br 0.4.0 → 0.5.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/data/issued.d.ts +13 -0
- package/dist/data/issued.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +631 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/issued-note.d.ts +60 -0
- package/dist/lib/issued-note.d.ts.map +1 -0
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/views/InvoiceNotesButton.d.ts +9 -0
- package/dist/views/InvoiceNotesButton.d.ts.map +1 -0
- package/functions/fiscalbr-plugnotas/index.ts +222 -0
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { recordRailFields, RECORD_GROUP, activeFacet, getSupabaseClientOptional, connectionSettings, connectionStatus, definePlugin, createConnectionStore, settingsStatus, maskCpfCnpj } from '@fayz-ai/core';
|
|
2
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,
|
|
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';
|
|
4
4
|
export { ComingSoon, EmBreve } from '@fayz-ai/ui';
|
|
5
|
-
import { registerDocumentOpener, getActiveTenantId, useUnitFilter, UNIT_DETAIL_ZONES, adminNavigateTo } from '@fayz-ai/admin';
|
|
6
|
-
import { Gift, RefreshCw, Loader2, FileDown, AlertTriangle, ChevronRight, Check, Trash2, Plus, Inbox, UploadCloud, ShieldCheck, FileText, ExternalLink, X, Info } from 'lucide-react';
|
|
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';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
import { FINANCIAL_INVOICE_ZONES } from '@fayz-ai/plugin-financial';
|
|
8
9
|
|
|
9
10
|
// src/index.ts
|
|
10
11
|
var PLUGNOTAS_CONNECTOR_ID = "plugnotas";
|
|
@@ -3647,6 +3648,129 @@ CREATE POLICY plg_fiscalbr_emitter_settings_authz_write ON public.plg_fiscalbr_e
|
|
|
3647
3648
|
WHERE le.id = plg_fiscalbr_emitter_settings.legal_entity_id
|
|
3648
3649
|
AND le.tenant_id = plg_fiscalbr_emitter_settings.tenant_id));
|
|
3649
3650
|
`;
|
|
3651
|
+
var MIGRATION_018_A_NOTA_DO_V1_ALCANCA_A_CONTA = `-- A nota que veio do V1 alcan\xE7a a conta que a gerou.
|
|
3652
|
+
--
|
|
3653
|
+
-- A conta a receber pergunta "que notas eu tenho?" pelo pedido dela:
|
|
3654
|
+
-- plg_financial_invoices.order_id \u2192 plg_fiscalbr_document_orders \u2192 a nota.
|
|
3655
|
+
-- A importa\xE7\xE3o s\xF3 preencheu esse v\xEDnculo a partir da tabela N:N do V1
|
|
3656
|
+
-- (fiscal_document_invoice_sources). A NFS-e do V1 nunca usou essa tabela: ela
|
|
3657
|
+
-- aponta a conta por fiscal_documents.invoice_id, que chegou aqui apenas como
|
|
3658
|
+
-- metadata.v1.invoice_ref. Medido em 06-10-2026 no cluster do ChefControl: 488
|
|
3659
|
+
-- NFS-e e 122 NFC-e sem caminho at\xE9 a conta.
|
|
3660
|
+
--
|
|
3661
|
+
-- A conta importada guarda o pr\xF3prio id do V1 em
|
|
3662
|
+
-- metadata.migration.source_numeric_id, ent\xE3o o par se resolve sem adivinhar.
|
|
3663
|
+
-- Num pool sem dado do V1 isto n\xE3o casa nada. Aditivo e idempotente.
|
|
3664
|
+
|
|
3665
|
+
INSERT INTO public.plg_fiscalbr_document_orders (tenant_id, document_id, order_id)
|
|
3666
|
+
SELECT DISTINCT f.tenant_id, f.document_id, i.order_id
|
|
3667
|
+
FROM public.plg_fiscalbr_documents f
|
|
3668
|
+
JOIN public.plg_financial_invoices i
|
|
3669
|
+
ON i.tenant_id = f.tenant_id
|
|
3670
|
+
AND i.metadata -> 'migration' ->> 'source_table' = 'invoices'
|
|
3671
|
+
AND i.metadata -> 'migration' ->> 'source_numeric_id' = f.metadata -> 'v1' ->> 'invoice_ref'
|
|
3672
|
+
JOIN public.orders o ON o.id = i.order_id
|
|
3673
|
+
WHERE f.metadata -> 'v1' ->> 'invoice_ref' IS NOT NULL
|
|
3674
|
+
ON CONFLICT (document_id, order_id) DO NOTHING;
|
|
3675
|
+
`;
|
|
3676
|
+
var MIGRATION_019_A_NOTA_EMITIDA_SE_CANCELA_PELO_V2 = `-- A nota emitida se consulta e se cancela pelo V2.
|
|
3677
|
+
--
|
|
3678
|
+
-- Quem fala com a PlugNotas \xE9 a fun\xE7\xE3o \`fiscalbr-plugnotas\` (a chave s\xF3 existe
|
|
3679
|
+
-- no servidor), mas ela roda com o JWT de quem clicou e n\xE3o grava nada direto:
|
|
3680
|
+
-- pergunta aqui se a pessoa pode, e devolve o resultado aqui. Uma porta de
|
|
3681
|
+
-- leitura, uma de escrita, a permiss\xE3o conferida nas duas.
|
|
3682
|
+
--
|
|
3683
|
+
-- consultar e baixar o PDF \u2192 financial.read na unidade da nota
|
|
3684
|
+
-- cancelar \u2192 financial.edit na unidade da nota
|
|
3685
|
+
--
|
|
3686
|
+
-- A espelho em \`documents.status\` anda junto: as duas tabelas s\xE3o a mesma nota
|
|
3687
|
+
-- vista de dois lugares (005), e um status divergente entre elas \xE9 a nota
|
|
3688
|
+
-- dizendo duas coisas. Idempotente.
|
|
3689
|
+
|
|
3690
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_note_for_provider(p_document_id uuid, p_action text)
|
|
3691
|
+
RETURNS jsonb
|
|
3692
|
+
LANGUAGE plpgsql
|
|
3693
|
+
STABLE SECURITY DEFINER
|
|
3694
|
+
SET search_path TO ''
|
|
3695
|
+
AS $$
|
|
3696
|
+
DECLARE
|
|
3697
|
+
v public.plg_fiscalbr_documents%ROWTYPE;
|
|
3698
|
+
BEGIN
|
|
3699
|
+
SELECT * INTO v FROM public.plg_fiscalbr_documents
|
|
3700
|
+
WHERE document_id = p_document_id AND tenant_id = app.current_tenant_id();
|
|
3701
|
+
IF NOT FOUND THEN
|
|
3702
|
+
RAISE EXCEPTION 'fiscal: note not found' USING ERRCODE = 'P0002';
|
|
3703
|
+
END IF;
|
|
3704
|
+
PERFORM public.plg_financial_require_permission(
|
|
3705
|
+
CASE WHEN p_action = 'cancel' THEN 'financial.edit' ELSE 'financial.read' END, v.unit_id);
|
|
3706
|
+
RETURN jsonb_build_object(
|
|
3707
|
+
'document_id', v.document_id, 'model', v.model, 'status', v.status,
|
|
3708
|
+
'environment', v.environment, 'provider_document_id', v.provider_document_id,
|
|
3709
|
+
'cancelled_at', v.cancelled_at, 'access_key', v.access_key, 'number', v.number);
|
|
3710
|
+
END $$;
|
|
3711
|
+
|
|
3712
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_note_apply(
|
|
3713
|
+
p_document_id uuid,
|
|
3714
|
+
p_action text,
|
|
3715
|
+
p_patch jsonb,
|
|
3716
|
+
p_event jsonb DEFAULT NULL
|
|
3717
|
+
)
|
|
3718
|
+
RETURNS void
|
|
3719
|
+
LANGUAGE plpgsql
|
|
3720
|
+
SECURITY DEFINER
|
|
3721
|
+
SET search_path TO ''
|
|
3722
|
+
AS $$
|
|
3723
|
+
DECLARE
|
|
3724
|
+
v public.plg_fiscalbr_documents%ROWTYPE;
|
|
3725
|
+
v_status text := p_patch ->> 'status';
|
|
3726
|
+
BEGIN
|
|
3727
|
+
SELECT * INTO v FROM public.plg_fiscalbr_documents
|
|
3728
|
+
WHERE document_id = p_document_id AND tenant_id = app.current_tenant_id()
|
|
3729
|
+
FOR UPDATE;
|
|
3730
|
+
IF NOT FOUND THEN
|
|
3731
|
+
RAISE EXCEPTION 'fiscal: note not found' USING ERRCODE = 'P0002';
|
|
3732
|
+
END IF;
|
|
3733
|
+
PERFORM public.plg_financial_require_permission(
|
|
3734
|
+
CASE WHEN p_action = 'cancel' THEN 'financial.edit' ELSE 'financial.read' END, v.unit_id);
|
|
3735
|
+
IF v_status IS NOT NULL AND v_status NOT IN ('processing','authorized','rejected','error','cancelled','substituted') THEN
|
|
3736
|
+
RAISE EXCEPTION 'fiscal: unknown status %', v_status USING ERRCODE = '22023';
|
|
3737
|
+
END IF;
|
|
3738
|
+
|
|
3739
|
+
-- Only what the provider answers; identity (model, unit, order) never moves.
|
|
3740
|
+
UPDATE public.plg_fiscalbr_documents SET
|
|
3741
|
+
status = COALESCE(v_status, status),
|
|
3742
|
+
number = COALESCE(p_patch ->> 'number', number),
|
|
3743
|
+
series = COALESCE(p_patch ->> 'series', series),
|
|
3744
|
+
access_key = COALESCE(p_patch ->> 'access_key', access_key),
|
|
3745
|
+
verification_code = COALESCE(p_patch ->> 'verification_code', verification_code),
|
|
3746
|
+
status_payload = COALESCE(p_patch -> 'status_payload', status_payload),
|
|
3747
|
+
rejection_payload = CASE WHEN p_patch ? 'rejection_payload' THEN p_patch -> 'rejection_payload' ELSE rejection_payload END,
|
|
3748
|
+
authorized_at = COALESCE((p_patch ->> 'authorized_at')::timestamptz, authorized_at),
|
|
3749
|
+
last_polled_at = CASE WHEN p_action = 'poll' THEN now() ELSE last_polled_at END,
|
|
3750
|
+
cancelled_at = CASE WHEN p_patch ? 'cancelled_at' THEN (p_patch ->> 'cancelled_at')::timestamptz ELSE cancelled_at END,
|
|
3751
|
+
cancel_reason = CASE WHEN p_patch ? 'cancel_reason' THEN p_patch ->> 'cancel_reason' ELSE cancel_reason END,
|
|
3752
|
+
cancelled_by_user_id = CASE WHEN p_action = 'cancel' THEN auth.uid()
|
|
3753
|
+
WHEN p_patch ? 'cancelled_at' AND p_patch -> 'cancelled_at' = 'null'::jsonb THEN NULL
|
|
3754
|
+
ELSE cancelled_by_user_id END
|
|
3755
|
+
WHERE document_id = p_document_id;
|
|
3756
|
+
|
|
3757
|
+
IF v_status IS NOT NULL THEN
|
|
3758
|
+
UPDATE public.documents SET status = v_status WHERE id = p_document_id AND status IS DISTINCT FROM v_status;
|
|
3759
|
+
END IF;
|
|
3760
|
+
|
|
3761
|
+
IF p_event IS NOT NULL THEN
|
|
3762
|
+
INSERT INTO public.plg_fiscalbr_document_events
|
|
3763
|
+
(tenant_id, unit_id, document_id, event_type, status, justification, response_payload, created_by_user_id)
|
|
3764
|
+
VALUES (v.tenant_id, v.unit_id, p_document_id, p_event ->> 'event_type', p_event ->> 'status',
|
|
3765
|
+
p_event ->> 'justification', p_event -> 'response_payload', auth.uid());
|
|
3766
|
+
END IF;
|
|
3767
|
+
END $$;
|
|
3768
|
+
|
|
3769
|
+
REVOKE ALL ON FUNCTION public.fiscalbr_note_for_provider(uuid, text) FROM PUBLIC, anon;
|
|
3770
|
+
REVOKE ALL ON FUNCTION public.fiscalbr_note_apply(uuid, text, jsonb, jsonb) FROM PUBLIC, anon;
|
|
3771
|
+
GRANT EXECUTE ON FUNCTION public.fiscalbr_note_for_provider(uuid, text) TO authenticated;
|
|
3772
|
+
GRANT EXECUTE ON FUNCTION public.fiscalbr_note_apply(uuid, text, jsonb, jsonb) TO authenticated;
|
|
3773
|
+
`;
|
|
3650
3774
|
var MIGRATIONS = [
|
|
3651
3775
|
{ id: "000_baseline", sql: MIGRATION_000_BASELINE },
|
|
3652
3776
|
{ id: "001_preserve_terminal_results_and_recover_leases", sql: MIGRATION_001_PRESERVE_TERMINAL_RESULTS_AND_RECOVER_LEASES },
|
|
@@ -3665,7 +3789,9 @@ var MIGRATIONS = [
|
|
|
3665
3789
|
{ id: "014_a_descricao_da_nota_tem_lixo_no_meio", sql: MIGRATION_014_A_DESCRICAO_DA_NOTA_TEM_LIXO_NO_MEIO },
|
|
3666
3790
|
{ id: "015_o_movimento_diz_que_veio_da_nota", sql: MIGRATION_015_O_MOVIMENTO_DIZ_QUE_VEIO_DA_NOTA },
|
|
3667
3791
|
{ id: "016_a_nota_guarda_o_proprio_xml", sql: MIGRATION_016_A_NOTA_GUARDA_O_PROPRIO_XML },
|
|
3668
|
-
{ id: "017_quem_emite_e_o_cnpj_nao_a_loja", sql: MIGRATION_017_QUEM_EMITE_E_O_CNPJ_NAO_A_LOJA }
|
|
3792
|
+
{ id: "017_quem_emite_e_o_cnpj_nao_a_loja", sql: MIGRATION_017_QUEM_EMITE_E_O_CNPJ_NAO_A_LOJA },
|
|
3793
|
+
{ 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 }
|
|
3669
3795
|
];
|
|
3670
3796
|
var taxGroupEntity = {
|
|
3671
3797
|
name: "Grupo fiscal",
|
|
@@ -6305,6 +6431,499 @@ function UnitFiscalWidget({ item }) {
|
|
|
6305
6431
|
] })
|
|
6306
6432
|
] });
|
|
6307
6433
|
}
|
|
6434
|
+
function sb3() {
|
|
6435
|
+
const supabase = getSupabaseClientOptional();
|
|
6436
|
+
if (!supabase) throw new Error("Supabase not initialized");
|
|
6437
|
+
return supabase;
|
|
6438
|
+
}
|
|
6439
|
+
var NOTE_COLUMNS2 = `document_id, model, status, environment, number, series, access_key,
|
|
6440
|
+
verification_code, provider_integration_id, total_value, recipient_snapshot, rejection_payload,
|
|
6441
|
+
created_at, authorized_at, cancelled_at, cancel_reason`;
|
|
6442
|
+
function toNote(r) {
|
|
6443
|
+
return {
|
|
6444
|
+
documentId: r.document_id,
|
|
6445
|
+
model: r.model,
|
|
6446
|
+
status: r.status,
|
|
6447
|
+
environment: r.environment ?? null,
|
|
6448
|
+
number: r.number ?? null,
|
|
6449
|
+
series: r.series ?? null,
|
|
6450
|
+
accessKey: r.access_key ?? null,
|
|
6451
|
+
verificationCode: r.verification_code ?? null,
|
|
6452
|
+
integrationId: r.provider_integration_id ?? null,
|
|
6453
|
+
totalValue: r.total_value == null ? null : Number(r.total_value),
|
|
6454
|
+
recipient: r.recipient_snapshot ?? null,
|
|
6455
|
+
rejection: r.rejection_payload ?? null,
|
|
6456
|
+
createdAt: r.created_at,
|
|
6457
|
+
authorizedAt: r.authorized_at ?? null,
|
|
6458
|
+
cancelledAt: r.cancelled_at ?? null,
|
|
6459
|
+
cancelReason: r.cancel_reason ?? null
|
|
6460
|
+
};
|
|
6461
|
+
}
|
|
6462
|
+
async function listInvoiceNotes(invoiceId) {
|
|
6463
|
+
const db = sb3();
|
|
6464
|
+
const { data: inv, error: invError } = await db.from("plg_financial_invoices").select("order_id").eq("id", invoiceId).maybeSingle();
|
|
6465
|
+
if (invError) throw invError;
|
|
6466
|
+
const orderId = inv?.order_id ?? null;
|
|
6467
|
+
if (!orderId) return [];
|
|
6468
|
+
const [linked, direct] = await Promise.all([
|
|
6469
|
+
db.from("plg_fiscalbr_document_orders").select("document_id").eq("order_id", orderId),
|
|
6470
|
+
db.from("plg_fiscalbr_documents").select(NOTE_COLUMNS2).eq("order_id", orderId)
|
|
6471
|
+
]);
|
|
6472
|
+
if (linked.error) throw linked.error;
|
|
6473
|
+
if (direct.error) throw direct.error;
|
|
6474
|
+
const byId = /* @__PURE__ */ new Map();
|
|
6475
|
+
for (const r of direct.data ?? []) byId.set(r.document_id, toNote(r));
|
|
6476
|
+
const missing = (linked.data ?? []).map((r) => r.document_id).filter((id) => !byId.has(id));
|
|
6477
|
+
if (missing.length) {
|
|
6478
|
+
const { data, error } = await db.from("plg_fiscalbr_documents").select(NOTE_COLUMNS2).in("document_id", missing);
|
|
6479
|
+
if (error) throw error;
|
|
6480
|
+
for (const r of data ?? []) byId.set(r.document_id, toNote(r));
|
|
6481
|
+
}
|
|
6482
|
+
return [...byId.values()].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
6483
|
+
}
|
|
6484
|
+
async function listNoteEvents(documentId) {
|
|
6485
|
+
const { data, error } = await sb3().from("plg_fiscalbr_document_events").select("id, event_type, status, justification, created_at").eq("document_id", documentId).order("created_at", { ascending: false }).limit(30);
|
|
6486
|
+
if (error) throw error;
|
|
6487
|
+
return (data ?? []).map((e) => ({
|
|
6488
|
+
id: e.id,
|
|
6489
|
+
eventType: e.event_type,
|
|
6490
|
+
status: e.status ?? null,
|
|
6491
|
+
justification: e.justification ?? null,
|
|
6492
|
+
createdAt: e.created_at
|
|
6493
|
+
}));
|
|
6494
|
+
}
|
|
6495
|
+
async function callIssuer(body) {
|
|
6496
|
+
const { data, error } = await sb3().functions.invoke("fiscalbr-plugnotas", { body: { ...body, tenantId: getActiveTenantId() } });
|
|
6497
|
+
if (error) {
|
|
6498
|
+
const ctx = error?.context;
|
|
6499
|
+
let message = error.message || "Falha ao falar com o emissor fiscal.";
|
|
6500
|
+
if (ctx && typeof ctx.json === "function") {
|
|
6501
|
+
try {
|
|
6502
|
+
const b = await ctx.json();
|
|
6503
|
+
if (b?.error) message = String(b.error);
|
|
6504
|
+
} catch {
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6507
|
+
throw new Error(message);
|
|
6508
|
+
}
|
|
6509
|
+
if (data?.error) throw new Error(String(data.error));
|
|
6510
|
+
return data;
|
|
6511
|
+
}
|
|
6512
|
+
async function refreshNote(documentId) {
|
|
6513
|
+
const r = await callIssuer({ action: "poll_status", document_id: documentId });
|
|
6514
|
+
return String(r?.status ?? "");
|
|
6515
|
+
}
|
|
6516
|
+
async function cancelNote(documentId, justification, code) {
|
|
6517
|
+
const r = await callIssuer({ action: "cancel", document_id: documentId, justification, code });
|
|
6518
|
+
return String(r?.status ?? "");
|
|
6519
|
+
}
|
|
6520
|
+
async function fetchNotePdf(documentId) {
|
|
6521
|
+
const data = await callIssuer({ action: "get_pdf", document_id: documentId });
|
|
6522
|
+
if (!(data instanceof Blob)) throw new Error("O emissor n\xE3o devolveu um PDF.");
|
|
6523
|
+
return new Blob([data], { type: "application/pdf" });
|
|
6524
|
+
}
|
|
6525
|
+
|
|
6526
|
+
// src/lib/issued-note.ts
|
|
6527
|
+
var MODEL_LABEL = { nfse: "NFS-e", nfce: "NFC-e", nfe: "NF-e" };
|
|
6528
|
+
var STATUS_META = {
|
|
6529
|
+
authorized: { label: "Autorizada", tone: "success" },
|
|
6530
|
+
draft: { label: "Enviando", tone: "warning" },
|
|
6531
|
+
processing: { label: "Processando", tone: "warning" },
|
|
6532
|
+
rejected: { label: "Rejeitada", tone: "danger" },
|
|
6533
|
+
denied: { label: "Denegada", tone: "danger" },
|
|
6534
|
+
error: { label: "Erro", tone: "danger" },
|
|
6535
|
+
cancelled: { label: "Cancelada", tone: "muted" },
|
|
6536
|
+
substituted: { label: "Substitu\xEDda", tone: "muted" }
|
|
6537
|
+
};
|
|
6538
|
+
var EVENT_LABEL = {
|
|
6539
|
+
emissao: "Emiss\xE3o enviada",
|
|
6540
|
+
consulta: "Consulta de status",
|
|
6541
|
+
cancelamento: "Cancelamento",
|
|
6542
|
+
substituicao: "Substitui\xE7\xE3o",
|
|
6543
|
+
cce: "Carta de corre\xE7\xE3o",
|
|
6544
|
+
inutilizacao: "Inutiliza\xE7\xE3o",
|
|
6545
|
+
webhook: "Notifica\xE7\xE3o do emissor fiscal",
|
|
6546
|
+
erro: "Erro"
|
|
6547
|
+
};
|
|
6548
|
+
var EVENT_STATUS_LABEL = {
|
|
6549
|
+
CRIADO: "Enviando",
|
|
6550
|
+
PROCESSANDO: "Processando",
|
|
6551
|
+
CONCLUIDO: "Autorizada",
|
|
6552
|
+
REJEITADO: "Rejeitada",
|
|
6553
|
+
DENEGADO: "Denegada",
|
|
6554
|
+
CANCELADO: "Cancelada",
|
|
6555
|
+
SUBSTITUIDO: "Substitu\xEDda",
|
|
6556
|
+
SOLICITADO: "Solicitado",
|
|
6557
|
+
ERRO: "Erro"
|
|
6558
|
+
};
|
|
6559
|
+
function eventStatusLabel(status) {
|
|
6560
|
+
return status ? EVENT_STATUS_LABEL[status.toUpperCase()] ?? null : null;
|
|
6561
|
+
}
|
|
6562
|
+
var IN_FLIGHT = ["draft", "processing"];
|
|
6563
|
+
var WITHOUT_EFFECT = ["rejected", "denied", "error", "cancelled", "substituted"];
|
|
6564
|
+
function isWithoutEffect(status) {
|
|
6565
|
+
return WITHOUT_EFFECT.includes(status);
|
|
6566
|
+
}
|
|
6567
|
+
function statusLabel(note) {
|
|
6568
|
+
if (IN_FLIGHT.includes(note.status) && note.cancelledAt) return "Cancelamento solicitado";
|
|
6569
|
+
return (STATUS_META[note.status] ?? STATUS_META.error).label;
|
|
6570
|
+
}
|
|
6571
|
+
function summarizeNotes(notes) {
|
|
6572
|
+
const s = { hasValid: false, withoutEffect: 0, inFlight: 0, validIsTest: false };
|
|
6573
|
+
for (const n of notes) {
|
|
6574
|
+
if (n.status === "authorized") {
|
|
6575
|
+
s.hasValid = true;
|
|
6576
|
+
if (n.environment === "homologacao") s.validIsTest = true;
|
|
6577
|
+
} else if (IN_FLIGHT.includes(n.status)) s.inFlight += 1;
|
|
6578
|
+
else if (WITHOUT_EFFECT.includes(n.status)) s.withoutEffect += 1;
|
|
6579
|
+
}
|
|
6580
|
+
return s;
|
|
6581
|
+
}
|
|
6582
|
+
function notesButtonState(s) {
|
|
6583
|
+
if (s.hasValid) return "authorized";
|
|
6584
|
+
if (s.inFlight > 0) return "processing";
|
|
6585
|
+
if (s.withoutEffect > 0) return "failed-only";
|
|
6586
|
+
return "none";
|
|
6587
|
+
}
|
|
6588
|
+
var SEFAZ_HINTS = [
|
|
6589
|
+
["E0120", "no padr\xE3o nacional a Inscri\xE7\xE3o Municipal \xE9 opcional (a nota identifica pelo CNPJ). Apague a IM na configura\xE7\xE3o fiscal da unidade e emita de novo."],
|
|
6590
|
+
["E0312", "geralmente falta o C\xD3DIGO DE TRIBUTA\xC7\xC3O DO MUNIC\xCDPIO (3 d\xEDgitos) na configura\xE7\xE3o fiscal da unidade \u2014 n\xE3o o nacional. Cada prefeitura tem a sua tabela; confirme o c\xF3digo com a contabilidade."],
|
|
6591
|
+
["E0314", "o c\xF3digo de tributa\xE7\xE3o do munic\xEDpio n\xE3o existe na tabela daquela prefeitura. Confirme o valor com a contabilidade da unidade \u2014 ele n\xE3o \xE9 o mesmo em todo munic\xEDpio. Fora do Rio de Janeiro, o normal \xE9 deixar esse campo VAZIO."],
|
|
6592
|
+
["E0084", "o CNPJ n\xE3o consta como estabelecido nesse munic\xEDpio no cadastro nacional. Isso se resolve FORA do sistema: a empresa precisa ter inscri\xE7\xE3o municipal ativa na prefeitura e o endere\xE7o do CNPJ na Receita Federal apontando para o mesmo munic\xEDpio. Fale com a contabilidade \u2014 n\xE3o h\xE1 o que ajustar aqui."]
|
|
6593
|
+
];
|
|
6594
|
+
var FIELD_LABELS = {
|
|
6595
|
+
"prestador.cpfCnpj": "CNPJ do emitente",
|
|
6596
|
+
"tomador.cpfCnpj": "CPF/CNPJ do cliente",
|
|
6597
|
+
"tomador.razaoSocial": "Nome do cliente",
|
|
6598
|
+
"tomador.endereco.cep": "CEP do cliente",
|
|
6599
|
+
"tomador.endereco.codigoCidade": "Munic\xEDpio do cliente",
|
|
6600
|
+
"servico.codigo": "C\xF3digo de tributa\xE7\xE3o do servi\xE7o",
|
|
6601
|
+
"servico.iss.aliquota": "Al\xEDquota de ISS",
|
|
6602
|
+
"itens.ncm": "NCM do produto",
|
|
6603
|
+
"itens.cfop": "CFOP do produto",
|
|
6604
|
+
"pagamentos.meio": "Forma de pagamento (tPag)",
|
|
6605
|
+
"emitente.inscricaoMunicipal": "Inscri\xE7\xE3o municipal",
|
|
6606
|
+
"emitente.inscricaoEstadual": "Inscri\xE7\xE3o estadual"
|
|
6607
|
+
};
|
|
6608
|
+
function explainRejection(rejection) {
|
|
6609
|
+
if (!rejection) return { summary: "Nota rejeitada sem detalhe do motivo.", fields: [] };
|
|
6610
|
+
const r = Array.isArray(rejection) ? rejection[0] : rejection;
|
|
6611
|
+
const err = r?.error ?? r;
|
|
6612
|
+
let message = String(err?.message || r?.mensagem || r?.motivo || "Nota rejeitada.");
|
|
6613
|
+
const embedded = message.match(/\[\s*\{[\s\S]*\}\s*\]/);
|
|
6614
|
+
if (embedded) {
|
|
6615
|
+
try {
|
|
6616
|
+
const arr = JSON.parse(embedded[0]);
|
|
6617
|
+
const parts = (Array.isArray(arr) ? arr : [arr]).map((e) => [e.Codigo ?? e.codigo, e.Descricao ?? e.descricao ?? e.Mensagem ?? e.mensagem].filter(Boolean).join(" \u2014 ")).filter(Boolean);
|
|
6618
|
+
if (parts.length) message = parts.join("; ");
|
|
6619
|
+
} catch {
|
|
6620
|
+
}
|
|
6621
|
+
}
|
|
6622
|
+
const hint = SEFAZ_HINTS.find(([code]) => message.includes(code));
|
|
6623
|
+
if (hint) message = `${message} \u2014 ${hint[1]}`;
|
|
6624
|
+
const fieldsObj = err?.data?.fields ?? {};
|
|
6625
|
+
const fields = Object.entries(fieldsObj).map(([field, msg]) => {
|
|
6626
|
+
const normalized = field.replace(/\[\d+\]/g, "").replace(/\.\d+\./g, ".");
|
|
6627
|
+
return { field, label: FIELD_LABELS[normalized] ?? field, message: String(msg) };
|
|
6628
|
+
});
|
|
6629
|
+
return { summary: message, fields };
|
|
6630
|
+
}
|
|
6631
|
+
function recipientLabel(recipient) {
|
|
6632
|
+
if (!recipient) return "Consumidor final";
|
|
6633
|
+
if (recipient.consumidor) return "Consumidor final";
|
|
6634
|
+
return String(recipient.razaoSocial ?? recipient.nome ?? recipient.cpfCnpj ?? "Consumidor final");
|
|
6635
|
+
}
|
|
6636
|
+
var TONE_VARIANT = {
|
|
6637
|
+
success: "success",
|
|
6638
|
+
warning: "warning",
|
|
6639
|
+
danger: "destructive",
|
|
6640
|
+
muted: "default"
|
|
6641
|
+
};
|
|
6642
|
+
var dateTime = (v) => v ? new Date(v).toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "short" }) : "\u2014";
|
|
6643
|
+
var dayOrDateTime = (v) => {
|
|
6644
|
+
if (!v) return "\u2014";
|
|
6645
|
+
const d = new Date(v);
|
|
6646
|
+
return d.getHours() === 0 && d.getMinutes() === 0 && d.getSeconds() === 0 ? d.toLocaleDateString("pt-BR") : dateTime(v);
|
|
6647
|
+
};
|
|
6648
|
+
var money4 = (v) => v == null ? "\u2014" : v.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
|
|
6649
|
+
function buttonHint(s) {
|
|
6650
|
+
const parts = [s.hasValid ? "Notas fiscais desta conta \u2014 h\xE1 uma nota v\xE1lida" : "Notas fiscais desta conta"];
|
|
6651
|
+
if (s.withoutEffect > 0) parts.push(`${s.withoutEffect} cancelada(s), rejeitada(s) ou com erro`);
|
|
6652
|
+
if (s.validIsTest) parts.push("a nota v\xE1lida \xE9 de TESTE, sem valor fiscal");
|
|
6653
|
+
return parts.join(" \xB7 ");
|
|
6654
|
+
}
|
|
6655
|
+
function InvoiceNotesButton({ invoiceId, direction, cancelled }) {
|
|
6656
|
+
const [notes, setNotes] = useState(null);
|
|
6657
|
+
const [open, setOpen] = useState(false);
|
|
6658
|
+
const can = usePermissionOptional();
|
|
6659
|
+
const canCancel = can("financial", "edit");
|
|
6660
|
+
const reload = useCallback(async () => {
|
|
6661
|
+
if (!invoiceId || direction !== "credit") return;
|
|
6662
|
+
try {
|
|
6663
|
+
setNotes(await listInvoiceNotes(invoiceId));
|
|
6664
|
+
} catch {
|
|
6665
|
+
setNotes((prev) => prev ?? []);
|
|
6666
|
+
}
|
|
6667
|
+
}, [invoiceId, direction]);
|
|
6668
|
+
useEffect(() => {
|
|
6669
|
+
void reload();
|
|
6670
|
+
}, [reload]);
|
|
6671
|
+
if (!notes || notes.length === 0) return null;
|
|
6672
|
+
const summary = summarizeNotes(notes);
|
|
6673
|
+
const state = notesButtonState(summary);
|
|
6674
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6675
|
+
/* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
6676
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "relative h-9 gap-1.5", "aria-label": "Nota Fiscal", onClick: () => setOpen(true), children: [
|
|
6677
|
+
state === "authorized" ? /* @__PURE__ */ jsx(CheckCircle2, { className: "h-4 w-4 text-success", "aria-hidden": true }) : state === "processing" ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-warning", "aria-hidden": true }) : /* @__PURE__ */ jsx(FileCheck2, { className: "h-4 w-4", "aria-hidden": true }),
|
|
6678
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Nota Fiscal" }),
|
|
6679
|
+
summary.withoutEffect > 0 && /* @__PURE__ */ jsx("span", { className: "inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-destructive px-1 text-[10px] font-bold text-destructive-foreground", children: summary.withoutEffect }),
|
|
6680
|
+
summary.validIsTest && /* @__PURE__ */ jsx("span", { className: "rounded border border-destructive px-1 text-[10px] font-semibold text-destructive", children: "TESTE" })
|
|
6681
|
+
] }) }),
|
|
6682
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: buttonHint(summary) })
|
|
6683
|
+
] }) }),
|
|
6684
|
+
/* @__PURE__ */ jsx(Modal, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(ModalContent, { size: "2xl", children: [
|
|
6685
|
+
/* @__PURE__ */ jsxs(ModalHeader, { children: [
|
|
6686
|
+
/* @__PURE__ */ jsx(ModalTitle, { children: "Nota Fiscal" }),
|
|
6687
|
+
/* @__PURE__ */ jsx(ModalDescription, { children: "Notas emitidas para esta conta." })
|
|
6688
|
+
] }),
|
|
6689
|
+
/* @__PURE__ */ jsxs(ModalBody, { className: "gap-2", children: [
|
|
6690
|
+
notes.map((n) => /* @__PURE__ */ jsx(NoteRow, { note: n, canCancel, onChanged: reload }, n.documentId)),
|
|
6691
|
+
/* @__PURE__ */ jsx("p", { className: "pt-2 text-xs text-muted-foreground", children: "Emitir uma nota nova por aqui chega na pr\xF3xima etapa." })
|
|
6692
|
+
] })
|
|
6693
|
+
] }) })
|
|
6694
|
+
] });
|
|
6695
|
+
}
|
|
6696
|
+
var POLL_DELAYS = [3e3, 5e3, 8e3, 12e3, 2e4];
|
|
6697
|
+
function NoteRow({ note, canCancel, onChanged }) {
|
|
6698
|
+
const [expanded, setExpanded] = useState(false);
|
|
6699
|
+
const [cancelling, setCancelling] = useState(false);
|
|
6700
|
+
const [busy, setBusy] = useState(null);
|
|
6701
|
+
const dimmed = isWithoutEffect(note.status);
|
|
6702
|
+
const tone = (STATUS_META[note.status] ?? STATUS_META.error).tone;
|
|
6703
|
+
const usable = note.status === "authorized" || note.status === "cancelled";
|
|
6704
|
+
const inFlight = note.status === "processing" || note.status === "draft";
|
|
6705
|
+
const cancellable = canCancel && note.status === "authorized" && !note.cancelledAt;
|
|
6706
|
+
const openPdf = async () => {
|
|
6707
|
+
const w = window.open("", "_blank");
|
|
6708
|
+
setBusy("pdf");
|
|
6709
|
+
try {
|
|
6710
|
+
const blob = await fetchNotePdf(note.documentId);
|
|
6711
|
+
const url = URL.createObjectURL(blob);
|
|
6712
|
+
if (w) w.location.href = url;
|
|
6713
|
+
else window.location.assign(url);
|
|
6714
|
+
} catch (e) {
|
|
6715
|
+
w?.close();
|
|
6716
|
+
toast.error("PDF indispon\xEDvel", { description: e instanceof Error ? e.message : String(e) });
|
|
6717
|
+
} finally {
|
|
6718
|
+
setBusy(null);
|
|
6719
|
+
}
|
|
6720
|
+
};
|
|
6721
|
+
const refresh = async () => {
|
|
6722
|
+
setBusy("refresh");
|
|
6723
|
+
try {
|
|
6724
|
+
const status = await refreshNote(note.documentId);
|
|
6725
|
+
await onChanged();
|
|
6726
|
+
toast(status === note.status ? "Ainda em processamento na SEFAZ/prefeitura" : `Nota ${statusLabel({ status, cancelledAt: null }).toLowerCase()}`);
|
|
6727
|
+
} catch (e) {
|
|
6728
|
+
toast.error("N\xE3o foi poss\xEDvel consultar", { description: e instanceof Error ? e.message : String(e) });
|
|
6729
|
+
} finally {
|
|
6730
|
+
setBusy(null);
|
|
6731
|
+
}
|
|
6732
|
+
};
|
|
6733
|
+
return /* @__PURE__ */ jsxs("div", { className: `rounded-lg border ${dimmed ? "bg-muted/30" : ""}`, children: [
|
|
6734
|
+
/* @__PURE__ */ jsxs(
|
|
6735
|
+
"button",
|
|
6736
|
+
{
|
|
6737
|
+
type: "button",
|
|
6738
|
+
className: "flex w-full items-center gap-2 p-3 text-left",
|
|
6739
|
+
onClick: () => setExpanded((v) => !v),
|
|
6740
|
+
"aria-expanded": expanded,
|
|
6741
|
+
children: [
|
|
6742
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2", children: [
|
|
6743
|
+
/* @__PURE__ */ jsx(Badge, { variant: "outline", children: MODEL_LABEL[note.model] ?? note.model }),
|
|
6744
|
+
note.number && /* @__PURE__ */ jsxs("span", { className: "font-mono text-sm", children: [
|
|
6745
|
+
"n\xBA ",
|
|
6746
|
+
note.number
|
|
6747
|
+
] }),
|
|
6748
|
+
/* @__PURE__ */ jsx(Badge, { variant: TONE_VARIANT[tone], children: statusLabel(note) }),
|
|
6749
|
+
note.environment === "homologacao" && /* @__PURE__ */ jsx("span", { className: "rounded border border-destructive px-1 text-[10px] font-semibold text-destructive", children: "TESTE" }),
|
|
6750
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto text-xs tabular-nums text-muted-foreground", children: dateTime(note.createdAt) })
|
|
6751
|
+
] }),
|
|
6752
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: `h-4 w-4 shrink-0 text-muted-foreground transition-transform ${expanded ? "rotate-180" : ""}`, "aria-hidden": true })
|
|
6753
|
+
]
|
|
6754
|
+
}
|
|
6755
|
+
),
|
|
6756
|
+
note.cancelReason && !expanded && /* @__PURE__ */ jsxs("p", { className: "flex items-start gap-1.5 px-3 pb-2 text-xs text-muted-foreground", children: [
|
|
6757
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0", "aria-hidden": true }),
|
|
6758
|
+
/* @__PURE__ */ jsxs("span", { className: "break-words", children: [
|
|
6759
|
+
"Motivo: ",
|
|
6760
|
+
note.cancelReason
|
|
6761
|
+
] })
|
|
6762
|
+
] }),
|
|
6763
|
+
(usable || inFlight || note.cancelledAt) && !cancelling && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 px-3 pb-3", children: [
|
|
6764
|
+
usable && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "h-7 text-xs", disabled: busy === "pdf", onClick: openPdf, children: [
|
|
6765
|
+
busy === "pdf" ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(FileText, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
6766
|
+
"PDF"
|
|
6767
|
+
] }),
|
|
6768
|
+
(inFlight || note.cancelledAt && note.status === "authorized") && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "h-7 text-xs", disabled: busy === "refresh", onClick: refresh, children: [
|
|
6769
|
+
busy === "refresh" ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(RefreshCw, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
6770
|
+
"Atualizar"
|
|
6771
|
+
] }),
|
|
6772
|
+
cancellable && /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", className: "ml-auto h-7 text-xs text-destructive hover:text-destructive", onClick: () => setCancelling(true), children: [
|
|
6773
|
+
/* @__PURE__ */ jsx(XCircle, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
6774
|
+
" Cancelar nota"
|
|
6775
|
+
] })
|
|
6776
|
+
] }),
|
|
6777
|
+
cancelling && /* @__PURE__ */ jsx(CancelNotePanel, { note, onClose: () => setCancelling(false), onDone: async () => {
|
|
6778
|
+
setCancelling(false);
|
|
6779
|
+
await onChanged();
|
|
6780
|
+
} }),
|
|
6781
|
+
expanded && /* @__PURE__ */ jsx(NoteDetails, { note })
|
|
6782
|
+
] });
|
|
6783
|
+
}
|
|
6784
|
+
var NFSE_REASONS = [
|
|
6785
|
+
{ value: "1", label: "Erro na emiss\xE3o" },
|
|
6786
|
+
{ value: "2", label: "Servi\xE7o n\xE3o prestado" },
|
|
6787
|
+
{ value: "9", label: "Outros" }
|
|
6788
|
+
];
|
|
6789
|
+
function CancelNotePanel({ note, onClose, onDone }) {
|
|
6790
|
+
const isNfse = note.model === "nfse";
|
|
6791
|
+
const [code, setCode] = useState("2");
|
|
6792
|
+
const [text, setText] = useState("");
|
|
6793
|
+
const [working, setWorking] = useState(null);
|
|
6794
|
+
const [error, setError] = useState(null);
|
|
6795
|
+
const min = isNfse ? 10 : 15;
|
|
6796
|
+
const missing = Math.max(0, min - text.trim().length);
|
|
6797
|
+
const confirm = async () => {
|
|
6798
|
+
setError(null);
|
|
6799
|
+
setWorking("sending");
|
|
6800
|
+
try {
|
|
6801
|
+
let status = await cancelNote(note.documentId, text.trim(), isNfse ? code : void 0);
|
|
6802
|
+
setWorking("waiting");
|
|
6803
|
+
for (const delay of POLL_DELAYS) {
|
|
6804
|
+
if (status === "cancelled" || status === "authorized") break;
|
|
6805
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
6806
|
+
try {
|
|
6807
|
+
status = await refreshNote(note.documentId);
|
|
6808
|
+
} catch {
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
if (status === "cancelled") toast.success(`${MODEL_LABEL[note.model]} n\xBA ${note.number ?? ""} cancelada`);
|
|
6812
|
+
else if (status === "authorized") toast.error("A SEFAZ/prefeitura recusou o cancelamento", { description: "A nota continua v\xE1lida. Veja o hist\xF3rico nos detalhes." });
|
|
6813
|
+
else toast("Cancelamento solicitado", { description: "A SEFAZ/prefeitura ainda est\xE1 processando. Use \xABAtualizar\xBB para conferir." });
|
|
6814
|
+
await onDone();
|
|
6815
|
+
} catch (e) {
|
|
6816
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
6817
|
+
setWorking(null);
|
|
6818
|
+
}
|
|
6819
|
+
};
|
|
6820
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t bg-destructive-soft/40 px-3 py-3", children: [
|
|
6821
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold", children: [
|
|
6822
|
+
"Cancelar ",
|
|
6823
|
+
MODEL_LABEL[note.model],
|
|
6824
|
+
" ",
|
|
6825
|
+
note.number ? `n\xBA ${note.number}` : ""
|
|
6826
|
+
] }),
|
|
6827
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
6828
|
+
isNfse ? "O prazo de cancelamento da NFS-e \xE9 definido pelo munic\xEDpio." : "O prazo de cancelamento da NFC-e \xE9 curto (em geral ~30 minutos ap\xF3s a autoriza\xE7\xE3o, conforme a UF). Fora do prazo, a SEFAZ recusa.",
|
|
6829
|
+
" ",
|
|
6830
|
+
"Cancelar n\xE3o pode ser desfeito."
|
|
6831
|
+
] }),
|
|
6832
|
+
isNfse && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
6833
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium", children: "Motivo" }),
|
|
6834
|
+
/* @__PURE__ */ jsx(SegmentedControl, { size: "sm", "aria-label": "Motivo do cancelamento", options: NFSE_REASONS, value: code, onChange: setCode })
|
|
6835
|
+
] }),
|
|
6836
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
6837
|
+
/* @__PURE__ */ jsx("label", { htmlFor: `cancel-${note.documentId}`, className: "text-xs font-medium", children: "Justificativa" }),
|
|
6838
|
+
/* @__PURE__ */ jsx(
|
|
6839
|
+
"textarea",
|
|
6840
|
+
{
|
|
6841
|
+
id: `cancel-${note.documentId}`,
|
|
6842
|
+
value: text,
|
|
6843
|
+
onChange: (e) => setText(e.target.value),
|
|
6844
|
+
maxLength: 255,
|
|
6845
|
+
rows: 3,
|
|
6846
|
+
disabled: !!working,
|
|
6847
|
+
placeholder: isNfse ? "Descreva o motivo\u2026" : "Ex.: Emiss\xE3o em duplicidade por erro operacional",
|
|
6848
|
+
className: "w-full resize-y rounded-md border bg-background px-3 py-2 text-sm",
|
|
6849
|
+
autoFocus: true
|
|
6850
|
+
}
|
|
6851
|
+
),
|
|
6852
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: missing > 0 ? `Faltam ${missing} caractere(s) \u2014 a justificativa vai para a SEFAZ/prefeitura.` : "Vai para a SEFAZ/prefeitura junto do pedido." })
|
|
6853
|
+
] }),
|
|
6854
|
+
error && /* @__PURE__ */ jsxs("p", { className: "flex items-start gap-1.5 rounded-md border border-destructive/40 bg-destructive-soft p-2 text-xs text-destructive-soft-foreground", children: [
|
|
6855
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0", "aria-hidden": true }),
|
|
6856
|
+
" ",
|
|
6857
|
+
/* @__PURE__ */ jsx("span", { className: "break-words", children: error })
|
|
6858
|
+
] }),
|
|
6859
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [
|
|
6860
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: !!working, children: "Voltar" }),
|
|
6861
|
+
/* @__PURE__ */ jsxs(Button, { variant: "destructive", size: "sm", onClick: confirm, disabled: !!working || missing > 0, children: [
|
|
6862
|
+
working ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(XCircle, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
6863
|
+
working === "waiting" ? "Aguardando a SEFAZ/prefeitura\u2026" : working ? "Enviando\u2026" : "Confirmar cancelamento"
|
|
6864
|
+
] })
|
|
6865
|
+
] })
|
|
6866
|
+
] });
|
|
6867
|
+
}
|
|
6868
|
+
function NoteDetails({ note }) {
|
|
6869
|
+
const [events, setEvents] = useState(null);
|
|
6870
|
+
useEffect(() => {
|
|
6871
|
+
let alive = true;
|
|
6872
|
+
listNoteEvents(note.documentId).then((e) => {
|
|
6873
|
+
if (alive) setEvents(e);
|
|
6874
|
+
}).catch(() => {
|
|
6875
|
+
if (alive) setEvents([]);
|
|
6876
|
+
});
|
|
6877
|
+
return () => {
|
|
6878
|
+
alive = false;
|
|
6879
|
+
};
|
|
6880
|
+
}, [note.documentId]);
|
|
6881
|
+
const failed = ["rejected", "denied", "error"].includes(note.status);
|
|
6882
|
+
const rejection = failed ? explainRejection(note.rejection) : null;
|
|
6883
|
+
const rows = [
|
|
6884
|
+
["Refer\xEAncia", note.integrationId && /* @__PURE__ */ jsx("span", { className: "break-all font-mono text-xs", children: note.integrationId })],
|
|
6885
|
+
["Chave de acesso", note.accessKey && /* @__PURE__ */ jsx("span", { className: "break-all font-mono text-xs", children: note.accessKey })],
|
|
6886
|
+
["C\xF3digo de verifica\xE7\xE3o", note.verificationCode],
|
|
6887
|
+
["Valor", money4(note.totalValue)],
|
|
6888
|
+
["Tomador", recipientLabel(note.recipient)],
|
|
6889
|
+
["Criada em", dateTime(note.createdAt)],
|
|
6890
|
+
["Autorizada em", note.authorizedAt && dayOrDateTime(note.authorizedAt)],
|
|
6891
|
+
["Cancelada em", note.cancelledAt && `${dateTime(note.cancelledAt)}${note.cancelReason ? ` \u2014 ${note.cancelReason}` : ""}`]
|
|
6892
|
+
];
|
|
6893
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t px-3 py-3", children: [
|
|
6894
|
+
/* @__PURE__ */ jsx("dl", { className: "text-sm", children: rows.filter(([, v]) => v).map(([label, value]) => /* @__PURE__ */ jsxs("div", { className: "flex gap-3 border-b py-1.5 last:border-0", children: [
|
|
6895
|
+
/* @__PURE__ */ jsx("dt", { className: "w-28 shrink-0 text-muted-foreground sm:w-36", children: label }),
|
|
6896
|
+
/* @__PURE__ */ jsx("dd", { className: "min-w-0 flex-1 break-words font-medium", children: value })
|
|
6897
|
+
] }, label)) }),
|
|
6898
|
+
rejection && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 rounded-md border border-destructive/40 bg-destructive-soft p-3", children: [
|
|
6899
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-destructive-soft-foreground", children: "Por que foi rejeitada?" }),
|
|
6900
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-foreground", children: rejection.summary }),
|
|
6901
|
+
rejection.fields.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-xs text-foreground", children: rejection.fields.map((f) => /* @__PURE__ */ jsxs("li", { children: [
|
|
6902
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
6903
|
+
f.label,
|
|
6904
|
+
":"
|
|
6905
|
+
] }),
|
|
6906
|
+
" ",
|
|
6907
|
+
f.message
|
|
6908
|
+
] }, f.field)) })
|
|
6909
|
+
] }),
|
|
6910
|
+
events === null ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
6911
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
|
|
6912
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-2/3" })
|
|
6913
|
+
] }) : events.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
6914
|
+
/* @__PURE__ */ jsx("p", { className: "mb-1.5 text-sm font-semibold", children: "Hist\xF3rico" }),
|
|
6915
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-1", children: events.map((e) => /* @__PURE__ */ jsxs("li", { className: "flex flex-wrap items-center gap-2 text-xs", children: [
|
|
6916
|
+
/* @__PURE__ */ jsx("span", { className: "whitespace-nowrap tabular-nums text-muted-foreground", children: dateTime(e.createdAt) }),
|
|
6917
|
+
/* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-[10px]", children: EVENT_LABEL[e.eventType] ?? "Evento" }),
|
|
6918
|
+
eventStatusLabel(e.status) && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: eventStatusLabel(e.status) }),
|
|
6919
|
+
e.justification && /* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate", children: [
|
|
6920
|
+
"\u2014 ",
|
|
6921
|
+
e.justification
|
|
6922
|
+
] })
|
|
6923
|
+
] }, e.id)) })
|
|
6924
|
+
] })
|
|
6925
|
+
] });
|
|
6926
|
+
}
|
|
6308
6927
|
|
|
6309
6928
|
// src/lib/financial-provider.ts
|
|
6310
6929
|
function fiscalReader(provider) {
|
|
@@ -6578,6 +7197,13 @@ function createPlugNotasPlugin(options = {}) {
|
|
|
6578
7197
|
order: 0,
|
|
6579
7198
|
permission: { feature: "financial", action: "read" }
|
|
6580
7199
|
},
|
|
7200
|
+
{
|
|
7201
|
+
id: "fiscal-br-invoice-notes",
|
|
7202
|
+
zone: FINANCIAL_INVOICE_ZONES.headerActions,
|
|
7203
|
+
component: InvoiceNotesButton,
|
|
7204
|
+
order: 0,
|
|
7205
|
+
permission: { feature: "financial", action: "read" }
|
|
7206
|
+
},
|
|
6581
7207
|
{
|
|
6582
7208
|
id: "fiscal-br-unit-fiscal",
|
|
6583
7209
|
zone: UNIT_DETAIL_ZONES.fiscal,
|
|
@@ -6657,6 +7283,6 @@ function createPlugNotasPlugin(options = {}) {
|
|
|
6657
7283
|
});
|
|
6658
7284
|
}
|
|
6659
7285
|
|
|
6660
|
-
export { InboundNoteDocumentModal, InboundNotesPage, InboundNotesView, IssuedNotesView, ManifestView, NfeParseError, NoteReconcileView, PLUGNOTAS_CONNECTOR_ID, PLUGNOTAS_FIELDS, PLUGNOTAS_SETTINGS, ReturnedNotesView, STOCK_BEARING_ENTRY_TYPES, TaxGroupsView, acceptNote, createPlugNotasConnector, createPlugNotasFinancialProvider, createPlugNotasPlugin, findInboxNoteByNumber, findInboxNoteByOperation, getInboxNote, getInboxNoteXml, inboundNoteDocumentModal, inboundNotesInventoryView, isStockBearing, isValidCnpj, listChartAccounts, listInboxNotes, listMeasurementUnits, listProductsByIds, listStockLocations, listSupplierMap, markPlugNotasEmitterReady, onlyDigits, parseNfeXml, paymentLabel, paymentTypeFromCode, rejectNote, saveNoteDecision, searchProducts, stageNoteFromXml, suggestNoteItems, toStagePayload };
|
|
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 };
|
|
6661
7287
|
//# sourceMappingURL=index.js.map
|
|
6662
7288
|
//# sourceMappingURL=index.js.map
|