@fayz-ai/plugin-fiscal-br 0.7.5 → 0.8.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.
@@ -22,6 +22,9 @@ export declare const MIGRATION_020_A_NOTA_ACHA_O_FORNECEDOR_PELO_CNPJ = "-- A no
22
22
  export declare const MIGRATION_021_USO_E_CONSUMO_ENTRA_NO_ESTOQUE = "-- \"Materiais de Uso e Consumo\" vira um tipo de registro da nota recebida.\n--\n-- \u00C9 o que a casa gasta sem vender nem transformar \u2014 luva, descart\u00E1vel, material\n-- de limpeza \u2014 e que ainda assim se conta no estoque. Entra no estoque como o\n-- insumo: precisa de produto vinculado e gera movimento ao aceitar.\n--\n-- Aditivo e idempotente. A fun\u00E7\u00E3o de aceite \u00E9 REMENDADA, n\u00E3o reescrita (mesmo\n-- padr\u00E3o da 015): copiar o corpo da 010 apagaria o `reason` que a 015 enxertou.\n\nALTER TABLE public.plg_fiscalbr_inbox_items\n DROP CONSTRAINT IF EXISTS plg_fiscalbr_inbox_items_entry_type_chk;\nALTER TABLE public.plg_fiscalbr_inbox_items\n ADD CONSTRAINT plg_fiscalbr_inbox_items_entry_type_chk\n CHECK (entry_type IS NULL OR entry_type IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico'));\n\nALTER TABLE public.plg_fiscalbr_supplier_product_map\n DROP CONSTRAINT IF EXISTS plg_fiscalbr_supplier_product_map_entry_type_chk;\nALTER TABLE public.plg_fiscalbr_supplier_product_map\n ADD CONSTRAINT plg_fiscalbr_supplier_product_map_entry_type_chk\n CHECK (entry_type IS NULL OR entry_type IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico'));\n\nDO $m$\nDECLARE\n v_def text;\n v_antes text := $q$entry_type IN ('insumo','revenda','patrimonio')$q$;\n v_depois text := $q$entry_type IN ('insumo','revenda','patrimonio','uso_consumo')$q$;\nBEGIN\n SELECT pg_get_functiondef(p.oid) INTO v_def\n FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace\n WHERE n.nspname = 'public' AND p.proname = 'fiscalbr_accept_inbox_document';\n\n IF v_def IS NULL THEN\n RAISE EXCEPTION 'fiscalbr/021: fiscalbr_accept_inbox_document n\u00E3o existe \u2014 aplicar 009/010 antes';\n END IF;\n\n IF position('uso_consumo' in v_def) > 0 THEN RETURN; END IF;\n\n IF position(v_antes in v_def) = 0 THEN\n RAISE EXCEPTION 'fiscalbr/021: o corpo de fiscalbr_accept_inbox_document n\u00E3o \u00E9 o esperado \u2014 confira \u00E0 m\u00E3o em vez de deixar passar calado';\n END IF;\n\n EXECUTE replace(v_def, v_antes, v_depois);\nEND\n$m$;\n";
23
23
  export declare const MIGRATION_022_A_NOTA_DO_V1_TRAZ_O_XML = "-- ---------------------------------------------------------------------------\n-- The V1 bridge carries the note's XML.\n-- ---------------------------------------------------------------------------\n-- 003 dropped `xml_content` because, back then, V2 had no column for it (016\n-- added one). Without the file a note copied from the V1 cannot be downloaded,\n-- and a note the V1 never broke into lines arrives with zero items and no way\n-- to rebuild them. Only the mapping changes; rows already copied are not touched.\n-- ---------------------------------------------------------------------------\n\nUPDATE migration.allowlist\n SET columns = columns || jsonb_build_object('xml_content', 'xml_content')\n WHERE source_table = 'dfe_inbox'\n AND columns->>'xml_content' IS DISTINCT FROM 'xml_content';\n";
24
24
  export declare const MIGRATION_023_A_NOTA_ACEITA_SEM_LINHAS_RECEBE_AS_LINHAS = "-- ---------------------------------------------------------------------------\n-- An accepted note that has no lines gets its lines, and nothing else.\n-- ---------------------------------------------------------------------------\n-- Notes copied from the V1 can carry a payable (or stock) and zero lines: the\n-- V1 never split their XML. Restaging is refused for them on purpose (it would\n-- rewrite the decision that became stock and payable), so they showed 0/0\n-- forever. This writes ONLY the missing lines, from the stored XML, and only\n-- while the note has none; the document, its payable and its instalments stay.\n-- ---------------------------------------------------------------------------\n\nCREATE OR REPLACE FUNCTION public.fiscalbr_fill_inbox_lines(p_document_id uuid, p_items jsonb)\nRETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO '' AS $function$\nDECLARE\n v_tenant uuid := public.plg_financial_require_tenant();\n v_doc public.plg_fiscalbr_inbox_documents%ROWTYPE;\n v_cnpj text;\n v_item jsonb;\n v_map public.plg_fiscalbr_supplier_product_map%ROWTYPE;\n v_n integer := 0;\nBEGIN\n SELECT * INTO v_doc FROM public.plg_fiscalbr_inbox_documents\n WHERE tenant_id = v_tenant AND id = p_document_id\n FOR UPDATE; -- two opens of the same note must not both see \"no lines\"\n IF NOT FOUND THEN\n RAISE EXCEPTION 'fiscalbr: nota n\u00E3o encontrada' USING ERRCODE = 'P0002';\n END IF;\n PERFORM public.plg_financial_require_permission('financial.create', v_doc.unit_id);\n\n IF EXISTS (SELECT 1 FROM public.plg_fiscalbr_inbox_items\n WHERE tenant_id = v_tenant AND inbox_document_id = v_doc.id) THEN\n RETURN jsonb_build_object('document_id', v_doc.id, 'items', 0, 'already_had_lines', true);\n END IF;\n\n v_cnpj := public.plg_fiscalbr_digits(v_doc.supplier_cnpj);\n\n FOR v_item IN SELECT * FROM jsonb_array_elements(coalesce(p_items, '[]'::jsonb)) LOOP\n v_n := v_n + 1;\n v_map := NULL;\n SELECT * INTO v_map FROM public.plg_fiscalbr_supplier_product_map\n WHERE tenant_id = v_tenant AND supplier_cnpj = v_cnpj\n AND supplier_product_code = coalesce(v_item->>'supplier_product_code', '');\n\n INSERT INTO public.plg_fiscalbr_inbox_items (\n tenant_id, inbox_document_id, item_number, supplier_product_code, ean_barcode,\n description, ncm, cest, cfop, unit_commercial, quantity_commercial,\n unit_price_commercial, total_price, discount, freight, other_expenses,\n icms_cst, icms_csosn, icms_base, icms_rate, icms_value,\n pis_value, cofins_value, ipi_value, raw_xml_item,\n product_id, entry_type, chart_account_id, stock_unit_id, conversion_factor,\n cost_mode, reconciled)\n VALUES (\n v_tenant, v_doc.id, coalesce((v_item->>'item_number')::int, v_n),\n v_item->>'supplier_product_code', v_item->>'ean_barcode',\n v_item->>'description', v_item->>'ncm', v_item->>'cest', v_item->>'cfop',\n v_item->>'unit', (v_item->>'quantity')::numeric,\n (v_item->>'unit_price')::numeric, (v_item->>'total')::numeric,\n (v_item->>'discount')::numeric, (v_item->>'freight')::numeric,\n (v_item->>'other')::numeric,\n v_item->>'icms_cst', v_item->>'icms_csosn', (v_item->>'icms_base')::numeric,\n (v_item->>'icms_rate')::numeric, (v_item->>'icms_value')::numeric,\n (v_item->>'pis_value')::numeric, (v_item->>'cofins_value')::numeric,\n (v_item->>'ipi_value')::numeric, v_item,\n v_map.product_id, v_map.entry_type, v_map.chart_account_id, v_map.stock_unit_id,\n coalesce(v_map.conversion_factor, 1), coalesce(v_map.cost_mode, 'novo'),\n v_map.product_id IS NOT NULL);\n END LOOP;\n\n RETURN jsonb_build_object('document_id', v_doc.id, 'items', v_n, 'already_had_lines', false);\nEND;\n$function$;\n\nREVOKE ALL ON FUNCTION public.fiscalbr_fill_inbox_lines(uuid, jsonb) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.fiscalbr_fill_inbox_lines(uuid, jsonb) TO authenticated;\n";
