@fayz-ai/plugin-fiscal-br 0.7.4 → 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.
- package/dist/data/inbox.d.ts +46 -0
- package/dist/data/inbox.d.ts.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +729 -167
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +5 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/views/InboundNotesPage.d.ts.map +1 -1
- package/dist/views/InboundNotesView.d.ts +1 -2
- package/dist/views/InboundNotesView.d.ts.map +1 -1
- package/dist/views/NoteReconcileView.d.ts.map +1 -1
- package/dist/views/PullByKeyDialog.d.ts +3 -1
- package/dist/views/PullByKeyDialog.d.ts.map +1 -1
- package/dist/views/UnitFiscalWidgets.d.ts.map +1 -1
- package/dist/views/inventoryView.d.ts.map +1 -1
- package/functions/fiscalbr-certificate/index.ts +162 -0
- package/functions/fiscalbr-dfe-pull/index.ts +86 -0
- package/package.json +5 -5
- package/dist/views/ManifestView.d.ts +0 -8
- package/dist/views/ManifestView.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { recordRailFields, RECORD_GROUP, activeFacet, describeError, getSupabaseClientOptional, connectionSettings, connectionStatus, definePlugin, createConnectionStore, settingsStatus, maskCpfCnpj } from '@fayz-ai/core';
|
|
2
|
-
import
|
|
3
|
-
import { ModalHeader, ModalTitle, ModalBody, Skeleton, AiActionButton, Dropdown, DropdownTrigger, Button, DropdownContent, DropdownItem, Hint, Card, SearchSelect, Input, EmBreve, DatePicker, ModalFooter, SubpageHeader,
|
|
1
|
+
import { recordRailFields, RECORD_GROUP, activeFacet, describeError, getSupabaseClientOptional, connectionSettings, connectionStatus, definePlugin, createConnectionStore, settingsStatus, maskCpfCnpj, getActiveTenantId as getActiveTenantId$1 } from '@fayz-ai/core';
|
|
2
|
+
import React6, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import { ModalHeader, ModalTitle, ModalBody, Skeleton, AiActionButton, Dropdown, DropdownTrigger, Button, DropdownContent, DropdownItem, Hint, Card, InlineLink, SearchSelect, Input, EmBreve, DatePicker, ModalFooter, SubpageHeader, Modal, ModalContent, ModalDescription, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, useFilterDraft, RowActions, PageHeaderActions, FilterPanel, FilterPanelField, SearchField, TooltipProvider, DataTable, ChevronPager, Tooltip, TooltipTrigger, TooltipContent, 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, PersonLink, getActiveTenantId, useUnitFilter, PageActions, usePermissionOptional, UNIT_DETAIL_ZONES
|
|
6
|
-
import { Loader2, Download, ChevronDown, FileCode2, FileText, RefreshCw, UserPlus, Gift, AlertTriangle, ChevronRight, Check, Trash2, Plus, Image, UploadCloud, Paperclip, ScanLine, Eye, ListChecks,
|
|
5
|
+
import { registerDocumentOpener, PersonLink, getActiveTenantId, useUnitFilter, PageActions, adminNavigateTo, usePermissionOptional, UNIT_DETAIL_ZONES } from '@fayz-ai/admin';
|
|
6
|
+
import { Loader2, Download, ChevronDown, FileCode2, FileText, RefreshCw, UserPlus, Gift, AlertTriangle, ChevronRight, Check, Trash2, Plus, KeyRound, Image, UploadCloud, Paperclip, ScanLine, Eye, ListChecks, FileDown, CheckCircle2, FileCheck2, XCircle, Inbox, X, Info, ShieldCheck } from 'lucide-react';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import { FINANCIAL_INVOICE_ZONES } from '@fayz-ai/plugin-financial';
|
|
9
9
|
|
|
@@ -3929,6 +3929,385 @@ BEGIN
|
|
|
3929
3929
|
END
|
|
3930
3930
|
$m$;
|
|
3931
3931
|
`;
|
|
3932
|
+
var MIGRATION_022_A_NOTA_DO_V1_TRAZ_O_XML = `-- ---------------------------------------------------------------------------
|
|
3933
|
+
-- The V1 bridge carries the note's XML.
|
|
3934
|
+
-- ---------------------------------------------------------------------------
|
|
3935
|
+
-- 003 dropped \`xml_content\` because, back then, V2 had no column for it (016
|
|
3936
|
+
-- added one). Without the file a note copied from the V1 cannot be downloaded,
|
|
3937
|
+
-- and a note the V1 never broke into lines arrives with zero items and no way
|
|
3938
|
+
-- to rebuild them. Only the mapping changes; rows already copied are not touched.
|
|
3939
|
+
-- ---------------------------------------------------------------------------
|
|
3940
|
+
|
|
3941
|
+
UPDATE migration.allowlist
|
|
3942
|
+
SET columns = columns || jsonb_build_object('xml_content', 'xml_content')
|
|
3943
|
+
WHERE source_table = 'dfe_inbox'
|
|
3944
|
+
AND columns->>'xml_content' IS DISTINCT FROM 'xml_content';
|
|
3945
|
+
`;
|
|
3946
|
+
var MIGRATION_023_A_NOTA_ACEITA_SEM_LINHAS_RECEBE_AS_LINHAS = `-- ---------------------------------------------------------------------------
|
|
3947
|
+
-- An accepted note that has no lines gets its lines, and nothing else.
|
|
3948
|
+
-- ---------------------------------------------------------------------------
|
|
3949
|
+
-- Notes copied from the V1 can carry a payable (or stock) and zero lines: the
|
|
3950
|
+
-- V1 never split their XML. Restaging is refused for them on purpose (it would
|
|
3951
|
+
-- rewrite the decision that became stock and payable), so they showed 0/0
|
|
3952
|
+
-- forever. This writes ONLY the missing lines, from the stored XML, and only
|
|
3953
|
+
-- while the note has none; the document, its payable and its instalments stay.
|
|
3954
|
+
-- ---------------------------------------------------------------------------
|
|
3955
|
+
|
|
3956
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_fill_inbox_lines(p_document_id uuid, p_items jsonb)
|
|
3957
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO '' AS $function$
|
|
3958
|
+
DECLARE
|
|
3959
|
+
v_tenant uuid := public.plg_financial_require_tenant();
|
|
3960
|
+
v_doc public.plg_fiscalbr_inbox_documents%ROWTYPE;
|
|
3961
|
+
v_cnpj text;
|
|
3962
|
+
v_item jsonb;
|
|
3963
|
+
v_map public.plg_fiscalbr_supplier_product_map%ROWTYPE;
|
|
3964
|
+
v_n integer := 0;
|
|
3965
|
+
BEGIN
|
|
3966
|
+
SELECT * INTO v_doc FROM public.plg_fiscalbr_inbox_documents
|
|
3967
|
+
WHERE tenant_id = v_tenant AND id = p_document_id
|
|
3968
|
+
FOR UPDATE; -- two opens of the same note must not both see "no lines"
|
|
3969
|
+
IF NOT FOUND THEN
|
|
3970
|
+
RAISE EXCEPTION 'fiscalbr: nota n\xE3o encontrada' USING ERRCODE = 'P0002';
|
|
3971
|
+
END IF;
|
|
3972
|
+
PERFORM public.plg_financial_require_permission('financial.create', v_doc.unit_id);
|
|
3973
|
+
|
|
3974
|
+
IF EXISTS (SELECT 1 FROM public.plg_fiscalbr_inbox_items
|
|
3975
|
+
WHERE tenant_id = v_tenant AND inbox_document_id = v_doc.id) THEN
|
|
3976
|
+
RETURN jsonb_build_object('document_id', v_doc.id, 'items', 0, 'already_had_lines', true);
|
|
3977
|
+
END IF;
|
|
3978
|
+
|
|
3979
|
+
v_cnpj := public.plg_fiscalbr_digits(v_doc.supplier_cnpj);
|
|
3980
|
+
|
|
3981
|
+
FOR v_item IN SELECT * FROM jsonb_array_elements(coalesce(p_items, '[]'::jsonb)) LOOP
|
|
3982
|
+
v_n := v_n + 1;
|
|
3983
|
+
v_map := NULL;
|
|
3984
|
+
SELECT * INTO v_map FROM public.plg_fiscalbr_supplier_product_map
|
|
3985
|
+
WHERE tenant_id = v_tenant AND supplier_cnpj = v_cnpj
|
|
3986
|
+
AND supplier_product_code = coalesce(v_item->>'supplier_product_code', '');
|
|
3987
|
+
|
|
3988
|
+
INSERT INTO public.plg_fiscalbr_inbox_items (
|
|
3989
|
+
tenant_id, inbox_document_id, item_number, supplier_product_code, ean_barcode,
|
|
3990
|
+
description, ncm, cest, cfop, unit_commercial, quantity_commercial,
|
|
3991
|
+
unit_price_commercial, total_price, discount, freight, other_expenses,
|
|
3992
|
+
icms_cst, icms_csosn, icms_base, icms_rate, icms_value,
|
|
3993
|
+
pis_value, cofins_value, ipi_value, raw_xml_item,
|
|
3994
|
+
product_id, entry_type, chart_account_id, stock_unit_id, conversion_factor,
|
|
3995
|
+
cost_mode, reconciled)
|
|
3996
|
+
VALUES (
|
|
3997
|
+
v_tenant, v_doc.id, coalesce((v_item->>'item_number')::int, v_n),
|
|
3998
|
+
v_item->>'supplier_product_code', v_item->>'ean_barcode',
|
|
3999
|
+
v_item->>'description', v_item->>'ncm', v_item->>'cest', v_item->>'cfop',
|
|
4000
|
+
v_item->>'unit', (v_item->>'quantity')::numeric,
|
|
4001
|
+
(v_item->>'unit_price')::numeric, (v_item->>'total')::numeric,
|
|
4002
|
+
(v_item->>'discount')::numeric, (v_item->>'freight')::numeric,
|
|
4003
|
+
(v_item->>'other')::numeric,
|
|
4004
|
+
v_item->>'icms_cst', v_item->>'icms_csosn', (v_item->>'icms_base')::numeric,
|
|
4005
|
+
(v_item->>'icms_rate')::numeric, (v_item->>'icms_value')::numeric,
|
|
4006
|
+
(v_item->>'pis_value')::numeric, (v_item->>'cofins_value')::numeric,
|
|
4007
|
+
(v_item->>'ipi_value')::numeric, v_item,
|
|
4008
|
+
v_map.product_id, v_map.entry_type, v_map.chart_account_id, v_map.stock_unit_id,
|
|
4009
|
+
coalesce(v_map.conversion_factor, 1), coalesce(v_map.cost_mode, 'novo'),
|
|
4010
|
+
v_map.product_id IS NOT NULL);
|
|
4011
|
+
END LOOP;
|
|
4012
|
+
|
|
4013
|
+
RETURN jsonb_build_object('document_id', v_doc.id, 'items', v_n, 'already_had_lines', false);
|
|
4014
|
+
END;
|
|
4015
|
+
$function$;
|
|
4016
|
+
|
|
4017
|
+
REVOKE ALL ON FUNCTION public.fiscalbr_fill_inbox_lines(uuid, jsonb) FROM PUBLIC;
|
|
4018
|
+
GRANT EXECUTE ON FUNCTION public.fiscalbr_fill_inbox_lines(uuid, jsonb) TO authenticated;
|
|
4019
|
+
`;
|
|
4020
|
+
var MIGRATION_024_A_NOTA_NAO_APONTA_PARA_CONTA_QUE_SUMIU = `-- ---------------------------------------------------------------------------
|
|
4021
|
+
-- A note never points at a payable (or stock movement) that no longer exists.
|
|
4022
|
+
-- ---------------------------------------------------------------------------
|
|
4023
|
+
-- invoice_id and stock_movement_id cross into other plugins, so they carry no
|
|
4024
|
+
-- foreign key. When a re-copy rebuilt a tenant's payables under new ids, the
|
|
4025
|
+
-- notes kept the old ones: the screen read "already accepted" and hid every
|
|
4026
|
+
-- action, while Contas a Pagar had nothing. This heals a note: a dead payable
|
|
4027
|
+
-- link is re-pointed to the live payable of the same NF-e when exactly one
|
|
4028
|
+
-- exists, otherwise cleared; a dead movement link is cleared, and the note
|
|
4029
|
+
-- stops counting as stocked only when that dead movement was all it had. A
|
|
4030
|
+
-- cleared note can be accepted again, which writes the payable and the stock.
|
|
4031
|
+
-- ---------------------------------------------------------------------------
|
|
4032
|
+
|
|
4033
|
+
CREATE OR REPLACE FUNCTION public.plg_fiscalbr_heal_note_links(p_tenant uuid, p_document_id uuid)
|
|
4034
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO '' AS $function$
|
|
4035
|
+
DECLARE
|
|
4036
|
+
v_doc public.plg_fiscalbr_inbox_documents%ROWTYPE;
|
|
4037
|
+
v_invoice uuid;
|
|
4038
|
+
v_matches integer;
|
|
4039
|
+
v_dead_mv integer := 0;
|
|
4040
|
+
v_relinked boolean := false;
|
|
4041
|
+
v_released boolean := false;
|
|
4042
|
+
v_unstocked boolean := false;
|
|
4043
|
+
BEGIN
|
|
4044
|
+
SELECT * INTO v_doc FROM public.plg_fiscalbr_inbox_documents
|
|
4045
|
+
WHERE tenant_id = p_tenant AND id = p_document_id
|
|
4046
|
+
FOR UPDATE;
|
|
4047
|
+
IF NOT FOUND THEN
|
|
4048
|
+
RETURN jsonb_build_object('document_id', p_document_id, 'changed', false);
|
|
4049
|
+
END IF;
|
|
4050
|
+
|
|
4051
|
+
IF v_doc.invoice_id IS NOT NULL AND NOT EXISTS (
|
|
4052
|
+
SELECT 1 FROM public.plg_financial_invoices f WHERE f.id = v_doc.invoice_id) THEN
|
|
4053
|
+
-- Same NF-e: the key this plugin writes, or the V1 conciliation's text for the same supplier.
|
|
4054
|
+
SELECT count(*), min(f.id::text)::uuid INTO v_matches, v_invoice
|
|
4055
|
+
FROM public.plg_financial_invoices f
|
|
4056
|
+
WHERE f.tenant_id = p_tenant AND f.direction = 'payable' AND f.cancelled_at IS NULL
|
|
4057
|
+
AND (f.idempotency_key = 'danfe:' || v_doc.access_key
|
|
4058
|
+
OR (v_doc.supplier_party_id IS NOT NULL
|
|
4059
|
+
AND f.counterparty_person_id = v_doc.supplier_party_id
|
|
4060
|
+
AND f.description ~ ('NF-e 0*' || ltrim(v_doc.nfe_number, '0') || '(\\D|$)')))
|
|
4061
|
+
AND NOT EXISTS (SELECT 1 FROM public.plg_fiscalbr_inbox_documents o
|
|
4062
|
+
WHERE o.tenant_id = p_tenant AND o.invoice_id = f.id AND o.id <> v_doc.id);
|
|
4063
|
+
IF v_matches = 1 THEN
|
|
4064
|
+
v_relinked := true;
|
|
4065
|
+
ELSE
|
|
4066
|
+
v_invoice := NULL;
|
|
4067
|
+
v_released := true;
|
|
4068
|
+
END IF;
|
|
4069
|
+
UPDATE public.plg_fiscalbr_inbox_documents SET invoice_id = v_invoice, updated_at = now()
|
|
4070
|
+
WHERE id = v_doc.id;
|
|
4071
|
+
END IF;
|
|
4072
|
+
|
|
4073
|
+
UPDATE public.plg_fiscalbr_inbox_items i SET stock_movement_id = NULL, updated_at = now()
|
|
4074
|
+
WHERE i.tenant_id = p_tenant AND i.inbox_document_id = v_doc.id
|
|
4075
|
+
AND i.stock_movement_id IS NOT NULL
|
|
4076
|
+
AND NOT EXISTS (SELECT 1 FROM public.plg_inventory_stock_movements m WHERE m.id = i.stock_movement_id);
|
|
4077
|
+
GET DIAGNOSTICS v_dead_mv = ROW_COUNT;
|
|
4078
|
+
|
|
4079
|
+
IF v_dead_mv > 0 AND v_doc.stocked_at IS NOT NULL
|
|
4080
|
+
AND v_doc.inventory_operation_id IS NULL
|
|
4081
|
+
AND NOT EXISTS (SELECT 1 FROM public.plg_fiscalbr_inbox_items i
|
|
4082
|
+
WHERE i.tenant_id = p_tenant AND i.inbox_document_id = v_doc.id
|
|
4083
|
+
AND i.stock_movement_id IS NOT NULL) THEN
|
|
4084
|
+
UPDATE public.plg_fiscalbr_inbox_documents SET stocked_at = NULL, updated_at = now()
|
|
4085
|
+
WHERE id = v_doc.id;
|
|
4086
|
+
v_unstocked := true;
|
|
4087
|
+
END IF;
|
|
4088
|
+
|
|
4089
|
+
RETURN jsonb_build_object('document_id', v_doc.id,
|
|
4090
|
+
'changed', v_relinked OR v_released OR v_dead_mv > 0,
|
|
4091
|
+
'relinked', v_relinked, 'released', v_released,
|
|
4092
|
+
'dead_movements', v_dead_mv, 'unstocked', v_unstocked);
|
|
4093
|
+
END;
|
|
4094
|
+
$function$;
|
|
4095
|
+
|
|
4096
|
+
REVOKE ALL ON FUNCTION public.plg_fiscalbr_heal_note_links(uuid, uuid) FROM PUBLIC, anon, authenticated;
|
|
4097
|
+
|
|
4098
|
+
-- The screen heals the note it opens, so a future re-copy cannot strand one again.
|
|
4099
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_heal_inbox_note(p_document_id uuid)
|
|
4100
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO '' AS $function$
|
|
4101
|
+
DECLARE
|
|
4102
|
+
v_tenant uuid := public.plg_financial_require_tenant();
|
|
4103
|
+
v_unit uuid;
|
|
4104
|
+
BEGIN
|
|
4105
|
+
SELECT unit_id INTO v_unit FROM public.plg_fiscalbr_inbox_documents
|
|
4106
|
+
WHERE tenant_id = v_tenant AND id = p_document_id;
|
|
4107
|
+
IF NOT FOUND THEN
|
|
4108
|
+
RAISE EXCEPTION 'fiscalbr: nota n\xE3o encontrada' USING ERRCODE = 'P0002';
|
|
4109
|
+
END IF;
|
|
4110
|
+
PERFORM public.plg_financial_require_permission('financial.create', v_unit);
|
|
4111
|
+
RETURN public.plg_fiscalbr_heal_note_links(v_tenant, p_document_id);
|
|
4112
|
+
END;
|
|
4113
|
+
$function$;
|
|
4114
|
+
|
|
4115
|
+
REVOKE ALL ON FUNCTION public.fiscalbr_heal_inbox_note(uuid) FROM PUBLIC;
|
|
4116
|
+
GRANT EXECUTE ON FUNCTION public.fiscalbr_heal_inbox_note(uuid) TO authenticated;
|
|
4117
|
+
|
|
4118
|
+
-- Heal what is already stranded.
|
|
4119
|
+
DO $backfill$
|
|
4120
|
+
DECLARE r record;
|
|
4121
|
+
BEGIN
|
|
4122
|
+
FOR r IN
|
|
4123
|
+
SELECT DISTINCT d.tenant_id, d.id
|
|
4124
|
+
FROM public.plg_fiscalbr_inbox_documents d
|
|
4125
|
+
LEFT JOIN public.plg_fiscalbr_inbox_items i
|
|
4126
|
+
ON i.tenant_id = d.tenant_id AND i.inbox_document_id = d.id
|
|
4127
|
+
WHERE (d.invoice_id IS NOT NULL
|
|
4128
|
+
AND NOT EXISTS (SELECT 1 FROM public.plg_financial_invoices f WHERE f.id = d.invoice_id))
|
|
4129
|
+
OR (i.stock_movement_id IS NOT NULL
|
|
4130
|
+
AND NOT EXISTS (SELECT 1 FROM public.plg_inventory_stock_movements m WHERE m.id = i.stock_movement_id))
|
|
4131
|
+
LOOP
|
|
4132
|
+
PERFORM public.plg_fiscalbr_heal_note_links(r.tenant_id, r.id);
|
|
4133
|
+
END LOOP;
|
|
4134
|
+
END
|
|
4135
|
+
$backfill$;
|
|
4136
|
+
`;
|
|
4137
|
+
var MIGRATION_025_A_CONTA_DA_NOTA_FECHA_COM_O_TOTAL = `-- ---------------------------------------------------------------------------
|
|
4138
|
+
-- The payable a note opens lists products as products, and adds up to its total.
|
|
4139
|
+
-- ---------------------------------------------------------------------------
|
|
4140
|
+
-- Two defects in the lines fiscalbr_accept_inbox_document wrote:
|
|
4141
|
+
-- 1. \`itemKind\` carried the fiscal entry type ('insumo', 'revenda'\u2026), which the
|
|
4142
|
+
-- financial screens do not know, so a linked product showed as "Insumo"
|
|
4143
|
+
-- with no product. The kind is 'product' when the line has one; the entry
|
|
4144
|
+
-- type moves to \`entryType\`, and product/chart account go to their columns.
|
|
4145
|
+
-- 2. Only the product lines were written, so freight, insurance, other
|
|
4146
|
+
-- expenses, IPI, ICMS-ST and discount were in the total but in no line
|
|
4147
|
+
-- (NF-e 1430116: R$ 480,47 owed, R$ 478,47 listed). Each becomes a line,
|
|
4148
|
+
-- and whatever still differs from the instalments becomes one more.
|
|
4149
|
+
-- ---------------------------------------------------------------------------
|
|
4150
|
+
|
|
4151
|
+
CREATE OR REPLACE FUNCTION public.plg_fiscalbr_note_charge_lines(
|
|
4152
|
+
p_tenant uuid, p_document_id uuid, p_invoice_id uuid, p_total numeric)
|
|
4153
|
+
RETURNS integer LANGUAGE plpgsql SECURITY DEFINER SET search_path TO '' AS $function$
|
|
4154
|
+
DECLARE
|
|
4155
|
+
v_doc public.plg_fiscalbr_inbox_documents%ROWTYPE;
|
|
4156
|
+
v_sum numeric;
|
|
4157
|
+
v_rest numeric;
|
|
4158
|
+
v_n integer := 0;
|
|
4159
|
+
v_c record;
|
|
4160
|
+
BEGIN
|
|
4161
|
+
SELECT * INTO v_doc FROM public.plg_fiscalbr_inbox_documents
|
|
4162
|
+
WHERE tenant_id = p_tenant AND id = p_document_id;
|
|
4163
|
+
IF NOT FOUND OR p_invoice_id IS NULL THEN RETURN 0; END IF;
|
|
4164
|
+
IF EXISTS (SELECT 1 FROM public.items
|
|
4165
|
+
WHERE tenant_id = p_tenant AND invoice_id = p_invoice_id
|
|
4166
|
+
AND metadata ? 'noteCharge') THEN
|
|
4167
|
+
RETURN 0;
|
|
4168
|
+
END IF;
|
|
4169
|
+
-- Only a payable that already lists the note's products: a V1 copy with no lines
|
|
4170
|
+
-- would get its whole total as one "difference" line.
|
|
4171
|
+
IF NOT EXISTS (SELECT 1 FROM public.items
|
|
4172
|
+
WHERE tenant_id = p_tenant AND invoice_id = p_invoice_id
|
|
4173
|
+
AND metadata ? 'cfop') THEN
|
|
4174
|
+
RETURN 0;
|
|
4175
|
+
END IF;
|
|
4176
|
+
|
|
4177
|
+
FOR v_c IN
|
|
4178
|
+
SELECT * FROM (VALUES
|
|
4179
|
+
(1, 'freight', 'Frete', round(coalesce(v_doc.total_freight, 0), 2)),
|
|
4180
|
+
(2, 'insurance', 'Seguro', round(coalesce(v_doc.total_insurance, 0), 2)),
|
|
4181
|
+
(3, 'other', 'Outras despesas da nota', round(coalesce(v_doc.total_other, 0), 2)),
|
|
4182
|
+
(4, 'ipi', 'IPI', round(coalesce(v_doc.total_ipi_value, 0), 2)),
|
|
4183
|
+
(5, 'icms_st', 'ICMS-ST', round(coalesce(v_doc.total_icms_st_value, 0), 2)),
|
|
4184
|
+
(6, 'discount', 'Desconto da nota', -round(coalesce(v_doc.total_discount, 0), 2))
|
|
4185
|
+
) AS c(ord, code, label, amount)
|
|
4186
|
+
WHERE amount <> 0
|
|
4187
|
+
ORDER BY ord
|
|
4188
|
+
LOOP
|
|
4189
|
+
INSERT INTO public.items (tenant_id, invoice_id, name, quantity, unit_price, total, sort_order, metadata)
|
|
4190
|
+
VALUES (p_tenant, p_invoice_id, v_c.label, 1, v_c.amount, v_c.amount, 1000 + v_c.ord,
|
|
4191
|
+
jsonb_build_object('itemKind', 'other', 'noteCharge', v_c.code));
|
|
4192
|
+
v_n := v_n + 1;
|
|
4193
|
+
END LOOP;
|
|
4194
|
+
|
|
4195
|
+
SELECT coalesce(sum(total), 0) INTO v_sum FROM public.items
|
|
4196
|
+
WHERE tenant_id = p_tenant AND invoice_id = p_invoice_id AND cancelled_at IS NULL;
|
|
4197
|
+
v_rest := round(coalesce(p_total, 0) - v_sum, 2);
|
|
4198
|
+
IF v_rest <> 0 THEN
|
|
4199
|
+
INSERT INTO public.items (tenant_id, invoice_id, name, quantity, unit_price, total, sort_order, metadata)
|
|
4200
|
+
VALUES (p_tenant, p_invoice_id, 'Diferen\xE7a entre a nota e as parcelas', 1, v_rest, v_rest, 1099,
|
|
4201
|
+
jsonb_build_object('itemKind', 'other', 'noteCharge', 'instalment_difference'));
|
|
4202
|
+
v_n := v_n + 1;
|
|
4203
|
+
END IF;
|
|
4204
|
+
RETURN v_n;
|
|
4205
|
+
END;
|
|
4206
|
+
$function$;
|
|
4207
|
+
|
|
4208
|
+
REVOKE ALL ON FUNCTION public.plg_fiscalbr_note_charge_lines(uuid, uuid, uuid, numeric) FROM PUBLIC, anon, authenticated;
|
|
4209
|
+
|
|
4210
|
+
DO $m$
|
|
4211
|
+
DECLARE
|
|
4212
|
+
v_def text;
|
|
4213
|
+
v_start integer;
|
|
4214
|
+
v_end integer;
|
|
4215
|
+
v_marker text := 'INSERT INTO public.items (tenant_id, invoice_id, name, quantity, unit_price, total, sort_order, metadata)';
|
|
4216
|
+
v_close text := 'ON CONFLICT DO NOTHING;';
|
|
4217
|
+
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)
|
|
4218
|
+
SELECT v_tenant, v_invoice, i.product_id, coalesce(i.chart_account_id, v_doc.default_chart_account_id),
|
|
4219
|
+
i.description, i.quantity_commercial, i.unit_price_commercial,
|
|
4220
|
+
round(coalesce(i.total_price, 0), 2), i.item_number,
|
|
4221
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
4222
|
+
'itemKind', CASE WHEN i.product_id IS NOT NULL THEN 'product' ELSE 'other' END,
|
|
4223
|
+
'referenceId', i.product_id, 'entryType', i.entry_type,
|
|
4224
|
+
'chartAccountId', coalesce(i.chart_account_id, v_doc.default_chart_account_id),
|
|
4225
|
+
'ncm', i.ncm, 'cfop', i.cfop,
|
|
4226
|
+
'supplierProductCode', i.supplier_product_code))
|
|
4227
|
+
FROM public.plg_fiscalbr_inbox_items i
|
|
4228
|
+
WHERE i.tenant_id = v_tenant AND i.inbox_document_id = v_doc.id
|
|
4229
|
+
ORDER BY i.item_number
|
|
4230
|
+
ON CONFLICT DO NOTHING;
|
|
4231
|
+
PERFORM public.plg_fiscalbr_note_charge_lines(v_tenant, v_doc.id, v_invoice, v_total);$q$;
|
|
4232
|
+
BEGIN
|
|
4233
|
+
SELECT pg_get_functiondef(p.oid) INTO v_def
|
|
4234
|
+
FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
4235
|
+
WHERE n.nspname = 'public' AND p.proname = 'fiscalbr_accept_inbox_document';
|
|
4236
|
+
IF v_def IS NULL THEN
|
|
4237
|
+
RAISE EXCEPTION 'fiscalbr/025: fiscalbr_accept_inbox_document does not exist';
|
|
4238
|
+
END IF;
|
|
4239
|
+
IF position('plg_fiscalbr_note_charge_lines' in v_def) > 0 THEN RETURN; END IF;
|
|
4240
|
+
|
|
4241
|
+
v_start := position(v_marker in v_def);
|
|
4242
|
+
IF v_start = 0 THEN
|
|
4243
|
+
RAISE EXCEPTION 'fiscalbr/025: unexpected body of fiscalbr_accept_inbox_document \u2014 check by hand';
|
|
4244
|
+
END IF;
|
|
4245
|
+
v_end := v_start + position(v_close in substr(v_def, v_start)) - 1 + length(v_close);
|
|
4246
|
+
EXECUTE substr(v_def, 1, v_start - 1) || v_new || substr(v_def, v_end);
|
|
4247
|
+
END
|
|
4248
|
+
$m$;
|
|
4249
|
+
|
|
4250
|
+
-- The payables already opened by notes: same kind/product fix, and the missing charge lines.
|
|
4251
|
+
ALTER TABLE public.items DISABLE TRIGGER items_invoice_line_immutable;
|
|
4252
|
+
|
|
4253
|
+
UPDATE public.items it
|
|
4254
|
+
SET product_id = coalesce(it.product_id, (it.metadata->>'referenceId')::uuid),
|
|
4255
|
+
metadata = jsonb_set(it.metadata || jsonb_build_object('entryType', it.metadata->>'itemKind'),
|
|
4256
|
+
'{itemKind}', '"product"'::jsonb, true),
|
|
4257
|
+
updated_at = now()
|
|
4258
|
+
FROM public.plg_financial_invoices f
|
|
4259
|
+
WHERE f.id = it.invoice_id
|
|
4260
|
+
AND f.metadata ? 'danfe'
|
|
4261
|
+
AND it.metadata->>'itemKind' IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico')
|
|
4262
|
+
AND it.metadata->>'referenceId' IS NOT NULL
|
|
4263
|
+
AND EXISTS (SELECT 1 FROM public.products p WHERE p.id = (it.metadata->>'referenceId')::uuid);
|
|
4264
|
+
|
|
4265
|
+
UPDATE public.items it
|
|
4266
|
+
SET metadata = jsonb_set(it.metadata || jsonb_build_object('entryType', it.metadata->>'itemKind'),
|
|
4267
|
+
'{itemKind}', '"other"'::jsonb, true),
|
|
4268
|
+
updated_at = now()
|
|
4269
|
+
FROM public.plg_financial_invoices f
|
|
4270
|
+
WHERE f.id = it.invoice_id
|
|
4271
|
+
AND f.metadata ? 'danfe'
|
|
4272
|
+
AND it.metadata->>'itemKind' IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico');
|
|
4273
|
+
|
|
4274
|
+
ALTER TABLE public.items ENABLE TRIGGER items_invoice_line_immutable;
|
|
4275
|
+
|
|
4276
|
+
DO $backfill$
|
|
4277
|
+
DECLARE r record;
|
|
4278
|
+
BEGIN
|
|
4279
|
+
FOR r IN
|
|
4280
|
+
SELECT d.tenant_id, d.id, f.id AS invoice_id, f.total
|
|
4281
|
+
FROM public.plg_fiscalbr_inbox_documents d
|
|
4282
|
+
JOIN public.plg_financial_invoices f ON f.id = d.invoice_id AND f.tenant_id = d.tenant_id
|
|
4283
|
+
WHERE f.metadata ? 'danfe' AND f.cancelled_at IS NULL
|
|
4284
|
+
LOOP
|
|
4285
|
+
PERFORM public.plg_fiscalbr_note_charge_lines(r.tenant_id, r.id, r.invoice_id, r.total);
|
|
4286
|
+
END LOOP;
|
|
4287
|
+
END
|
|
4288
|
+
$backfill$;
|
|
4289
|
+
`;
|
|
4290
|
+
var MIGRATION_026_O_RECEBIMENTO_PARADO_DIZ_POR_QUE = `-- ---------------------------------------------------------------------------
|
|
4291
|
+
-- When notes stop arriving, the emitter row says why.
|
|
4292
|
+
-- ---------------------------------------------------------------------------
|
|
4293
|
+
-- The SEFAZ pull fails per CNPJ (expired A1 certificate, provider refusal) and
|
|
4294
|
+
-- the V1 records only the last failure (integration_sync_state.last_error). The
|
|
4295
|
+
-- V2 kept nothing, so a tenant could go two weeks without a note and no screen
|
|
4296
|
+
-- would say so. These two columns hold that last failure; the notes list warns
|
|
4297
|
+
-- from them and from a stale dfe_last_sync_at.
|
|
4298
|
+
-- ---------------------------------------------------------------------------
|
|
4299
|
+
|
|
4300
|
+
ALTER TABLE public.plg_fiscalbr_emitter_settings
|
|
4301
|
+
ADD COLUMN IF NOT EXISTS dfe_last_error text,
|
|
4302
|
+
ADD COLUMN IF NOT EXISTS dfe_last_error_at timestamptz;
|
|
4303
|
+
|
|
4304
|
+
COMMENT ON COLUMN public.plg_fiscalbr_emitter_settings.dfe_last_error IS
|
|
4305
|
+
'V1: integration_sync_state.last_error (source = dfe_inbox) \u2014 last failure pulling notes from SEFAZ; null when the last pull worked.';
|
|
4306
|
+
COMMENT ON COLUMN public.plg_fiscalbr_emitter_settings.dfe_last_error_at IS
|
|
4307
|
+
'V1: integration_sync_state.last_synced_at when last_status = error.';
|
|
4308
|
+
|
|
4309
|
+
GRANT SELECT (dfe_last_error, dfe_last_error_at) ON public.plg_fiscalbr_emitter_settings TO authenticated;
|
|
4310
|
+
`;
|
|
3932
4311
|
var MIGRATIONS = [
|
|
3933
4312
|
{ id: "000_baseline", sql: MIGRATION_000_BASELINE },
|
|
3934
4313
|
{ id: "001_preserve_terminal_results_and_recover_leases", sql: MIGRATION_001_PRESERVE_TERMINAL_RESULTS_AND_RECOVER_LEASES },
|
|
@@ -3951,7 +4330,12 @@ var MIGRATIONS = [
|
|
|
3951
4330
|
{ id: "018_a_nota_do_v1_alcanca_a_conta", sql: MIGRATION_018_A_NOTA_DO_V1_ALCANCA_A_CONTA },
|
|
3952
4331
|
{ id: "019_a_nota_emitida_se_cancela_pelo_v2", sql: MIGRATION_019_A_NOTA_EMITIDA_SE_CANCELA_PELO_V2 },
|
|
3953
4332
|
{ id: "020_a_nota_acha_o_fornecedor_pelo_cnpj", sql: MIGRATION_020_A_NOTA_ACHA_O_FORNECEDOR_PELO_CNPJ },
|
|
3954
|
-
{ id: "021_uso_e_consumo_entra_no_estoque", sql: MIGRATION_021_USO_E_CONSUMO_ENTRA_NO_ESTOQUE }
|
|
4333
|
+
{ id: "021_uso_e_consumo_entra_no_estoque", sql: MIGRATION_021_USO_E_CONSUMO_ENTRA_NO_ESTOQUE },
|
|
4334
|
+
{ id: "022_a_nota_do_v1_traz_o_xml", sql: MIGRATION_022_A_NOTA_DO_V1_TRAZ_O_XML },
|
|
4335
|
+
{ id: "023_a_nota_aceita_sem_linhas_recebe_as_linhas", sql: MIGRATION_023_A_NOTA_ACEITA_SEM_LINHAS_RECEBE_AS_LINHAS },
|
|
4336
|
+
{ id: "024_a_nota_nao_aponta_para_conta_que_sumiu", sql: MIGRATION_024_A_NOTA_NAO_APONTA_PARA_CONTA_QUE_SUMIU },
|
|
4337
|
+
{ id: "025_a_conta_da_nota_fecha_com_o_total", sql: MIGRATION_025_A_CONTA_DA_NOTA_FECHA_COM_O_TOTAL },
|
|
4338
|
+
{ id: "026_o_recebimento_parado_diz_por_que", sql: MIGRATION_026_O_RECEBIMENTO_PARADO_DIZ_POR_QUE }
|
|
3955
4339
|
];
|
|
3956
4340
|
var taxGroupEntity = {
|
|
3957
4341
|
name: "Grupo fiscal",
|
|
@@ -4411,7 +4795,7 @@ async function listInboxNotes(options = {}) {
|
|
|
4411
4795
|
if (filter === "stocked") query = query.not("stocked_at", "is", null);
|
|
4412
4796
|
if (filter === "pending") query = query.is("stocked_at", null);
|
|
4413
4797
|
if (filter === "rejected") query = query.eq("status", "rejected");
|
|
4414
|
-
if (filter === "
|
|
4798
|
+
if (filter === "manifest_error") query = query.eq("manifestation_status", "erro");
|
|
4415
4799
|
if (search?.trim()) {
|
|
4416
4800
|
const term = search.trim();
|
|
4417
4801
|
query = query.or(
|
|
@@ -4526,6 +4910,27 @@ async function stageNoteFromXml(xml, options = {}) {
|
|
|
4526
4910
|
parsed
|
|
4527
4911
|
};
|
|
4528
4912
|
}
|
|
4913
|
+
async function fillInboxLines(documentId, xml) {
|
|
4914
|
+
const payload = toStagePayload(parseNfeXml(xml));
|
|
4915
|
+
const { data, error } = await sb().rpc("fiscalbr_fill_inbox_lines", {
|
|
4916
|
+
p_document_id: documentId,
|
|
4917
|
+
p_items: payload.items
|
|
4918
|
+
});
|
|
4919
|
+
if (error) throw error;
|
|
4920
|
+
return Number(data?.items ?? 0);
|
|
4921
|
+
}
|
|
4922
|
+
async function healInboxNote(documentId) {
|
|
4923
|
+
const { data, error } = await sb().rpc("fiscalbr_heal_inbox_note", { p_document_id: documentId });
|
|
4924
|
+
if (error) throw error;
|
|
4925
|
+
const r = data ?? {};
|
|
4926
|
+
return { changed: Boolean(r.changed), released: Boolean(r.released) };
|
|
4927
|
+
}
|
|
4928
|
+
async function getNotePayable(invoiceId) {
|
|
4929
|
+
const { data, error } = await sb().from("plg_financial_invoices").select("id, number, total, cancelled_at").eq("id", invoiceId).maybeSingle();
|
|
4930
|
+
if (error) throw error;
|
|
4931
|
+
if (!data) return null;
|
|
4932
|
+
return { id: data.id, number: data.number ?? null, total: Number(data.total ?? 0), cancelledAt: data.cancelled_at ?? null };
|
|
4933
|
+
}
|
|
4529
4934
|
var PullByKeyError = class extends Error {
|
|
4530
4935
|
constructor(message, code, options) {
|
|
4531
4936
|
super(message);
|
|
@@ -4850,9 +5255,64 @@ function parseBoleto(raw) {
|
|
|
4850
5255
|
}
|
|
4851
5256
|
return { amount: cents > 0 ? cents / 100 : null, dueDate };
|
|
4852
5257
|
}
|
|
5258
|
+
var STALE_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
5259
|
+
function expiryFromError(error) {
|
|
5260
|
+
const m = error?.match(/vencido em:?\s*(\d{2})\/(\d{2})\/(\d{4})/i);
|
|
5261
|
+
return m ? `${m[3]}-${m[2]}-${m[1]}` : null;
|
|
5262
|
+
}
|
|
5263
|
+
async function getInboxIntakeHealth() {
|
|
5264
|
+
const { data, error } = await sb().from("plg_fiscalbr_emitter_settings").select("legal_entity_id, dfe_sync_enabled, dfe_last_sync_at, dfe_last_error, certificate_expires_at").eq("dfe_sync_enabled", true);
|
|
5265
|
+
if (error) throw error;
|
|
5266
|
+
const now = Date.now();
|
|
5267
|
+
const stalled = (data ?? []).filter((r) => {
|
|
5268
|
+
const expired = r.certificate_expires_at && Date.parse(r.certificate_expires_at) < now;
|
|
5269
|
+
const stale = !r.dfe_last_sync_at || now - Date.parse(r.dfe_last_sync_at) > STALE_AFTER_MS;
|
|
5270
|
+
return expired || stale || Boolean(r.dfe_last_error);
|
|
5271
|
+
});
|
|
5272
|
+
if (stalled.length === 0) return null;
|
|
5273
|
+
const since = stalled.map((r) => r.dfe_last_sync_at).filter(Boolean).sort()[0] ?? null;
|
|
5274
|
+
const withError = stalled.find((r) => r.dfe_last_error);
|
|
5275
|
+
const certificateExpiredAt = stalled.map((r) => r.certificate_expires_at && Date.parse(r.certificate_expires_at) < now ? r.certificate_expires_at : expiryFromError(r.dfe_last_error)).find(Boolean) ?? null;
|
|
5276
|
+
const target = stalled.find((r) => r.certificate_expires_at && Date.parse(r.certificate_expires_at) < now) ?? withError ?? stalled[0];
|
|
5277
|
+
const { data: unit } = await sb().from("v_unit_company_legal_entities").select("unit_id").eq("company_legal_entity_id", target.legal_entity_id).limit(1).maybeSingle();
|
|
5278
|
+
return { stalled: stalled.length, since, certificateExpiredAt, error: withError?.dfe_last_error ?? null, unitId: unit?.unit_id ?? null };
|
|
5279
|
+
}
|
|
5280
|
+
async function syncNewNotes(max = 25) {
|
|
5281
|
+
const { data: newest } = await sb().from("plg_fiscalbr_inbox_documents").select("issue_date").order("issue_date", { ascending: false }).limit(1).maybeSingle();
|
|
5282
|
+
const since = newest?.issue_date ? new Date(Date.parse(newest.issue_date) - 2 * 864e5).toISOString().slice(0, 10) : void 0;
|
|
5283
|
+
const { data, error } = await sb().functions.invoke("fiscalbr-dfe-pull", {
|
|
5284
|
+
body: { action: "list", since, tenantId: getActiveTenantId() ?? null }
|
|
5285
|
+
});
|
|
5286
|
+
if (error) {
|
|
5287
|
+
let body = null;
|
|
5288
|
+
try {
|
|
5289
|
+
body = await error?.context?.json?.();
|
|
5290
|
+
} catch {
|
|
5291
|
+
}
|
|
5292
|
+
throw new PullByKeyError(body?.error || error.message || "Falha ao falar com a SEFAZ.", body?.code);
|
|
5293
|
+
}
|
|
5294
|
+
const listed = data?.notes ?? [];
|
|
5295
|
+
if (listed.length === 0) return { added: 0, waiting: 0, failed: (data?.failures ?? []).length };
|
|
5296
|
+
const keys = [...new Set(listed.map((n) => n.key))];
|
|
5297
|
+
const { data: known } = await sb().from("plg_fiscalbr_inbox_documents").select("access_key").in("access_key", keys);
|
|
5298
|
+
const have = new Set((known ?? []).map((r) => r.access_key));
|
|
5299
|
+
const missing = listed.filter((n, i) => !have.has(n.key) && listed.findIndex((m) => m.key === n.key) === i).slice(0, max);
|
|
5300
|
+
const result = { added: 0, waiting: 0, failed: (data?.failures ?? []).length };
|
|
5301
|
+
for (const n of missing) {
|
|
5302
|
+
try {
|
|
5303
|
+
const r = await pullNoteByKey(n.key, { legalEntityId: n.legalEntityId });
|
|
5304
|
+
if (r.status === "staged") result.added++;
|
|
5305
|
+
else result.waiting++;
|
|
5306
|
+
} catch (e) {
|
|
5307
|
+
if (e instanceof PullByKeyError && (e.code === "summary_only" || e.code === "throttled")) result.waiting++;
|
|
5308
|
+
else result.failed++;
|
|
5309
|
+
}
|
|
5310
|
+
}
|
|
5311
|
+
return result;
|
|
5312
|
+
}
|
|
4853
5313
|
var formatCnpj = (d) => `${d.slice(0, 2)}.${d.slice(2, 5)}.${d.slice(5, 8)}/${d.slice(8, 12)}-${d.slice(12)}`;
|
|
4854
5314
|
var groupKey = (digits2) => digits2.replace(/(\d{4})(?=\d)/g, "$1 ");
|
|
4855
|
-
function PullByKeyDialog({ open, onOpenChange, unitId, onPulled }) {
|
|
5315
|
+
function PullByKeyDialog({ open, onOpenChange, unitId, initialKey, onPulled }) {
|
|
4856
5316
|
const [value, setValue] = useState("");
|
|
4857
5317
|
const [busy, setBusy] = useState(false);
|
|
4858
5318
|
const [error, setError] = useState(null);
|
|
@@ -4860,13 +5320,16 @@ function PullByKeyDialog({ open, onOpenChange, unitId, onPulled }) {
|
|
|
4860
5320
|
const [cnpjOptions, setCnpjOptions] = useState(null);
|
|
4861
5321
|
const [legalEntityId, setLegalEntityId] = useState("");
|
|
4862
5322
|
useEffect(() => {
|
|
4863
|
-
if (open)
|
|
5323
|
+
if (open) {
|
|
5324
|
+
if (initialKey) setValue(initialKey);
|
|
5325
|
+
return;
|
|
5326
|
+
}
|
|
4864
5327
|
setValue("");
|
|
4865
5328
|
setError(null);
|
|
4866
5329
|
setNotice(null);
|
|
4867
5330
|
setCnpjOptions(null);
|
|
4868
5331
|
setLegalEntityId("");
|
|
4869
|
-
}, [open]);
|
|
5332
|
+
}, [open, initialKey]);
|
|
4870
5333
|
const digits2 = value.replace(/\D/g, "").slice(0, 44);
|
|
4871
5334
|
const needsCnpj = !!cnpjOptions && !legalEntityId;
|
|
4872
5335
|
const blocked = digits2.length !== 44 ? `Faltam ${44 - digits2.length} d\xEDgitos` : needsCnpj ? "Escolha o CNPJ" : null;
|
|
@@ -4961,16 +5424,17 @@ var cnpj = (digits2) => {
|
|
|
4961
5424
|
if (d.length !== 14) return digits2 ?? "\u2014";
|
|
4962
5425
|
return `${d.slice(0, 2)}.${d.slice(2, 5)}.${d.slice(5, 8)}/${d.slice(8, 12)}-${d.slice(12)}`;
|
|
4963
5426
|
};
|
|
4964
|
-
var CAMPO = "h-9 w-full rounded-
|
|
5427
|
+
var CAMPO = "h-9 w-full rounded-input border border-input bg-background px-3 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2";
|
|
4965
5428
|
var STATE_OPTIONS = [
|
|
4966
5429
|
{ value: "all", label: "Todos" },
|
|
4967
5430
|
{ value: "pending", label: "Estoque pendente" },
|
|
4968
5431
|
{ value: "stocked", label: "Estocado" },
|
|
4969
|
-
{ value: "
|
|
5432
|
+
{ value: "manifest_error", label: "Manifesta\xE7\xE3o com erro" },
|
|
4970
5433
|
{ value: "rejected", label: "Recusada" }
|
|
4971
5434
|
];
|
|
4972
5435
|
var isoDay = (d) => d.toISOString().slice(0, 10);
|
|
4973
|
-
|
|
5436
|
+
var SYNC_KEY = "fiscalbr.inbox.lastSefazSync";
|
|
5437
|
+
function InboundNotesView({ onOpenNote }) {
|
|
4974
5438
|
const [notes, setNotes] = useState([]);
|
|
4975
5439
|
const [loading, setLoading] = useState(true);
|
|
4976
5440
|
const [search, setSearch] = useState("");
|
|
@@ -5003,7 +5467,9 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
|
|
|
5003
5467
|
filter: applied.filter,
|
|
5004
5468
|
unitId: applied.unitId || void 0,
|
|
5005
5469
|
from: applied.from || void 0,
|
|
5006
|
-
to: applied.to || void 0
|
|
5470
|
+
to: applied.to || void 0,
|
|
5471
|
+
// The list pages on screen; the default 200 would silently drop the rest.
|
|
5472
|
+
limit: 5e3
|
|
5007
5473
|
}));
|
|
5008
5474
|
} finally {
|
|
5009
5475
|
setLoading(false);
|
|
@@ -5012,6 +5478,44 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
|
|
|
5012
5478
|
useEffect(() => {
|
|
5013
5479
|
void reload();
|
|
5014
5480
|
}, [reload]);
|
|
5481
|
+
const [intake, setIntake] = useState(null);
|
|
5482
|
+
const [syncing, setSyncing] = useState(false);
|
|
5483
|
+
const [syncNote, setSyncNote] = useState(null);
|
|
5484
|
+
const refreshIntake = useCallback(() => {
|
|
5485
|
+
getInboxIntakeHealth().then(setIntake).catch(() => setIntake(null));
|
|
5486
|
+
}, []);
|
|
5487
|
+
const syncNow = useCallback(async (quiet) => {
|
|
5488
|
+
setSyncing(true);
|
|
5489
|
+
if (!quiet) setSyncNote(null);
|
|
5490
|
+
try {
|
|
5491
|
+
const r = await syncNewNotes();
|
|
5492
|
+
try {
|
|
5493
|
+
localStorage.setItem(SYNC_KEY, String(Date.now()));
|
|
5494
|
+
} catch {
|
|
5495
|
+
}
|
|
5496
|
+
if (r.added) void reload();
|
|
5497
|
+
const parts = [
|
|
5498
|
+
r.added ? `${r.added} ${r.added === 1 ? "nota nova" : "notas novas"}` : null,
|
|
5499
|
+
r.waiting ? `${r.waiting} ainda sem o XML completo na SEFAZ` : null,
|
|
5500
|
+
r.failed ? `${r.failed} com erro` : null
|
|
5501
|
+
].filter(Boolean);
|
|
5502
|
+
if (!quiet || parts.length) setSyncNote(parts.length ? `SEFAZ: ${parts.join(" \xB7 ")}.` : "SEFAZ: nenhuma nota nova.");
|
|
5503
|
+
} catch (e) {
|
|
5504
|
+
if (!quiet) setSyncNote(e instanceof Error ? e.message : String(e));
|
|
5505
|
+
} finally {
|
|
5506
|
+
setSyncing(false);
|
|
5507
|
+
refreshIntake();
|
|
5508
|
+
}
|
|
5509
|
+
}, [reload, refreshIntake]);
|
|
5510
|
+
useEffect(() => {
|
|
5511
|
+
refreshIntake();
|
|
5512
|
+
let last = 0;
|
|
5513
|
+
try {
|
|
5514
|
+
last = Number(localStorage.getItem(SYNC_KEY) ?? 0);
|
|
5515
|
+
} catch {
|
|
5516
|
+
}
|
|
5517
|
+
if (Date.now() - last > 30 * 6e4) void syncNow(true);
|
|
5518
|
+
}, []);
|
|
5015
5519
|
const ingest = useCallback(async (files) => {
|
|
5016
5520
|
const xmls = files.filter((f) => /\.xml$/i.test(f.name));
|
|
5017
5521
|
if (xmls.length === 0) {
|
|
@@ -5090,6 +5594,11 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
|
|
|
5090
5594
|
return day(note.issueDate).replace(/\D/g, "").includes(digits2);
|
|
5091
5595
|
});
|
|
5092
5596
|
}, [notes, query]);
|
|
5597
|
+
const [page, setPage] = useState(1);
|
|
5598
|
+
useEffect(() => {
|
|
5599
|
+
setPage(1);
|
|
5600
|
+
}, [notes, query]);
|
|
5601
|
+
const pageRows = useMemo(() => shown.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE), [shown, page]);
|
|
5093
5602
|
const columns = useMemo(() => [
|
|
5094
5603
|
{
|
|
5095
5604
|
id: "issueDate",
|
|
@@ -5191,7 +5700,7 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
|
|
|
5191
5700
|
] }) : /* @__PURE__ */ jsxs("div", { children: [
|
|
5192
5701
|
/* @__PURE__ */ jsx(Inbox, { className: "mx-auto h-8 w-8 text-muted-foreground", "aria-hidden": true }),
|
|
5193
5702
|
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm font-medium", children: "Nenhuma nota de entrada" }),
|
|
5194
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "As notas chegam da SEFAZ
|
|
5703
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "As notas chegam da SEFAZ sozinhas, ou voc\xEA arrasta o XML aqui." })
|
|
5195
5704
|
] });
|
|
5196
5705
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
5197
5706
|
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(
|
|
@@ -5209,6 +5718,16 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
|
|
|
5209
5718
|
disabled: busy,
|
|
5210
5719
|
onSelect: () => fileInput.current?.click()
|
|
5211
5720
|
},
|
|
5721
|
+
{
|
|
5722
|
+
id: "sync-sefaz",
|
|
5723
|
+
label: syncing ? "Buscando na SEFAZ\u2026" : "Buscar notas novas",
|
|
5724
|
+
icon: syncing ? Loader2 : RefreshCw,
|
|
5725
|
+
disabled: syncing,
|
|
5726
|
+
testId: "sync-sefaz",
|
|
5727
|
+
onSelect: () => {
|
|
5728
|
+
void syncNow(false);
|
|
5729
|
+
}
|
|
5730
|
+
},
|
|
5212
5731
|
{
|
|
5213
5732
|
id: "pull-by-key",
|
|
5214
5733
|
label: "Puxar por chave",
|
|
@@ -5216,11 +5735,29 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
|
|
|
5216
5735
|
creates: true,
|
|
5217
5736
|
testId: "pull-by-key",
|
|
5218
5737
|
onSelect: () => setPullOpen(true)
|
|
5219
|
-
}
|
|
5220
|
-
...onOpenManifest ? [{ id: "manifest", label: "Fila da SEFAZ", icon: Inbox, onSelect: onOpenManifest }] : []
|
|
5738
|
+
}
|
|
5221
5739
|
]
|
|
5222
5740
|
}
|
|
5223
5741
|
) }),
|
|
5742
|
+
syncNote && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", "data-testid": "sync-note", children: syncNote }),
|
|
5743
|
+
intake && /* @__PURE__ */ jsxs(Card, { className: "border-warning bg-warning-soft p-4", "data-testid": "intake-stalled", children: [
|
|
5744
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-warning-soft-foreground", children: intake.certificateExpiredAt ? `As notas da SEFAZ n\xE3o est\xE3o chegando: o certificado digital venceu em ${day(intake.certificateExpiredAt)}.` : `As notas da SEFAZ n\xE3o est\xE3o chegando${intake.since ? ` desde ${day(intake.since)}` : ""}.` }),
|
|
5745
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-warning-soft-foreground", children: [
|
|
5746
|
+
intake.certificateExpiredAt ? "Renove o certificado A1 do CNPJ e envie o arquivo novo. As notas que ficaram para tr\xE1s chegam sozinhas depois disso." : intake.error ?? "A busca autom\xE1tica parou. Enquanto isso, d\xE1 para importar o XML ou puxar a nota pela chave.",
|
|
5747
|
+
intake.stalled > 1 ? ` (${intake.stalled} CNPJs afetados)` : ""
|
|
5748
|
+
] }),
|
|
5749
|
+
intake.unitId && (intake.certificateExpiredAt || intake.error) && /* @__PURE__ */ jsx(
|
|
5750
|
+
Button,
|
|
5751
|
+
{
|
|
5752
|
+
size: "sm",
|
|
5753
|
+
variant: "outline",
|
|
5754
|
+
className: "mt-3",
|
|
5755
|
+
"data-testid": "intake-update-certificate",
|
|
5756
|
+
onClick: () => adminNavigateTo(`/settings/locations/${intake.unitId}/fields:tax`),
|
|
5757
|
+
children: "Atualizar certificado"
|
|
5758
|
+
}
|
|
5759
|
+
)
|
|
5760
|
+
] }),
|
|
5224
5761
|
/* @__PURE__ */ jsxs(
|
|
5225
5762
|
Card,
|
|
5226
5763
|
{
|
|
@@ -5324,7 +5861,7 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
|
|
|
5324
5861
|
}
|
|
5325
5862
|
),
|
|
5326
5863
|
rowError && /* @__PURE__ */ jsx("p", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", role: "alert", children: rowError }),
|
|
5327
|
-
/* @__PURE__ */ jsxs(Card, {
|
|
5864
|
+
/* @__PURE__ */ jsxs(Card, { style: { overflow: "clip" }, children: [
|
|
5328
5865
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 border-b border-border px-3 py-2", children: /* @__PURE__ */ jsx("div", { className: "min-w-[10rem] flex-1", children: /* @__PURE__ */ jsx(
|
|
5329
5866
|
SearchField,
|
|
5330
5867
|
{
|
|
@@ -5339,17 +5876,19 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
|
|
|
5339
5876
|
{
|
|
5340
5877
|
variant: "bare",
|
|
5341
5878
|
columns,
|
|
5342
|
-
data: loading ? [] :
|
|
5879
|
+
data: loading ? [] : pageRows,
|
|
5343
5880
|
loading,
|
|
5344
5881
|
getRowId: (note) => note.id,
|
|
5345
5882
|
onRowClick: (note) => onOpenNote(note.id),
|
|
5346
5883
|
emptyMessage,
|
|
5347
5884
|
mobileLayout: "cards"
|
|
5348
5885
|
}
|
|
5349
|
-
) })
|
|
5886
|
+
) }),
|
|
5887
|
+
!loading && shown.length > 0 && /* @__PURE__ */ jsx(ChevronPager, { page, pageSize: PAGE_SIZE, total: shown.length, onPageChange: setPage, noun: "notas" })
|
|
5350
5888
|
] })
|
|
5351
5889
|
] });
|
|
5352
5890
|
}
|
|
5891
|
+
var PAGE_SIZE = 50;
|
|
5353
5892
|
function ActionIcon({ label, children }) {
|
|
5354
5893
|
return /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
5355
5894
|
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
|
|
@@ -5629,6 +6168,9 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
5629
6168
|
const [baixandoXml, setBaixandoXml] = useState(false);
|
|
5630
6169
|
const [suggesting, setSuggesting] = useState(false);
|
|
5631
6170
|
const [suggestionNote, setSuggestionNote] = useState(null);
|
|
6171
|
+
const [pullOpen, setPullOpen] = useState(false);
|
|
6172
|
+
const [payable, setPayable] = useState(void 0);
|
|
6173
|
+
const [payableLost, setPayableLost] = useState(false);
|
|
5632
6174
|
const load = useCallback(async () => {
|
|
5633
6175
|
setLoading(true);
|
|
5634
6176
|
try {
|
|
@@ -5637,6 +6179,20 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
5637
6179
|
const linked = await linkInboxSupplier(noteId).catch(() => null);
|
|
5638
6180
|
if (linked?.supplierPartyId) detail = { ...detail, supplierPartyId: linked.supplierPartyId };
|
|
5639
6181
|
}
|
|
6182
|
+
if (detail && (detail.invoiceId || detail.stockedAt)) {
|
|
6183
|
+
const healed = await healInboxNote(noteId).catch(() => null);
|
|
6184
|
+
if (healed?.released) setPayableLost(true);
|
|
6185
|
+
if (healed?.changed) detail = await getInboxNote(noteId) ?? detail;
|
|
6186
|
+
}
|
|
6187
|
+
setPayable(detail?.invoiceId ? await getNotePayable(detail.invoiceId).catch(() => void 0) : void 0);
|
|
6188
|
+
if (detail && detail.items.length === 0 && detail.hasXml && detail.status !== "rejected") {
|
|
6189
|
+
const xml = await getInboxNoteXml(noteId).catch(() => null);
|
|
6190
|
+
if (xml) {
|
|
6191
|
+
const accepted2 = Boolean(detail.stockedAt || detail.invoiceId);
|
|
6192
|
+
const filled = accepted2 ? await fillInboxLines(noteId, xml).catch(() => 0) : (await stageNoteFromXml(xml, { unitId: detail.unitId }).catch(() => null))?.itemCount ?? 0;
|
|
6193
|
+
if (filled) detail = await getInboxNote(noteId) ?? detail;
|
|
6194
|
+
}
|
|
6195
|
+
}
|
|
5640
6196
|
setNote(detail);
|
|
5641
6197
|
const failed = [];
|
|
5642
6198
|
const [u, a, l] = await Promise.all([
|
|
@@ -5800,7 +6356,6 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
5800
6356
|
{
|
|
5801
6357
|
size: "sm",
|
|
5802
6358
|
className: "shrink-0",
|
|
5803
|
-
tone: reavaliar ? "soft" : "solid",
|
|
5804
6359
|
busy: suggesting,
|
|
5805
6360
|
busyLabel: "Pensando\u2026",
|
|
5806
6361
|
onClick: () => {
|
|
@@ -6042,6 +6597,32 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
6042
6597
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-success-soft-foreground", children: accepted.alreadyAccepted ? "Esta nota j\xE1 havia sido aceita \u2014 nada foi lan\xE7ado de novo." : `Aceita: ${accepted.stockLines} ${accepted.stockLines === 1 ? "linha" : "linhas"} de estoque e um t\xEDtulo de ${money3(accepted.invoiceTotal)} em ${accepted.instalments} ${accepted.instalments === 1 ? "parcela" : "parcelas"}.` }),
|
|
6043
6598
|
accepted.invoiceId && onOpenInvoice && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-2", onClick: () => onOpenInvoice(accepted.invoiceId), children: "Abrir a conta a pagar" })
|
|
6044
6599
|
] }),
|
|
6600
|
+
payableLost && !readOnly && /* @__PURE__ */ jsxs(Card, { className: "border-warning bg-warning-soft p-4", "data-testid": "note-payable-lost", children: [
|
|
6601
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-warning-soft-foreground", children: "A conta a pagar e a entrada no estoque desta nota foram apagadas fora daqui." }),
|
|
6602
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-warning-soft-foreground", children: "Confira a concilia\xE7\xE3o e clique em Aceitar para lan\xE7ar as duas de novo." })
|
|
6603
|
+
] }),
|
|
6604
|
+
!accepted && /* @__PURE__ */ jsxs(Card, { className: "p-4", "data-testid": "note-postings", children: [
|
|
6605
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Lan\xE7amentos desta nota" }),
|
|
6606
|
+
/* @__PURE__ */ jsxs("dl", { className: "mt-3 grid gap-x-6 gap-y-3 text-sm sm:grid-cols-2", children: [
|
|
6607
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
6608
|
+
/* @__PURE__ */ jsx("dt", { className: "text-xs text-muted-foreground", children: "Conta a pagar" }),
|
|
6609
|
+
/* @__PURE__ */ jsxs("dd", { className: "mt-0.5 flex flex-wrap items-center gap-2", children: [
|
|
6610
|
+
!note.invoiceId && /* @__PURE__ */ jsx("span", { className: payableLost ? "text-destructive" : "text-muted-foreground", children: payableLost ? "N\xE3o existe mais \u2014 foi apagada; aceite a nota para lan\xE7ar de novo" : readOnly ? "Nenhuma \u2014 esta nota n\xE3o abriu conta a pagar" : "Ainda n\xE3o lan\xE7ada \u2014 nasce quando a nota for aceita" }),
|
|
6611
|
+
note.invoiceId && payable === null && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "N\xE3o encontrada \u2014 a conta foi apagada" }),
|
|
6612
|
+
payable && /* @__PURE__ */ jsxs("span", { "data-testid": "note-payable-link", children: [
|
|
6613
|
+
onOpenInvoice ? /* @__PURE__ */ jsx(InlineLink, { onClick: () => onOpenInvoice(payable.id), children: payable.number ?? "Conta a pagar" }) : payable.number ?? "Conta a pagar",
|
|
6614
|
+
" \xB7 ",
|
|
6615
|
+
money3(payable.total),
|
|
6616
|
+
payable.cancelledAt && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: " \xB7 cancelada" })
|
|
6617
|
+
] })
|
|
6618
|
+
] })
|
|
6619
|
+
] }),
|
|
6620
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
6621
|
+
/* @__PURE__ */ jsx("dt", { className: "text-xs text-muted-foreground", children: "Estoque" }),
|
|
6622
|
+
/* @__PURE__ */ jsx("dd", { className: "mt-0.5", children: note.stockedAt ? `Entrada em ${day3(note.stockedAt)}` : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: readOnly ? "Sem entrada no estoque" : "Ainda sem entrada \u2014 entra quando a nota for aceita" }) })
|
|
6623
|
+
] })
|
|
6624
|
+
] })
|
|
6625
|
+
] }),
|
|
6045
6626
|
/* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
|
|
6046
6627
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Situa\xE7\xE3o fiscal" }),
|
|
6047
6628
|
/* @__PURE__ */ jsxs("dl", { className: "mt-3 grid gap-x-6 gap-y-2 text-sm sm:grid-cols-2 lg:grid-cols-3", children: [
|
|
@@ -6180,6 +6761,26 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
6180
6761
|
}
|
|
6181
6762
|
)
|
|
6182
6763
|
] }),
|
|
6764
|
+
note.items.length === 0 && /* @__PURE__ */ jsxs(Card, { className: "p-6 text-center", "data-testid": "note-without-items", children: [
|
|
6765
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Esta nota chegou sem os itens" }),
|
|
6766
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: readOnly || !note.accessKey ? "N\xE3o h\xE1 o XML dela guardado para ler os itens." : note.hasXml ? "O XML guardado \xE9 s\xF3 o resumo da SEFAZ, sem os itens. Busque a nota completa pela chave de acesso." : "O XML dela n\xE3o veio junto. Busque na SEFAZ pela chave de acesso para conciliar." }),
|
|
6767
|
+
!readOnly && note.accessKey && note.status !== "rejected" && /* @__PURE__ */ jsxs(Button, { className: "mt-3", size: "sm", onClick: () => setPullOpen(true), children: [
|
|
6768
|
+
/* @__PURE__ */ jsx(Download, { className: "mr-1.5 h-4 w-4" }),
|
|
6769
|
+
" Buscar itens na SEFAZ"
|
|
6770
|
+
] }),
|
|
6771
|
+
/* @__PURE__ */ jsx(
|
|
6772
|
+
PullByKeyDialog,
|
|
6773
|
+
{
|
|
6774
|
+
open: pullOpen,
|
|
6775
|
+
onOpenChange: setPullOpen,
|
|
6776
|
+
unitId: note.unitId,
|
|
6777
|
+
initialKey: note.accessKey ?? void 0,
|
|
6778
|
+
onPulled: () => {
|
|
6779
|
+
void load();
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6782
|
+
)
|
|
6783
|
+
] }),
|
|
6183
6784
|
note.items.map((item) => {
|
|
6184
6785
|
const d = drafts[item.id] ?? draftOf(item);
|
|
6185
6786
|
const factor = Number(d.conversionFactor.replace(",", ".")) || 1;
|
|
@@ -6577,132 +7178,20 @@ function Field({ label, value, mono, small }) {
|
|
|
6577
7178
|
/* @__PURE__ */ jsx("dd", { className: `${mono ? "tabular-nums" : ""} ${small ? "text-xs" : "text-sm"}`, children: value })
|
|
6578
7179
|
] });
|
|
6579
7180
|
}
|
|
6580
|
-
var money4 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(v || 0);
|
|
6581
|
-
var day4 = (iso) => iso ? iso.slice(0, 10).split("-").reverse().join("/") : "\u2014";
|
|
6582
|
-
var MANIFEST_TONE = {
|
|
6583
|
-
confirmada: "bg-success-soft text-success-soft-foreground",
|
|
6584
|
-
processando: "bg-info-soft text-info-soft-foreground",
|
|
6585
|
-
erro: "bg-destructive/10 text-destructive"
|
|
6586
|
-
};
|
|
6587
|
-
function ManifestView({ onOpenNote, onBack }) {
|
|
6588
|
-
const [notes, setNotes] = useState([]);
|
|
6589
|
-
const [loading, setLoading] = useState(true);
|
|
6590
|
-
const [tab, setTab] = useState("pendentes");
|
|
6591
|
-
const [pullOpen, setPullOpen] = useState(false);
|
|
6592
|
-
const reload = useCallback(async () => {
|
|
6593
|
-
setLoading(true);
|
|
6594
|
-
try {
|
|
6595
|
-
setNotes(await listInboxNotes({ filter: "all", limit: 300 }));
|
|
6596
|
-
} finally {
|
|
6597
|
-
setLoading(false);
|
|
6598
|
-
}
|
|
6599
|
-
}, []);
|
|
6600
|
-
useEffect(() => {
|
|
6601
|
-
void reload();
|
|
6602
|
-
}, [reload]);
|
|
6603
|
-
const shown = useMemo(() => notes.filter((n) => {
|
|
6604
|
-
const status = n.manifestationStatus ?? "";
|
|
6605
|
-
if (tab === "todas") return true;
|
|
6606
|
-
if (tab === "confirmadas") return status === "confirmada";
|
|
6607
|
-
if (tab === "erro") return status === "erro";
|
|
6608
|
-
return status !== "confirmada";
|
|
6609
|
-
}), [notes, tab]);
|
|
6610
|
-
const pending = notes.filter((n) => (n.manifestationStatus ?? "") !== "confirmada").length;
|
|
6611
|
-
const filters = useMemo(() => [{
|
|
6612
|
-
id: "manifestacao",
|
|
6613
|
-
label: "Manifesta\xE7\xE3o",
|
|
6614
|
-
allLabel: "Todas",
|
|
6615
|
-
value: tab === "todas" ? void 0 : tab,
|
|
6616
|
-
onChange: (v) => setTab(v ?? "todas"),
|
|
6617
|
-
options: [
|
|
6618
|
-
{ value: "pendentes", label: "Pendentes" },
|
|
6619
|
-
{ value: "confirmadas", label: "Confirmadas" },
|
|
6620
|
-
{ value: "erro", label: "Com erro" }
|
|
6621
|
-
]
|
|
6622
|
-
}], [tab]);
|
|
6623
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
6624
|
-
onBack && /* @__PURE__ */ jsx(SubpageHeader, { title: "Fila da SEFAZ", onBack, parentLabel: "Manifesto de Notas" }),
|
|
6625
|
-
/* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
|
|
6626
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
6627
|
-
/* @__PURE__ */ jsx(ShieldCheck, { className: "mt-0.5 h-5 w-5 text-muted-foreground", "aria-hidden": true }),
|
|
6628
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
6629
|
-
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium", children: [
|
|
6630
|
-
pending,
|
|
6631
|
-
" ",
|
|
6632
|
-
pending === 1 ? "nota aguardando" : "notas aguardando",
|
|
6633
|
-
" manifesta\xE7\xE3o \xE0 SEFAZ"
|
|
6634
|
-
] }),
|
|
6635
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Manifestar responde \xE0 SEFAZ e tem prazo. Dar entrada responde ao seu estoque e ao financeiro \u2014 s\xE3o duas filas." })
|
|
6636
|
-
] })
|
|
6637
|
-
] }),
|
|
6638
|
-
/* @__PURE__ */ jsx(Hint, { className: "mt-3", children: /* @__PURE__ */ jsx("span", { children: "A manifesta\xE7\xE3o continua sendo feita no sistema antigo por enquanto. Aqui voc\xEA l\xEA a fila e o que a SEFAZ respondeu; a entrada no estoque e a conta a pagar j\xE1 s\xE3o feitas nesta tela nova." }) }),
|
|
6639
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [
|
|
6640
|
-
/* @__PURE__ */ jsx(EmBreve, { label: "Aceitar / Recusar", o_que: "Responder \xE0 SEFAZ \u2014 \xE9 obriga\xE7\xE3o com prazo. Hoje a manifesta\xE7\xE3o \xE9 feita no sistema antigo" }),
|
|
6641
|
-
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setPullOpen(true), children: [
|
|
6642
|
-
/* @__PURE__ */ jsx(KeyRound, { className: "mr-1.5 h-4 w-4" }),
|
|
6643
|
-
"Puxar por chave"
|
|
6644
|
-
] }),
|
|
6645
|
-
/* @__PURE__ */ jsx(EmBreve, { label: "Sincronizar agora", o_que: "Pedir \xE0 SEFAZ o que chegou desde a \xFAltima varredura" }),
|
|
6646
|
-
/* @__PURE__ */ jsx(EmBreve, { label: "Resincronizar tudo", o_que: "Refazer a varredura inteira do per\xEDodo" }),
|
|
6647
|
-
/* @__PURE__ */ jsx(EmBreve, { label: "Diagnosticar", o_que: "Por que uma nota n\xE3o chegou: certificado, credencial ou cursor parado" })
|
|
6648
|
-
] })
|
|
6649
|
-
] }),
|
|
6650
|
-
/* @__PURE__ */ jsx(PullByKeyDialog, { open: pullOpen, onOpenChange: setPullOpen, onPulled: (r) => {
|
|
6651
|
-
void reload();
|
|
6652
|
-
onOpenNote(r.documentId);
|
|
6653
|
-
} }),
|
|
6654
|
-
/* @__PURE__ */ jsx(ListFilterBar, { filters, onClear: () => setTab("todas"), activeCount: tab === "todas" ? 0 : 1 }),
|
|
6655
|
-
loading ? /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-full" }, i)) }) : shown.length === 0 ? /* @__PURE__ */ jsxs(Card, { className: "p-10 text-center", children: [
|
|
6656
|
-
/* @__PURE__ */ jsx(FileText, { className: "mx-auto h-8 w-8 text-muted-foreground", "aria-hidden": true }),
|
|
6657
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm font-medium", children: "Nenhuma nota nesta fila" })
|
|
6658
|
-
] }) : /* @__PURE__ */ jsx(Card, { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
6659
|
-
/* @__PURE__ */ jsx("thead", { className: "border-b bg-muted/40 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
6660
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Emiss\xE3o" }),
|
|
6661
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium", children: "N\xBA" }),
|
|
6662
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Fornecedor" }),
|
|
6663
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: "Valor" }),
|
|
6664
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: "Parcelas" }),
|
|
6665
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-center font-medium", children: "Manifesta\xE7\xE3o" }),
|
|
6666
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: "A\xE7\xF5es" })
|
|
6667
|
-
] }) }),
|
|
6668
|
-
/* @__PURE__ */ jsx("tbody", { children: shown.map((note) => /* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0 hover:bg-muted/30", children: [
|
|
6669
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums text-muted-foreground", children: day4(note.issueDate) }),
|
|
6670
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums font-medium", children: note.number }),
|
|
6671
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: note.supplierName || "\u2014" }),
|
|
6672
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: money4(note.total) }),
|
|
6673
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums text-muted-foreground", children: note.instalmentCount }),
|
|
6674
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-center", children: /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${MANIFEST_TONE[note.manifestationStatus ?? ""] ?? "bg-muted text-muted-foreground"}`, children: note.manifestationStatus ?? "n\xE3o manifestada" }) }),
|
|
6675
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1.5", children: [
|
|
6676
|
-
/* @__PURE__ */ jsx(EmBreve, { label: "XML", o_que: "Ver o XML bruto autorizado desta nota" }),
|
|
6677
|
-
/* @__PURE__ */ jsx(EmBreve, { label: "PDF", o_que: "Abrir o DANFE desta nota" }),
|
|
6678
|
-
/* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => onOpenNote(note.id), children: [
|
|
6679
|
-
"Abrir concilia\xE7\xE3o",
|
|
6680
|
-
/* @__PURE__ */ jsx(ExternalLink, { className: "ml-1.5 h-3.5 w-3.5" })
|
|
6681
|
-
] })
|
|
6682
|
-
] }) })
|
|
6683
|
-
] }, note.id)) })
|
|
6684
|
-
] }) }) })
|
|
6685
|
-
] });
|
|
6686
|
-
}
|
|
6687
7181
|
var HASH_PREFIX = "#/inbound-notes";
|
|
6688
7182
|
function viewFromLocation() {
|
|
6689
7183
|
if (typeof window === "undefined") return { kind: "list" };
|
|
6690
7184
|
const hash = window.location.hash;
|
|
6691
7185
|
const note = hash.match(/\/inbound-notes\/note\/([0-9a-f-]{36})/i);
|
|
6692
7186
|
if (note) return { kind: "note", id: note[1] };
|
|
6693
|
-
if (/\/inbound-notes\/manifest/.test(hash)) return { kind: "manifest" };
|
|
6694
7187
|
return { kind: "list" };
|
|
6695
7188
|
}
|
|
6696
|
-
var pathOf = (view) => view.kind === "note" ? `${HASH_PREFIX}/note/${view.id}` :
|
|
7189
|
+
var pathOf = (view) => view.kind === "note" ? `${HASH_PREFIX}/note/${view.id}` : HASH_PREFIX;
|
|
6697
7190
|
var STACK_MARK = { inboundNotes: true };
|
|
6698
|
-
function InboundNotesPage({ payableRoute } = {}) {
|
|
7191
|
+
function InboundNotesPage({ payableRoute = (id) => `/financial/payables/${id}` } = {}) {
|
|
6699
7192
|
const [view, setView] = useState(viewFromLocation);
|
|
6700
|
-
const [background, setBackground] = useState(
|
|
6701
|
-
() => viewFromLocation().kind === "manifest" ? "manifest" : "list"
|
|
6702
|
-
);
|
|
6703
7193
|
const go = useCallback((next) => {
|
|
6704
7194
|
setView(next);
|
|
6705
|
-
if (next.kind !== "note") setBackground(next.kind);
|
|
6706
7195
|
if (typeof window === "undefined") return;
|
|
6707
7196
|
const path = pathOf(next);
|
|
6708
7197
|
if (window.location.hash === path) return;
|
|
@@ -6719,7 +7208,6 @@ function InboundNotesPage({ payableRoute } = {}) {
|
|
|
6719
7208
|
const onPop = () => {
|
|
6720
7209
|
const next = viewFromLocation();
|
|
6721
7210
|
setView(next);
|
|
6722
|
-
if (next.kind !== "note") setBackground(next.kind);
|
|
6723
7211
|
};
|
|
6724
7212
|
window.addEventListener("popstate", onPop);
|
|
6725
7213
|
window.addEventListener("hashchange", onPop);
|
|
@@ -6728,7 +7216,7 @@ function InboundNotesPage({ payableRoute } = {}) {
|
|
|
6728
7216
|
window.removeEventListener("hashchange", onPop);
|
|
6729
7217
|
};
|
|
6730
7218
|
}, []);
|
|
6731
|
-
const fecharNota = useCallback(() => go({ kind:
|
|
7219
|
+
const fecharNota = useCallback(() => go({ kind: "list" }), [go]);
|
|
6732
7220
|
useEffect(() => {
|
|
6733
7221
|
if (typeof window === "undefined") return;
|
|
6734
7222
|
const op = window.location.hash.match(/[?&]operation=([0-9a-f-]{36})/i)?.[1];
|
|
@@ -6743,13 +7231,7 @@ function InboundNotesPage({ payableRoute } = {}) {
|
|
|
6743
7231
|
};
|
|
6744
7232
|
}, [go]);
|
|
6745
7233
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6746
|
-
|
|
6747
|
-
InboundNotesView,
|
|
6748
|
-
{
|
|
6749
|
-
onOpenNote: (id) => go({ kind: "note", id }),
|
|
6750
|
-
onOpenManifest: () => go({ kind: "manifest" })
|
|
6751
|
-
}
|
|
6752
|
-
),
|
|
7234
|
+
/* @__PURE__ */ jsx(InboundNotesView, { onOpenNote: (id) => go({ kind: "note", id }) }),
|
|
6753
7235
|
/* @__PURE__ */ jsx(Modal, { open: view.kind === "note", onOpenChange: (open) => {
|
|
6754
7236
|
if (!open) fecharNota();
|
|
6755
7237
|
}, children: view.kind === "note" && // `noPadding`: a conciliação desenha o próprio cabeçalho e o próprio
|
|
@@ -6801,13 +7283,31 @@ var COLUMNS = [
|
|
|
6801
7283
|
"certificate_uploaded_at",
|
|
6802
7284
|
"dfe_sync_enabled",
|
|
6803
7285
|
"dfe_last_sync_at",
|
|
7286
|
+
"dfe_last_error",
|
|
6804
7287
|
"provider_synced_at"
|
|
6805
7288
|
].join(", ");
|
|
6806
7289
|
var sb2 = () => getSupabaseClientOptional();
|
|
7290
|
+
async function uploadCertificate(legalEntityId, file, password) {
|
|
7291
|
+
const buf = new Uint8Array(await file.arrayBuffer());
|
|
7292
|
+
let bin = "";
|
|
7293
|
+
for (let i = 0; i < buf.length; i++) bin += String.fromCharCode(buf[i]);
|
|
7294
|
+
const { data, error } = await sb2().functions.invoke("fiscalbr-certificate", {
|
|
7295
|
+
body: { legalEntityId, pfxBase64: btoa(bin), password, fileName: file.name, tenantId: getActiveTenantId$1() ?? null }
|
|
7296
|
+
});
|
|
7297
|
+
if (error) {
|
|
7298
|
+
let body = null;
|
|
7299
|
+
try {
|
|
7300
|
+
body = await error?.context?.json?.();
|
|
7301
|
+
} catch {
|
|
7302
|
+
}
|
|
7303
|
+
throw new Error(body?.error || error.message || "Falha ao falar com o servi\xE7o fiscal.");
|
|
7304
|
+
}
|
|
7305
|
+
return data;
|
|
7306
|
+
}
|
|
6807
7307
|
function useEmitterSettings(legalEntityId) {
|
|
6808
|
-
const [row, setRow] =
|
|
6809
|
-
const [loading, setLoading] =
|
|
6810
|
-
|
|
7308
|
+
const [row, setRow] = React6.useState(null);
|
|
7309
|
+
const [loading, setLoading] = React6.useState(true);
|
|
7310
|
+
React6.useEffect(() => {
|
|
6811
7311
|
if (!legalEntityId) {
|
|
6812
7312
|
setLoading(false);
|
|
6813
7313
|
return;
|
|
@@ -6823,7 +7323,7 @@ function useEmitterSettings(legalEntityId) {
|
|
|
6823
7323
|
alive = false;
|
|
6824
7324
|
};
|
|
6825
7325
|
}, [legalEntityId]);
|
|
6826
|
-
const save =
|
|
7326
|
+
const save = React6.useCallback(async (tenantId, patch) => {
|
|
6827
7327
|
if (!legalEntityId) return false;
|
|
6828
7328
|
const payload = { ...patch, tenant_id: tenantId, legal_entity_id: legalEntityId, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6829
7329
|
const { data, error } = await sb2().from("plg_fiscalbr_emitter_settings").upsert(payload, { onConflict: "legal_entity_id" }).select(COLUMNS).single();
|
|
@@ -6834,7 +7334,7 @@ function useEmitterSettings(legalEntityId) {
|
|
|
6834
7334
|
setRow(data);
|
|
6835
7335
|
return true;
|
|
6836
7336
|
}, [legalEntityId]);
|
|
6837
|
-
return { row, loading, save };
|
|
7337
|
+
return { row, loading, save, setRow };
|
|
6838
7338
|
}
|
|
6839
7339
|
function NeedsLegalEntity({ item, what }) {
|
|
6840
7340
|
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col items-start gap-3 py-6", children: [
|
|
@@ -6876,7 +7376,7 @@ function SummaryCard({ title, lines, badge, onConfigure }) {
|
|
|
6876
7376
|
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardHeader, { className: "flex-row items-start justify-between gap-3 space-y-0", children: [
|
|
6877
7377
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
6878
7378
|
/* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: title }),
|
|
6879
|
-
/* @__PURE__ */ jsx("dl", { className: "mt-2 grid gap-x-6 gap-y-1 text-sm", style: { gridTemplateColumns: "auto 1fr" }, children: lines.map((l) => /* @__PURE__ */ jsxs(
|
|
7379
|
+
/* @__PURE__ */ jsx("dl", { className: "mt-2 grid gap-x-6 gap-y-1 text-sm", style: { gridTemplateColumns: "auto 1fr" }, children: lines.map((l) => /* @__PURE__ */ jsxs(React6.Fragment, { children: [
|
|
6880
7380
|
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: l.label }),
|
|
6881
7381
|
/* @__PURE__ */ jsx("dd", { className: "text-foreground", children: l.value })
|
|
6882
7382
|
] }, l.label)) })
|
|
@@ -6896,8 +7396,15 @@ function SummaryCard({ title, lines, badge, onConfigure }) {
|
|
|
6896
7396
|
}
|
|
6897
7397
|
var fmtDateTime = (v) => v ? new Date(v).toLocaleString("pt-BR") : null;
|
|
6898
7398
|
function UnitCertificateWidget({ item }) {
|
|
6899
|
-
const { row, loading, save } = useEmitterSettings(item?.legalEntityId);
|
|
6900
|
-
const [open, setOpen] =
|
|
7399
|
+
const { row, loading, save, setRow } = useEmitterSettings(item?.legalEntityId);
|
|
7400
|
+
const [open, setOpen] = React6.useState(false);
|
|
7401
|
+
const [file, setFile] = React6.useState(null);
|
|
7402
|
+
const [password, setPassword] = React6.useState("");
|
|
7403
|
+
const [sending, setSending] = React6.useState(false);
|
|
7404
|
+
const expiredNow = !!row?.certificate_expires_at && Date.parse(row.certificate_expires_at) < Date.now();
|
|
7405
|
+
React6.useEffect(() => {
|
|
7406
|
+
if (expiredNow) setOpen(true);
|
|
7407
|
+
}, [expiredNow]);
|
|
6901
7408
|
if (!item?.id) return null;
|
|
6902
7409
|
if (!item.legalEntityId) return /* @__PURE__ */ jsx(NeedsLegalEntity, { item, what: "O certificado digital" });
|
|
6903
7410
|
if (loading || !row) return /* @__PURE__ */ jsx(LoadingCard, {});
|
|
@@ -6965,10 +7472,55 @@ function UnitCertificateWidget({ item }) {
|
|
|
6965
7472
|
hasCertificate && /* @__PURE__ */ jsx(Badge, { variant: expired ? "destructive" : "secondary", children: expired ? "Vencido" : "Ativo" })
|
|
6966
7473
|
] }),
|
|
6967
7474
|
/* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
|
|
6968
|
-
/* @__PURE__ */ jsx(Field2, { label: "Arquivo (.pfx)", children: /* @__PURE__ */ jsx(
|
|
6969
|
-
|
|
7475
|
+
/* @__PURE__ */ jsx(Field2, { label: "Arquivo (.pfx)", children: /* @__PURE__ */ jsx(
|
|
7476
|
+
Input,
|
|
7477
|
+
{
|
|
7478
|
+
type: "file",
|
|
7479
|
+
accept: ".pfx,.p12,application/x-pkcs12",
|
|
7480
|
+
"data-testid": "certificate-file",
|
|
7481
|
+
onChange: (e) => setFile(e.target.files?.[0] ?? null)
|
|
7482
|
+
}
|
|
7483
|
+
) }),
|
|
7484
|
+
/* @__PURE__ */ jsx(Field2, { label: "Senha do certificado", children: /* @__PURE__ */ jsx(
|
|
7485
|
+
Input,
|
|
7486
|
+
{
|
|
7487
|
+
type: "password",
|
|
7488
|
+
autoComplete: "new-password",
|
|
7489
|
+
name: "pfx-password",
|
|
7490
|
+
value: password,
|
|
7491
|
+
"data-testid": "certificate-password",
|
|
7492
|
+
onChange: (e) => setPassword(e.target.value)
|
|
7493
|
+
}
|
|
7494
|
+
) })
|
|
6970
7495
|
] }) }),
|
|
6971
|
-
/* @__PURE__ */ jsx("
|
|
7496
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
7497
|
+
Button,
|
|
7498
|
+
{
|
|
7499
|
+
size: "sm",
|
|
7500
|
+
"data-testid": "certificate-send",
|
|
7501
|
+
disabled: !file || !password || sending,
|
|
7502
|
+
onClick: () => {
|
|
7503
|
+
if (!file) return;
|
|
7504
|
+
setSending(true);
|
|
7505
|
+
void uploadCertificate(item.legalEntityId, file, password).then((res) => {
|
|
7506
|
+
setRow((r) => ({
|
|
7507
|
+
...r ?? {},
|
|
7508
|
+
certificate_cn: res.cn,
|
|
7509
|
+
certificate_expires_at: res.expiresAt,
|
|
7510
|
+
certificate_uploaded_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7511
|
+
dfe_sync_enabled: true,
|
|
7512
|
+
dfe_last_error: res.warning ?? null
|
|
7513
|
+
}));
|
|
7514
|
+
setFile(null);
|
|
7515
|
+
setPassword("");
|
|
7516
|
+
if (res.warning) toast.warning("Certificado enviado, com um aviso", { description: res.warning });
|
|
7517
|
+
else toast.success(`Certificado enviado \xB7 v\xE1lido at\xE9 ${new Date(res.expiresAt).toLocaleDateString("pt-BR")}`);
|
|
7518
|
+
}).catch((e) => toast.error("N\xE3o foi poss\xEDvel enviar o certificado", { description: e.message })).finally(() => setSending(false));
|
|
7519
|
+
},
|
|
7520
|
+
children: sending ? "Enviando\u2026" : hasCertificate ? "Atualizar certificado" : "Enviar certificado"
|
|
7521
|
+
}
|
|
7522
|
+
) }),
|
|
7523
|
+
row.dfe_last_error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: row.dfe_last_error })
|
|
6972
7524
|
] }),
|
|
6973
7525
|
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
6974
7526
|
"\xDAltima sincroniza\xE7\xE3o: ",
|
|
@@ -7000,11 +7552,11 @@ function EnvSelect({ value, onChange, disabled }) {
|
|
|
7000
7552
|
}
|
|
7001
7553
|
function UnitFiscalWidget({ item }) {
|
|
7002
7554
|
const { row, loading, save } = useEmitterSettings(item?.legalEntityId);
|
|
7003
|
-
const [draft, setDraft] =
|
|
7004
|
-
const [cscToken, setCscToken] =
|
|
7005
|
-
const [saving, setSaving] =
|
|
7006
|
-
const [open, setOpen] =
|
|
7007
|
-
|
|
7555
|
+
const [draft, setDraft] = React6.useState({});
|
|
7556
|
+
const [cscToken, setCscToken] = React6.useState("");
|
|
7557
|
+
const [saving, setSaving] = React6.useState(false);
|
|
7558
|
+
const [open, setOpen] = React6.useState(false);
|
|
7559
|
+
React6.useEffect(() => {
|
|
7008
7560
|
if (row) setDraft(row);
|
|
7009
7561
|
}, [row]);
|
|
7010
7562
|
if (!item?.id) return null;
|
|
@@ -7372,7 +7924,7 @@ var dayOrDateTime = (v) => {
|
|
|
7372
7924
|
const d = new Date(v);
|
|
7373
7925
|
return d.getHours() === 0 && d.getMinutes() === 0 && d.getSeconds() === 0 ? d.toLocaleDateString("pt-BR") : dateTime(v);
|
|
7374
7926
|
};
|
|
7375
|
-
var
|
|
7927
|
+
var money4 = (v) => v == null ? "\u2014" : v.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
|
|
7376
7928
|
function buttonHint(s) {
|
|
7377
7929
|
const parts = [s.hasValid ? "Notas fiscais desta conta \u2014 h\xE1 uma nota v\xE1lida" : "Notas fiscais desta conta"];
|
|
7378
7930
|
if (s.withoutEffect > 0) parts.push(`${s.withoutEffect} cancelada(s), rejeitada(s) ou com erro`);
|
|
@@ -7611,7 +8163,7 @@ function NoteDetails({ note }) {
|
|
|
7611
8163
|
["Refer\xEAncia", note.integrationId && /* @__PURE__ */ jsx("span", { className: "break-all font-mono text-xs", children: note.integrationId })],
|
|
7612
8164
|
["Chave de acesso", note.accessKey && /* @__PURE__ */ jsx("span", { className: "break-all font-mono text-xs", children: note.accessKey })],
|
|
7613
8165
|
["C\xF3digo de verifica\xE7\xE3o", note.verificationCode],
|
|
7614
|
-
["Valor",
|
|
8166
|
+
["Valor", money4(note.totalValue)],
|
|
7615
8167
|
["Tomador", recipientLabel(note.recipient)],
|
|
7616
8168
|
["Criada em", dateTime(note.createdAt)],
|
|
7617
8169
|
["Autorizada em", note.authorizedAt && dayOrDateTime(note.authorizedAt)],
|
|
@@ -7826,9 +8378,9 @@ function inboundNotesInventoryView(options = {}) {
|
|
|
7826
8378
|
};
|
|
7827
8379
|
}
|
|
7828
8380
|
function InboundNoteDocumentModal({ movement, onClose }) {
|
|
7829
|
-
const [noteId, setNoteId] =
|
|
7830
|
-
const [erro, setErro] =
|
|
7831
|
-
|
|
8381
|
+
const [noteId, setNoteId] = React6.useState(null);
|
|
8382
|
+
const [erro, setErro] = React6.useState(null);
|
|
8383
|
+
React6.useEffect(() => {
|
|
7832
8384
|
let vivo = true;
|
|
7833
8385
|
const numero = nfeNumberOf(movement);
|
|
7834
8386
|
const busca = movement.operationId ? findInboxNoteByOperation(movement.operationId).then((id) => id ?? (numero ? findInboxNoteByNumber(numero) : null)) : numero ? findInboxNoteByNumber(numero) : Promise.resolve(null);
|
|
@@ -7859,7 +8411,17 @@ function InboundNoteDocumentModal({ movement, onClose }) {
|
|
|
7859
8411
|
] })
|
|
7860
8412
|
] });
|
|
7861
8413
|
}
|
|
7862
|
-
return /* @__PURE__ */ jsx(
|
|
8414
|
+
return /* @__PURE__ */ jsx(
|
|
8415
|
+
NoteReconcileView,
|
|
8416
|
+
{
|
|
8417
|
+
chrome: "modal",
|
|
8418
|
+
noteId,
|
|
8419
|
+
onBack: onClose,
|
|
8420
|
+
onOpenInvoice: (id) => {
|
|
8421
|
+
window.location.hash = `/financial/payables/${id}`;
|
|
8422
|
+
}
|
|
8423
|
+
}
|
|
8424
|
+
);
|
|
7863
8425
|
}
|
|
7864
8426
|
function inboundNoteDocumentModal(movement, close) {
|
|
7865
8427
|
if (movement.documentType !== "nfe" || !movement.operationId) return void 0;
|
|
@@ -8010,6 +8572,6 @@ function createPlugNotasPlugin(options = {}) {
|
|
|
8010
8572
|
});
|
|
8011
8573
|
}
|
|
8012
8574
|
|
|
8013
|
-
export { EVENT_LABEL, InboundNoteDocumentModal, InboundNotesPage, InboundNotesView, InvoiceNotesButton, IssuedNotesView, MODEL_LABEL,
|
|
8575
|
+
export { EVENT_LABEL, InboundNoteDocumentModal, InboundNotesPage, InboundNotesView, InvoiceNotesButton, IssuedNotesView, MODEL_LABEL, NfeParseError, NoteReconcileView, PLUGNOTAS_CONNECTOR_ID, PLUGNOTAS_FIELDS, PLUGNOTAS_SETTINGS, PullByKeyError, ReturnedNotesView, STATUS_META, STOCK_BEARING_ENTRY_TYPES, TaxGroupsView, acceptNote, addNoteAttachment, cancelNote, createPlugNotasConnector, createPlugNotasFinancialProvider, createPlugNotasPlugin, eventStatusLabel, explainRejection, extractPaymentCode, fetchNotePdf, fillInboxLines, findInboxNoteByNumber, findInboxNoteByOperation, getInboxIntakeHealth, getInboxNote, getInboxNotePdf, getInboxNoteXml, getNotePayable, healInboxNote, inboundNoteDocumentModal, inboundNotesInventoryView, isStockBearing, isValidCnpj, isWithoutEffect, linkInboxSupplier, listChartAccounts, listInboxNotes, listInvoiceNotes, listMeasurementUnits, listNoteAttachments, listNoteEvents, listProductsByIds, listStockLocations, listSupplierMap, markPlugNotasEmitterReady, moveNoteAttachmentsToInvoice, notesButtonState, onlyDigits, parseBoleto, parseNfeXml, paymentLabel, paymentTypeFromCode, pullNoteByKey, recipientLabel, refreshNote, rejectNote, removeNoteAttachment, saveNoteDecision, searchProducts, stageNoteFromXml, statusLabel, suggestNoteItems, summarizeNotes, syncNewNotes, toStagePayload };
|
|
8014
8576
|
//# sourceMappingURL=index.js.map
|
|
8015
8577
|
//# sourceMappingURL=index.js.map
|