25
+ export declare const MIGRATION_024_A_NOTA_NAO_APONTA_PARA_CONTA_QUE_SUMIU = "-- ---------------------------------------------------------------------------\n-- A note never points at a payable (or stock movement) that no longer exists.\n-- ---------------------------------------------------------------------------\n-- invoice_id and stock_movement_id cross into other plugins, so they carry no\n-- foreign key. When a re-copy rebuilt a tenant's payables under new ids, the\n-- notes kept the old ones: the screen read \"already accepted\" and hid every\n-- action, while Contas a Pagar had nothing. This heals a note: a dead payable\n-- link is re-pointed to the live payable of the same NF-e when exactly one\n-- exists, otherwise cleared; a dead movement link is cleared, and the note\n-- stops counting as stocked only when that dead movement was all it had. A\n-- cleared note can be accepted again, which writes the payable and the stock.\n-- ---------------------------------------------------------------------------\n\nCREATE OR REPLACE FUNCTION public.plg_fiscalbr_heal_note_links(p_tenant uuid, p_document_id uuid)\nRETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO '' AS $function$\nDECLARE\n v_doc public.plg_fiscalbr_inbox_documents%ROWTYPE;\n v_invoice uuid;\n v_matches integer;\n v_dead_mv integer := 0;\n v_relinked boolean := false;\n v_released boolean := false;\n v_unstocked boolean := false;\nBEGIN\n SELECT * INTO v_doc FROM public.plg_fiscalbr_inbox_documents\n WHERE tenant_id = p_tenant AND id = p_document_id\n FOR UPDATE;\n IF NOT FOUND THEN\n RETURN jsonb_build_object('document_id', p_document_id, 'changed', false);\n END IF;\n\n IF v_doc.invoice_id IS NOT NULL AND NOT EXISTS (\n SELECT 1 FROM public.plg_financial_invoices f WHERE f.id = v_doc.invoice_id) THEN\n -- Same NF-e: the key this plugin writes, or the V1 conciliation's text for the same supplier.\n SELECT count(*), min(f.id::text)::uuid INTO v_matches, v_invoice\n FROM public.plg_financial_invoices f\n WHERE f.tenant_id = p_tenant AND f.direction = 'payable' AND f.cancelled_at IS NULL\n AND (f.idempotency_key = 'danfe:' || v_doc.access_key\n OR (v_doc.supplier_party_id IS NOT NULL\n AND f.counterparty_person_id = v_doc.supplier_party_id\n AND f.description ~ ('NF-e 0*' || ltrim(v_doc.nfe_number, '0') || '(\\D|$)')))\n AND NOT EXISTS (SELECT 1 FROM public.plg_fiscalbr_inbox_documents o\n WHERE o.tenant_id = p_tenant AND o.invoice_id = f.id AND o.id <> v_doc.id);\n IF v_matches = 1 THEN\n v_relinked := true;\n ELSE\n v_invoice := NULL;\n v_released := true;\n END IF;\n UPDATE public.plg_fiscalbr_inbox_documents SET invoice_id = v_invoice, updated_at = now()\n WHERE id = v_doc.id;\n END IF;\n\n UPDATE public.plg_fiscalbr_inbox_items i SET stock_movement_id = NULL, updated_at = now()\n WHERE i.tenant_id = p_tenant AND i.inbox_document_id = v_doc.id\n AND i.stock_movement_id IS NOT NULL\n AND NOT EXISTS (SELECT 1 FROM public.plg_inventory_stock_movements m WHERE m.id = i.stock_movement_id);\n GET DIAGNOSTICS v_dead_mv = ROW_COUNT;\n\n IF v_dead_mv > 0 AND v_doc.stocked_at IS NOT NULL\n AND v_doc.inventory_operation_id IS NULL\n AND NOT EXISTS (SELECT 1 FROM public.plg_fiscalbr_inbox_items i\n WHERE i.tenant_id = p_tenant AND i.inbox_document_id = v_doc.id\n AND i.stock_movement_id IS NOT NULL) THEN\n UPDATE public.plg_fiscalbr_inbox_documents SET stocked_at = NULL, updated_at = now()\n WHERE id = v_doc.id;\n v_unstocked := true;\n END IF;\n\n RETURN jsonb_build_object('document_id', v_doc.id,\n 'changed', v_relinked OR v_released OR v_dead_mv > 0,\n 'relinked', v_relinked, 'released', v_released,\n 'dead_movements', v_dead_mv, 'unstocked', v_unstocked);\nEND;\n$function$;\n\nREVOKE ALL ON FUNCTION public.plg_fiscalbr_heal_note_links(uuid, uuid) FROM PUBLIC, anon, authenticated;\n\n-- The screen heals the note it opens, so a future re-copy cannot strand one again.\nCREATE OR REPLACE FUNCTION public.fiscalbr_heal_inbox_note(p_document_id uuid)\nRETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO '' AS $function$\nDECLARE\n v_tenant uuid := public.plg_financial_require_tenant();\n v_unit uuid;\nBEGIN\n SELECT unit_id INTO v_unit FROM public.plg_fiscalbr_inbox_documents\n WHERE tenant_id = v_tenant AND id = p_document_id;\n IF NOT FOUND THEN\n RAISE EXCEPTION 'fiscalbr: nota n\u00E3o encontrada' USING ERRCODE = 'P0002';\n END IF;\n PERFORM public.plg_financial_require_permission('financial.create', v_unit);\n RETURN public.plg_fiscalbr_heal_note_links(v_tenant, p_document_id);\nEND;\n$function$;\n\nREVOKE ALL ON FUNCTION public.fiscalbr_heal_inbox_note(uuid) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.fiscalbr_heal_inbox_note(uuid) TO authenticated;\n\n-- Heal what is already stranded.\nDO $backfill$\nDECLARE r record;\nBEGIN\n FOR r IN\n SELECT DISTINCT d.tenant_id, d.id\n FROM public.plg_fiscalbr_inbox_documents d\n LEFT JOIN public.plg_fiscalbr_inbox_items i\n ON i.tenant_id = d.tenant_id AND i.inbox_document_id = d.id\n WHERE (d.invoice_id IS NOT NULL\n AND NOT EXISTS (SELECT 1 FROM public.plg_financial_invoices f WHERE f.id = d.invoice_id))\n OR (i.stock_movement_id IS NOT NULL\n AND NOT EXISTS (SELECT 1 FROM public.plg_inventory_stock_movements m WHERE m.id = i.stock_movement_id))\n LOOP\n PERFORM public.plg_fiscalbr_heal_note_links(r.tenant_id, r.id);\n END LOOP;\nEND\n$backfill$;\n";
26
+ export declare const MIGRATION_025_A_CONTA_DA_NOTA_FECHA_COM_O_TOTAL = "-- ---------------------------------------------------------------------------\n-- The payable a note opens lists products as products, and adds up to its total.\n-- ---------------------------------------------------------------------------\n-- Two defects in the lines fiscalbr_accept_inbox_document wrote:\n-- 1. `itemKind` carried the fiscal entry type ('insumo', 'revenda'\u2026), which the\n-- financial screens do not know, so a linked product showed as \"Insumo\"\n-- with no product. The kind is 'product' when the line has one; the entry\n-- type moves to `entryType`, and product/chart account go to their columns.\n-- 2. Only the product lines were written, so freight, insurance, other\n-- expenses, IPI, ICMS-ST and discount were in the total but in no line\n-- (NF-e 1430116: R$ 480,47 owed, R$ 478,47 listed). Each becomes a line,\n-- and whatever still differs from the instalments becomes one more.\n-- ---------------------------------------------------------------------------\n\nCREATE OR REPLACE FUNCTION public.plg_fiscalbr_note_charge_lines(\n p_tenant uuid, p_document_id uuid, p_invoice_id uuid, p_total numeric)\nRETURNS integer LANGUAGE plpgsql SECURITY DEFINER SET search_path TO '' AS $function$\nDECLARE\n v_doc public.plg_fiscalbr_inbox_documents%ROWTYPE;\n v_sum numeric;\n v_rest numeric;\n v_n integer := 0;\n v_c record;\nBEGIN\n SELECT * INTO v_doc FROM public.plg_fiscalbr_inbox_documents\n WHERE tenant_id = p_tenant AND id = p_document_id;\n IF NOT FOUND OR p_invoice_id IS NULL THEN RETURN 0; END IF;\n IF EXISTS (SELECT 1 FROM public.items\n WHERE tenant_id = p_tenant AND invoice_id = p_invoice_id\n AND metadata ? 'noteCharge') THEN\n RETURN 0;\n END IF;\n -- Only a payable that already lists the note's products: a V1 copy with no lines\n -- would get its whole total as one \"difference\" line.\n IF NOT EXISTS (SELECT 1 FROM public.items\n WHERE tenant_id = p_tenant AND invoice_id = p_invoice_id\n AND metadata ? 'cfop') THEN\n RETURN 0;\n END IF;\n\n FOR v_c IN\n SELECT * FROM (VALUES\n (1, 'freight', 'Frete', round(coalesce(v_doc.total_freight, 0), 2)),\n (2, 'insurance', 'Seguro', round(coalesce(v_doc.total_insurance, 0), 2)),\n (3, 'other', 'Outras despesas da nota', round(coalesce(v_doc.total_other, 0), 2)),\n (4, 'ipi', 'IPI', round(coalesce(v_doc.total_ipi_value, 0), 2)),\n (5, 'icms_st', 'ICMS-ST', round(coalesce(v_doc.total_icms_st_value, 0), 2)),\n (6, 'discount', 'Desconto da nota', -round(coalesce(v_doc.total_discount, 0), 2))\n ) AS c(ord, code, label, amount)\n WHERE amount <> 0\n ORDER BY ord\n LOOP\n INSERT INTO public.items (tenant_id, invoice_id, name, quantity, unit_price, total, sort_order, metadata)\n VALUES (p_tenant, p_invoice_id, v_c.label, 1, v_c.amount, v_c.amount, 1000 + v_c.ord,\n jsonb_build_object('itemKind', 'other', 'noteCharge', v_c.code));\n v_n := v_n + 1;\n END LOOP;\n\n SELECT coalesce(sum(total), 0) INTO v_sum FROM public.items\n WHERE tenant_id = p_tenant AND invoice_id = p_invoice_id AND cancelled_at IS NULL;\n v_rest := round(coalesce(p_total, 0) - v_sum, 2);\n IF v_rest <> 0 THEN\n INSERT INTO public.items (tenant_id, invoice_id, name, quantity, unit_price, total, sort_order, metadata)\n VALUES (p_tenant, p_invoice_id, 'Diferen\u00E7a entre a nota e as parcelas', 1, v_rest, v_rest, 1099,\n jsonb_build_object('itemKind', 'other', 'noteCharge', 'instalment_difference'));\n v_n := v_n + 1;\n END IF;\n RETURN v_n;\nEND;\n$function$;\n\nREVOKE ALL ON FUNCTION public.plg_fiscalbr_note_charge_lines(uuid, uuid, uuid, numeric) FROM PUBLIC, anon, authenticated;\n\nDO $m$\nDECLARE\n v_def text;\n v_start integer;\n v_end integer;\n v_marker text := 'INSERT INTO public.items (tenant_id, invoice_id, name, quantity, unit_price, total, sort_order, metadata)';\n v_close text := 'ON CONFLICT DO NOTHING;';\n v_new text := $q$INSERT INTO public.items (tenant_id, invoice_id, product_id, chart_account_id, name, quantity, unit_price, total, sort_order, metadata)\n SELECT v_tenant, v_invoice, i.product_id, coalesce(i.chart_account_id, v_doc.default_chart_account_id),\n i.description, i.quantity_commercial, i.unit_price_commercial,\n round(coalesce(i.total_price, 0), 2), i.item_number,\n jsonb_strip_nulls(jsonb_build_object(\n 'itemKind', CASE WHEN i.product_id IS NOT NULL THEN 'product' ELSE 'other' END,\n 'referenceId', i.product_id, 'entryType', i.entry_type,\n 'chartAccountId', coalesce(i.chart_account_id, v_doc.default_chart_account_id),\n 'ncm', i.ncm, 'cfop', i.cfop,\n 'supplierProductCode', i.supplier_product_code))\n FROM public.plg_fiscalbr_inbox_items i\n WHERE i.tenant_id = v_tenant AND i.inbox_document_id = v_doc.id\n ORDER BY i.item_number\n ON CONFLICT DO NOTHING;\n PERFORM public.plg_fiscalbr_note_charge_lines(v_tenant, v_doc.id, v_invoice, v_total);$q$;\nBEGIN\n SELECT pg_get_functiondef(p.oid) INTO v_def\n FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace\n WHERE n.nspname = 'public' AND p.proname = 'fiscalbr_accept_inbox_document';\n IF v_def IS NULL THEN\n RAISE EXCEPTION 'fiscalbr/025: fiscalbr_accept_inbox_document does not exist';\n END IF;\n IF position('plg_fiscalbr_note_charge_lines' in v_def) > 0 THEN RETURN; END IF;\n\n v_start := position(v_marker in v_def);\n IF v_start = 0 THEN\n RAISE EXCEPTION 'fiscalbr/025: unexpected body of fiscalbr_accept_inbox_document \u2014 check by hand';\n END IF;\n v_end := v_start + position(v_close in substr(v_def, v_start)) - 1 + length(v_close);\n EXECUTE substr(v_def, 1, v_start - 1) || v_new || substr(v_def, v_end);\nEND\n$m$;\n\n-- The payables already opened by notes: same kind/product fix, and the missing charge lines.\nALTER TABLE public.items DISABLE TRIGGER items_invoice_line_immutable;\n\nUPDATE public.items it\n SET product_id = coalesce(it.product_id, (it.metadata->>'referenceId')::uuid),\n metadata = jsonb_set(it.metadata || jsonb_build_object('entryType', it.metadata->>'itemKind'),\n '{itemKind}', '\"product\"'::jsonb, true),\n updated_at = now()\n FROM public.plg_financial_invoices f\n WHERE f.id = it.invoice_id\n AND f.metadata ? 'danfe'\n AND it.metadata->>'itemKind' IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico')\n AND it.metadata->>'referenceId' IS NOT NULL\n AND EXISTS (SELECT 1 FROM public.products p WHERE p.id = (it.metadata->>'referenceId')::uuid);\n\nUPDATE public.items it\n SET metadata = jsonb_set(it.metadata || jsonb_build_object('entryType', it.metadata->>'itemKind'),\n '{itemKind}', '\"other\"'::jsonb, true),\n updated_at = now()\n FROM public.plg_financial_invoices f\n WHERE f.id = it.invoice_id\n AND f.metadata ? 'danfe'\n AND it.metadata->>'itemKind' IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico');\n\nALTER TABLE public.items ENABLE TRIGGER items_invoice_line_immutable;\n\nDO $backfill$\nDECLARE r record;\nBEGIN\n FOR r IN\n SELECT d.tenant_id, d.id, f.id AS invoice_id, f.total\n FROM public.plg_fiscalbr_inbox_documents d\n JOIN public.plg_financial_invoices f ON f.id = d.invoice_id AND f.tenant_id = d.tenant_id\n WHERE f.metadata ? 'danfe' AND f.cancelled_at IS NULL\n LOOP\n PERFORM public.plg_fiscalbr_note_charge_lines(r.tenant_id, r.id, r.invoice_id, r.total);\n END LOOP;\nEND\n$backfill$;\n";
27
+ export declare const MIGRATION_026_O_RECEBIMENTO_PARADO_DIZ_POR_QUE = "-- ---------------------------------------------------------------------------\n-- When notes stop arriving, the emitter row says why.\n-- ---------------------------------------------------------------------------\n-- The SEFAZ pull fails per CNPJ (expired A1 certificate, provider refusal) and\n-- the V1 records only the last failure (integration_sync_state.last_error). The\n-- V2 kept nothing, so a tenant could go two weeks without a note and no screen\n-- would say so. These two columns hold that last failure; the notes list warns\n-- from them and from a stale dfe_last_sync_at.\n-- ---------------------------------------------------------------------------\n\nALTER TABLE public.plg_fiscalbr_emitter_settings\n ADD COLUMN IF NOT EXISTS dfe_last_error text,\n ADD COLUMN IF NOT EXISTS dfe_last_error_at timestamptz;\n\nCOMMENT ON COLUMN public.plg_fiscalbr_emitter_settings.dfe_last_error IS\n 'V1: integration_sync_state.last_error (source = dfe_inbox) \u2014 last failure pulling notes from SEFAZ; null when the last pull worked.';\nCOMMENT ON COLUMN public.plg_fiscalbr_emitter_settings.dfe_last_error_at IS\n 'V1: integration_sync_state.last_synced_at when last_status = error.';\n\nGRANT SELECT (dfe_last_error, dfe_last_error_at) ON public.plg_fiscalbr_emitter_settings TO authenticated;\n";
25
28
  export declare const MIGRATIONS: Array<{
26
29
  id: string;
27
30
  sql: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,2g0BA2jBlC,CAAA;AAED,eAAO,MAAM,0DAA0D,uzRAmMtE,CAAA;AAED,eAAO,MAAM,+CAA+C,m5oBAyW3D,CAAA;AAED,eAAO,MAAM,0CAA0C,o1XAgPtD,CAAA;AAED,eAAO,MAAM,8CAA8C,6qCAsB1D,CAAA;AAED,eAAO,MAAM,2CAA2C,w8dA6MvD,CAAA;AAED,eAAO,MAAM,yCAAyC,42BAkBrD,CAAA;AAED,eAAO,MAAM,oDAAoD,4oDA+BhE,CAAA;AAED,eAAO,MAAM,yDAAyD,otbA0NrE,CAAA;AAED,eAAO,MAAM,gEAAgE,q1+BAoiB5E,CAAA;AAED,eAAO,MAAM,gEAAgE,mqVAgO5E,CAAA;AAED,eAAO,MAAM,8CAA8C,k/HAiF1D,CAAA;AAED,eAAO,MAAM,oDAAoD,w2OA4JhE,CAAA;AAED,eAAO,MAAM,oDAAoD,gvRAwJhE,CAAA;AAED,eAAO,MAAM,kDAAkD,shPAoK9D,CAAA;AAED,eAAO,MAAM,8CAA8C,8/EA0C1D,CAAA;AAED,eAAO,MAAM,yCAAyC,2mJA2ErD,CAAA;AAED,eAAO,MAAM,4CAA4C,68QA0IxD,CAAA;AAED,eAAO,MAAM,0CAA0C,83CAwBtD,CAAA;AAED,eAAO,MAAM,+CAA+C,81JAiG3D,CAAA;AAED,eAAO,MAAM,gDAAgD,y+IAgH5D,CAAA;AAED,eAAO,MAAM,4CAA4C,ylEA4CxD,CAAA;AAED,eAAO,MAAM,qCAAqC,kzBAajD,CAAA;AAED,eAAO,MAAM,uDAAuD,u6HAyEnE,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAyBzD,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,2g0BA2jBlC,CAAA;AAED,eAAO,MAAM,0DAA0D,uzRAmMtE,CAAA;AAED,eAAO,MAAM,+CAA+C,m5oBAyW3D,CAAA;AAED,eAAO,MAAM,0CAA0C,o1XAgPtD,CAAA;AAED,eAAO,MAAM,8CAA8C,6qCAsB1D,CAAA;AAED,eAAO,MAAM,2CAA2C,w8dA6MvD,CAAA;AAED,eAAO,MAAM,yCAAyC,42BAkBrD,CAAA;AAED,eAAO,MAAM,oDAAoD,4oDA+BhE,CAAA;AAED,eAAO,MAAM,yDAAyD,otbA0NrE,CAAA;AAED,eAAO,MAAM,gEAAgE,q1+BAoiB5E,CAAA;AAED,eAAO,MAAM,gEAAgE,mqVAgO5E,CAAA;AAED,eAAO,MAAM,8CAA8C,k/HAiF1D,CAAA;AAED,eAAO,MAAM,oDAAoD,w2OA4JhE,CAAA;AAED,eAAO,MAAM,oDAAoD,gvRAwJhE,CAAA;AAED,eAAO,MAAM,kDAAkD,shPAoK9D,CAAA;AAED,eAAO,MAAM,8CAA8C,8/EA0C1D,CAAA;AAED,eAAO,MAAM,yCAAyC,2mJA2ErD,CAAA;AAED,eAAO,MAAM,4CAA4C,68QA0IxD,CAAA;AAED,eAAO,MAAM,0CAA0C,83CAwBtD,CAAA;AAED,eAAO,MAAM,+CAA+C,81JAiG3D,CAAA;AAED,eAAO,MAAM,gDAAgD,y+IAgH5D,CAAA;AAED,eAAO,MAAM,4CAA4C,ylEA4CxD,CAAA;AAED,eAAO,MAAM,qCAAqC,kzBAajD,CAAA;AAED,eAAO,MAAM,uDAAuD,u6HAyEnE,CAAA;AAED,eAAO,MAAM,oDAAoD,6/KAoHhE,CAAA;AAED,eAAO,MAAM,+CAA+C,uqPAwJ3D,CAAA;AAED,eAAO,MAAM,8CAA8C,myCAoB1D,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CA4BzD,CAAA"}
package/dist/types.d.ts CHANGED
@@ -58,7 +58,7 @@ export type InboxEntryType = 'insumo' | 'revenda' | 'patrimonio' | 'uso_consumo'
58
58
  export declare const STOCK_BEARING_ENTRY_TYPES: readonly InboxEntryType[];
59
59
  export declare const isStockBearing: (type: string | null | undefined) => boolean;
60
60
  export type InboxCostMode = 'novo' | 'media';
61
- export type InboxStatusFilter = 'all' | 'pending' | 'stocked' | 'rejected' | 'to_manifest';
61
+ export type InboxStatusFilter = 'all' | 'pending' | 'stocked' | 'rejected' | 'manifest_error';
62
62
  export interface InboxNote {
63
63
  id: string;
64
64
  accessKey: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"InboundNotesPage.d.ts","sourceRoot":"","sources":["../../src/views/InboundNotesPage.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAA2C,MAAM,OAAO,CAAA;AA4B/D,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;CAC7C;AAKD,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,GAAE,qBAA0B,qBA0F5E;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,KAAK,CAAC,YAAY,CAInG"}
1
+ {"version":3,"file":"InboundNotesPage.d.ts","sourceRoot":"","sources":["../../src/views/InboundNotesPage.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAuB/D,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;CAC7C;AAKD,wBAAgB,gBAAgB,CAAC,EAAE,YAAkD,EAAE,GAAE,qBAA0B,qBA6ElH;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,KAAK,CAAC,YAAY,CAInG"}
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  export interface InboundNotesViewProps {
3
3
  /** Abre a conciliação da nota. A rota é do host — esta tela não sabe navegar. */
4
4
  onOpenNote: (noteId: string) => void;
5
- onOpenManifest?: () => void;
6
5
  }
7
- export declare function InboundNotesView({ onOpenNote, onOpenManifest }: InboundNotesViewProps): React.JSX.Element;
6
+ export declare function InboundNotesView({ onOpenNote }: InboundNotesViewProps): React.JSX.Element;
8
7
  //# sourceMappingURL=InboundNotesView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InboundNotesView.d.ts","sourceRoot":"","sources":["../../src/views/InboundNotesView.tsx"],"names":[],"mappings":"AAYA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAkDhF,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,qBAAqB,qBAicrF"}
1
+ {"version":3,"file":"InboundNotesView.d.ts","sourceRoot":"","sources":["../../src/views/InboundNotesView.tsx"],"names":[],"mappings":"AAYA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAoDhF,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,EAAE,qBAAqB,qBA6frE"}
@@ -1 +1 @@
1
- {"version":3,"file":"NoteReconcileView.d.ts","sourceRoot":"","sources":["../../src/views/NoteReconcileView.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAoD,MAAM,OAAO,CAAA;AAgFxE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,uFAAuF;IACvF,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC1B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAe,EAAE,EAAE,sBAAsB,qBAopC3G"}
1
+ {"version":3,"file":"NoteReconcileView.d.ts","sourceRoot":"","sources":["../../src/views/NoteReconcileView.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAoD,MAAM,OAAO,CAAA;AAgFxE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,uFAAuF;IACvF,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC1B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAe,EAAE,EAAE,sBAAsB,qBAitC3G"}
@@ -1 +1 @@
1
- {"version":3,"file":"UnitFiscalWidgets.d.ts","sourceRoot":"","sources":["../../src/views/UnitFiscalWidgets.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAgCzB,UAAU,QAAQ;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAmID,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,4BA+ElE;AA6BD,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,4BA+J7D"}
1
+ {"version":3,"file":"UnitFiscalWidgets.d.ts","sourceRoot":"","sources":["../../src/views/UnitFiscalWidgets.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AA+BzB,UAAU,QAAQ;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAkJD,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,4BAgHlE;AA6BD,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,4BA+J7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"inventoryView.d.ts","sourceRoot":"","sources":["../../src/views/inventoryView.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,EAAoB,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAIjF,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,qBAA0B;;;;;;;;;;;EA6B5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IAC9D,QAAQ,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5E,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,qBA2DA;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACzD,KAAK,EAAE,MAAM,IAAI,GAChB,KAAK,CAAC,SAAS,GAAG,SAAS,CAG7B"}
1
+ {"version":3,"file":"inventoryView.d.ts","sourceRoot":"","sources":["../../src/views/inventoryView.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,EAAoB,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAIjF,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,qBAA0B;;;;;;;;;;;EA6B5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IAC9D,QAAQ,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5E,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,qBAkEA;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACzD,KAAK,EAAE,MAAM,IAAI,GAChB,KAAK,CAAC,SAAS,GAAG,SAAS,CAG7B"}
@@ -0,0 +1,162 @@
1
+ // fiscalbr-certificate — send a CNPJ's A1 certificate (.pfx + password) to PlugNotas.
2
+ //
3
+ // Env: PLUGNOTAS_API_KEY (server-only), PLUGNOTAS_BASE_URL (optional), SUPABASE_URL, SUPABASE_ANON_KEY.
4
+ //
5
+ // Port of the V1 `dfe-plugnotas-certificate`, with two differences:
6
+ // - Validity and holder are read from the file itself. The V1 took them from
7
+ // PlugNotas' answer, which does not carry them in the shape it read, so no
8
+ // company in the V1 ever had an expiry date.
9
+ // - A CNPJ that already has a certificate there is REPLACED in place (same
10
+ // id). The V1 hourly sync re-registers the company with the id it stored; a
11
+ // new id would leave that sync patching the expired one forever.
12
+ // The file is never stored. The row is written with the caller's JWT, so the
13
+ // emitter-settings policy (settings.manage) decides who may do this.
14
+ import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.45.0'
15
+ import forge from 'npm:node-forge@1.3.1'
16
+
17
+ const corsHeaders = {
18
+ 'Access-Control-Allow-Origin': '*',
19
+ 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
20
+ }
21
+
22
+ const json = (body: unknown, status = 200) =>
23
+ new Response(JSON.stringify(body), { status, headers: { ...corsHeaders, 'Content-Type': 'application/json' } })
24
+
25
+ // deno-lint-ignore no-explicit-any
26
+ type Any = any
27
+
28
+ const API = () => (Deno.env.get('PLUGNOTAS_BASE_URL') || 'https://api.plugnotas.com.br').replace(/\/$/, '')
29
+ const digits = (v: unknown) => String(v ?? '').replace(/\D/g, '')
30
+
31
+ function parse(text: string): Any {
32
+ try { return JSON.parse(text) } catch { return null }
33
+ }
34
+
35
+ function bytesFromBase64(b64: string): Uint8Array {
36
+ const bin = atob(b64.replace(/^data:[^;]+;base64,/, ''))
37
+ const out = new Uint8Array(bin.length)
38
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i)
39
+ return out
40
+ }
41
+
42
+ /** Holder, CNPJ and validity from the .pfx; throws a pt-BR message on a wrong password or a bad file. */
43
+ function readPfx(bytes: Uint8Array, password: string): { cn: string; cnpj: string; notAfter: Date } {
44
+ let p12: Any
45
+ try {
46
+ let bin = ''
47
+ for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])
48
+ p12 = forge.pkcs12.pkcs12FromAsn1(forge.asn1.fromDer(bin), password)
49
+ } catch (e) {
50
+ const msg = String((e as Error)?.message ?? e)
51
+ throw new Error(/mac|password|decrypt/i.test(msg)
52
+ ? 'A senha não abre este certificado. Confira a senha do arquivo .pfx.'
53
+ : 'Este arquivo não é um certificado A1 (.pfx) válido.')
54
+ }
55
+ const bags = p12.getBags({ bagType: forge.pki.oids.certBag })[forge.pki.oids.certBag] ?? []
56
+ // The holder's certificate is the one whose subject carries a CNPJ; the rest is the chain.
57
+ const certs = bags.map((b: Any) => b.cert).filter(Boolean)
58
+ const cnOf = (c: Any) => String(c.subject.getField('CN')?.value ?? '')
59
+ const holder = certs.find((c: Any) => /\d{14}/.test(cnOf(c))) ?? certs[0]
60
+ if (!holder) throw new Error('O arquivo não traz nenhum certificado.')
61
+ const cn = cnOf(holder)
62
+ return { cn, cnpj: (cn.match(/(\d{14})/) ?? [])[1] ?? '', notAfter: holder.validity.notAfter }
63
+ }
64
+
65
+ async function sendToProvider(apiKey: string, bytes: Uint8Array, password: string, fileName: string, existingId: string | null) {
66
+ const form = () => {
67
+ const f = new FormData()
68
+ f.append('arquivo', new Blob([bytes], { type: 'application/x-pkcs12' }), fileName)
69
+ f.append('senha', password)
70
+ return f
71
+ }
72
+ if (existingId) {
73
+ const put = await fetch(`${API()}/certificado/${encodeURIComponent(existingId)}`, {
74
+ method: 'PUT', headers: { 'x-api-key': apiKey, Accept: 'application/json' }, body: form(),
75
+ })
76
+ const text = await put.text()
77
+ if (put.ok) return { id: String(parse(text)?.data?.id || parse(text)?.id || existingId), replaced: true }
78
+ // Gone on their side (or no in-place update): register a new one below. Anything else is the provider refusing.
79
+ if (put.status !== 404 && put.status !== 405) throw new Error(`O serviço fiscal recusou o certificado (${put.status}): ${text.slice(0, 300)}`)
80
+ }
81
+ const post = await fetch(`${API()}/certificado`, {
82
+ method: 'POST', headers: { 'x-api-key': apiKey, Accept: 'application/json' }, body: form(),
83
+ })
84
+ const text = await post.text()
85
+ if (!post.ok) throw new Error(`O serviço fiscal recusou o certificado (${post.status}): ${text.slice(0, 300)}`)
86
+ const body = parse(text)
87
+ const id = String(body?.data?.id || body?.id || '')
88
+ if (!id) throw new Error('O serviço fiscal aceitou o arquivo mas não devolveu o identificador do certificado.')
89
+ return { id, replaced: false }
90
+ }
91
+
92
+ /** Points the company registered at PlugNotas to the certificate and keeps DF-e reception on. */
93
+ async function attachToCompany(apiKey: string, cnpj: string, certificateId: string): Promise<string | null> {
94
+ const res = await fetch(`${API()}/empresa/${cnpj}`, {
95
+ method: 'PATCH',
96
+ headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json', Accept: 'application/json' },
97
+ body: JSON.stringify({ certificado: certificateId, nfe: { ativo: true, config: { producao: true, dfe: { ativo: true } } } }),
98
+ })
99
+ if (res.ok) return null
100
+ const text = await res.text()
101
+ // Not registered there yet: the first pull registers it with this certificate.
102
+ if (res.status === 404) return null
103
+ return `O certificado foi enviado, mas a empresa no serviço fiscal não aceitou a troca (${res.status}): ${text.slice(0, 300)}`
104
+ }
105
+
106
+ Deno.serve(async (req) => {
107
+ if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders })
108
+ try {
109
+ const apiKey = Deno.env.get('PLUGNOTAS_API_KEY')
110
+ if (!apiKey) return json({ error: 'A conexão com a SEFAZ não está configurada neste ambiente.' }, 503)
111
+ const auth = req.headers.get('Authorization') || ''
112
+ if (!auth) return json({ error: 'Unauthorized' }, 401)
113
+
114
+ const body: Any = await req.json().catch(() => ({}))
115
+ const legalEntityId = typeof body?.legalEntityId === 'string' ? body.legalEntityId : ''
116
+ const password = typeof body?.password === 'string' ? body.password : ''
117
+ const pfx = typeof body?.pfxBase64 === 'string' ? body.pfxBase64 : ''
118
+ if (!legalEntityId || !pfx || !password) return json({ error: 'Envie o arquivo .pfx e a senha.' }, 400)
119
+
120
+ const tenantId = typeof body?.tenantId === 'string' ? body.tenantId : ''
121
+ const db = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_ANON_KEY')!, {
122
+ global: { headers: { Authorization: auth, ...(tenantId ? { 'x-fayz-tenant': tenantId } : {}) } },
123
+ })
124
+ const { data: row, error } = await db
125
+ .from('plg_fiscalbr_emitter_settings')
126
+ .select('id, certificate_provider_id, company_legal_entities!inner(document_number)')
127
+ .eq('legal_entity_id', legalEntityId)
128
+ .maybeSingle()
129
+ if (error) return json({ error: error.message }, 500)
130
+ if (!row) return json({ error: 'Esta empresa ainda não tem configuração fiscal salva.' }, 404)
131
+ const cnpj = digits((row as Any).company_legal_entities?.document_number)
132
+ if (cnpj.length !== 14) return json({ error: 'Salve o CNPJ da empresa antes de enviar o certificado.' }, 422)
133
+
134
+ const bytes = bytesFromBase64(pfx)
135
+ const cert = readPfx(bytes, password)
136
+ if (cert.notAfter.getTime() < Date.now()) {
137
+ return json({ error: `Este certificado já venceu em ${cert.notAfter.toLocaleDateString('pt-BR')}. Envie o certificado renovado.` }, 422)
138
+ }
139
+ if (cert.cnpj && cert.cnpj.slice(0, 8) !== cnpj.slice(0, 8)) {
140
+ return json({ error: `Este certificado é do CNPJ ${cert.cnpj}, não desta empresa (${cnpj}).` }, 422)
141
+ }
142
+
143
+ const sent = await sendToProvider(apiKey, bytes, password, String(body?.fileName || 'certificado.pfx'), (row as Any).certificate_provider_id || null)
144
+ const warning = await attachToCompany(apiKey, cnpj, sent.id)
145
+
146
+ const now = new Date().toISOString()
147
+ const { error: upd } = await db.from('plg_fiscalbr_emitter_settings').update({
148
+ certificate_provider_id: sent.id,
149
+ certificate_cn: cert.cn || null,
150
+ certificate_expires_at: cert.notAfter.toISOString(),
151
+ certificate_uploaded_at: now,
152
+ dfe_sync_enabled: true,
153
+ dfe_last_error: warning,
154
+ dfe_last_error_at: warning ? now : null,
155
+ }).eq('id', (row as Any).id)
156
+ if (upd) return json({ error: `O certificado foi enviado, mas não consegui salvar aqui: ${upd.message}` }, 403)
157
+
158
+ return json({ ok: true, cn: cert.cn, expiresAt: cert.notAfter.toISOString(), replaced: sent.replaced, warning })
159
+ } catch (e) {
160
+ return json({ error: e instanceof Error ? e.message : String(e) }, 400)
161
+ }
162
+ })
@@ -76,6 +76,91 @@ async function downloadXml(apiKey: string, cnpj: string, key: string): Promise<{
76
76
  return { xml: '', summaryOnly }
77
77
  }
78
78
 
79
+ const isoDay = (d: Date) => d.toISOString().slice(0, 10)
80
+
81
+ /** One page-walk of /nfe/destinada for a CNPJ and window (the route pages 25 at a time by hash). */
82
+ async function listDestinadas(apiKey: string, cnpj: string, from: string, to: string): Promise<string[]> {
83
+ const base = new URLSearchParams({ cpfCnpj: cnpj, resume: 'true', dataInicial: from, dataFinal: to })
84
+ const keys: string[] = []
85
+ const seen = new Set<string>()
86
+ let url = `${API()}/nfe/destinada?${base}`
87
+ for (let page = 0; url && page < 20; page++) {
88
+ let res: Response | null = null
89
+ let text = ''
90
+ for (let attempt = 0; attempt < 3; attempt++) {
91
+ res = await fetch(url, { headers: { 'x-api-key': apiKey, Accept: 'application/json' } })
92
+ text = await res.text()
93
+ if (res.ok || res.status < 500) break
94
+ await new Promise((r) => setTimeout(r, 1500 * (attempt + 1)))
95
+ }
96
+ if (!res?.ok) {
97
+ throw new Error(deepString(parse(text), ['message', 'mensagem', 'error']) || `PlugNotas ${res?.status}`)
98
+ }
99
+ const json = parse(text)
100
+ const docs: Any[] = Array.isArray(json) ? json : (json?.documentos || json?.notas || json?.data || json?.items || json?.resultado || [])
101
+ for (const d of docs) {
102
+ const k = digits(d?.chave || d?.chaveNFe || d?.chNFe)
103
+ if (k.length === 44) keys.push(k)
104
+ }
105
+ const next = deepString(json, ['hashProximaPagina', 'proximaPagina', 'nextPage'])
106
+ if (next && !seen.has(next)) {
107
+ seen.add(next)
108
+ const p = new URLSearchParams(base)
109
+ p.set('hashProximaPagina', next)
110
+ url = `${API()}/nfe/destinada?${p}`
111
+ } else url = ''
112
+ }
113
+ return keys
114
+ }
115
+
116
+ /**
117
+ * The access keys SEFAZ delivered to this account's CNPJs since `since` (max 30
118
+ * days back). The browser stages the ones it does not have through the same
119
+ * by-key path, so permissions and the de-para stay that path's. Each CNPJ's
120
+ * outcome is written back to its emitter row, which is what the notes list
121
+ * reads to say "intake stopped".
122
+ */
123
+ async function listNew(apiKey: string, auth: string, body: Any): Promise<Response> {
124
+ const tenantId = typeof body?.tenantId === 'string' ? body.tenantId : ''
125
+ const db = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_ANON_KEY')!, {
126
+ global: { headers: { Authorization: auth, ...(tenantId ? { 'x-fayz-tenant': tenantId } : {}) } },
127
+ })
128
+ const { data: rows, error } = await db
129
+ .from('plg_fiscalbr_emitter_settings')
130
+ .select('id, legal_entity_id, certificate_provider_id, company_legal_entities!inner(document_number)')
131
+ .eq('dfe_sync_enabled', true)
132
+ if (error) return json({ error: error.message }, 500)
133
+ // Health columns only, on rows the caller just proved it can read; a reader
134
+ // without settings.manage must still leave "last pull / last error" behind.
135
+ const admin = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!)
136
+
137
+ const today = new Date()
138
+ const floor = new Date(today.getTime() - 30 * 86_400_000)
139
+ const asked = typeof body?.since === 'string' ? new Date(body.since) : floor
140
+ const from = isoDay(isNaN(asked.getTime()) || asked < floor ? floor : asked)
141
+ const to = isoDay(today)
142
+
143
+ const found: Array<{ key: string; legalEntityId: string }> = []
144
+ const failures: Array<{ legalEntityId: string; error: string }> = []
145
+ for (const r of rows ?? []) {
146
+ const cnpj = digits((r as Any).company_legal_entities?.document_number)
147
+ if (!(r as Any).certificate_provider_id || cnpj.length !== 14) continue
148
+ try {
149
+ for (const key of await listDestinadas(apiKey, cnpj, from, to)) found.push({ key, legalEntityId: (r as Any).legal_entity_id })
150
+ await admin.from('plg_fiscalbr_emitter_settings')
151
+ .update({ dfe_last_sync_at: new Date().toISOString(), dfe_last_error: null, dfe_last_error_at: null })
152
+ .eq('id', (r as Any).id)
153
+ } catch (e) {
154
+ const message = e instanceof Error ? e.message : String(e)
155
+ failures.push({ legalEntityId: (r as Any).legal_entity_id, error: message })
156
+ await admin.from('plg_fiscalbr_emitter_settings')
157
+ .update({ dfe_last_error: message.slice(0, 500), dfe_last_error_at: new Date().toISOString() })
158
+ .eq('id', (r as Any).id)
159
+ }
160
+ }
161
+ return json({ from, to, notes: found, failures })
162
+ }
163
+
79
164
  Deno.serve(async (req) => {
80
165
  if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders })
81
166
  try {
@@ -85,6 +170,7 @@ Deno.serve(async (req) => {
85
170
  if (!auth) return json({ error: 'Unauthorized' }, 401)
86
171
 
87
172
  const body: Any = await req.json().catch(() => ({}))
173
+ if (body?.action === 'list') return await listNew(apiKey, auth, body)
88
174
  const key = digits(body?.key)
89
175
  if (key.length !== 44) return json({ error: 'A chave de acesso da NF-e tem 44 dígitos.' }, 400)
90
176
  if (!isValidAccessKey(key)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fayz-ai/plugin-fiscal-br",
3
- "version": "0.7.5",
3
+ "version": "0.8.0",
4
4
  "description": "[preview] Brazilian fiscal addon for asynchronous NFC-e emission through PlugNotas",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -24,10 +24,10 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@tanstack/react-table": "^8.20.0",
27
- "@fayz-ai/admin": "^0.46.1",
28
- "@fayz-ai/core": "^0.44.0",
29
- "@fayz-ai/plugin-financial": "^0.23.3",
30
- "@fayz-ai/ui": "^0.46.1"
27
+ "@fayz-ai/admin": "^0.47.0",
28
+ "@fayz-ai/core": "^0.47.0",
29
+ "@fayz-ai/plugin-financial": "^0.23.4",
30
+ "@fayz-ai/ui": "^0.47.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^20.0.0",
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- export interface ManifestViewProps {
3
- onOpenNote: (noteId: string) => void;
4
- /** Volta para o Manifesto de Notas. Sem isto só o «voltar» do navegador saía daqui. */
5
- onBack?: () => void;
6
- }
7
- export declare function ManifestView({ onOpenNote, onBack }: ManifestViewProps): React.JSX.Element;
8
- //# sourceMappingURL=ManifestView.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ManifestView.d.ts","sourceRoot":"","sources":["../../src/views/ManifestView.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAoD,MAAM,OAAO,CAAA;AAoBxE,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,wBAAgB,YAAY,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,iBAAiB,qBAgJrE"}