@fayz-ai/plugin-fiscal-br 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,54 +1,426 @@
1
1
  import { packagingCount } from './chunk-5LI2FDOM.js';
2
- import { recordRailFields, RECORD_GROUP, activeFacet, describeError, getSupabaseClientOptional, formatCurrency, connectionSettings, connectionStatus, definePlugin, createConnectionStore, settingsStatus, maskCpfCnpj, getActiveTenantId as getActiveTenantId$1 } from '@fayz-ai/core';
3
- import React6, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
4
- 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';
2
+ import { createConnectionStore, recordRailFields, RECORD_GROUP, activeFacet, describeError, getSupabaseClientOptional, formatCurrency, maskCpfCnpj, connectionStatus, definePlugin, getActiveTenantId as getActiveTenantId$1 } from '@fayz-ai/core';
3
+ import React, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
4
+ 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, CardContent, CardHeader, CardTitle, CardDescription, Badge, Switch, useFilterDraft, RowActions, PageHeaderActions, FilterPanel, FilterPanelField, SearchField, TooltipProvider, DataTable, ChevronPager, Tooltip, TooltipTrigger, TooltipContent, SegmentedControl, ComingSoon, toast } from '@fayz-ai/ui';
5
5
  export { ComingSoon, EmBreve } from '@fayz-ai/ui';
6
- import { registerDocumentOpener, PersonLink, getActiveTenantId, useUnitFilter, PageActions, adminNavigateTo, usePermissionOptional, UNIT_DETAIL_ZONES } from '@fayz-ai/admin';
7
- 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';
6
+ import { Loader2, Download, ChevronDown, FileCode2, FileText, RefreshCw, UserPlus, Gift, AlertTriangle, ChevronRight, Check, Trash2, Plus, KeyRound, Image, UploadCloud, Paperclip, ScanLine, Eye, X, ShieldCheck, ListChecks, FileDown, CheckCircle2, FileCheck2, XCircle, Inbox, Info } from 'lucide-react';
7
+ import { registerDocumentOpener, PersonLink, getActiveTenantId, useUnitFilter, PageActions, adminNavigateTo, usePermissionOptional, UNIT_DETAIL_ZONES, serviceBaseUrl } from '@fayz-ai/admin';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
9
  import { FINANCIAL_INVOICE_ZONES } from '@fayz-ai/financial/views/zones';
10
10
 
11
- var PLUGNOTAS_CONNECTOR_ID = "plugnotas";
12
- var PLUGNOTAS_MARK = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%230F766E'/%3E%3Cpath d='M10 7h12v18l-2-1.5-2 1.5-2-1.5-2 1.5-2-1.5-2 1.5V7Z' fill='white'/%3E%3Cpath d='M13 12h6M13 16h6M13 20h4' stroke='%230F766E' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E";
13
- var PLUGNOTAS_FIELDS = [
14
- {
15
- key: "emitterCnpj",
16
- label: "CNPJ do emitente",
17
- type: "text",
18
- placeholder: "00.000.000/0000-00"
11
+ var EmitterError = class extends Error {
12
+ constructor(status, code, message, gates = [], field = null) {
13
+ super(message);
14
+ this.status = status;
15
+ this.code = code;
16
+ this.gates = gates;
17
+ this.field = field;
18
+ this.name = "EmitterError";
19
19
  }
20
+ };
21
+ var SENTENCES = {
22
+ no_client: "O servi\xE7o de integra\xE7\xF5es n\xE3o est\xE1 configurado neste app.",
23
+ not_configured: "A emiss\xE3o fiscal ainda n\xE3o est\xE1 ligada no servidor.",
24
+ unauthorized: "Sua sess\xE3o expirou. Entre de novo.",
25
+ not_allowed: "Seu acesso n\xE3o permite alterar a configura\xE7\xE3o fiscal.",
26
+ unavailable: "N\xE3o foi poss\xEDvel falar com o servi\xE7o fiscal. Tente de novo em instantes.",
27
+ unknown_emitter: "Esta empresa n\xE3o foi encontrada nesta conta.",
28
+ cnpj_elsewhere: "Este CNPJ j\xE1 est\xE1 configurado para emiss\xE3o em outra conta.",
29
+ observed: "Esta empresa j\xE1 emite pelo sistema anterior. Ela fica s\xF3 para consulta aqui at\xE9 a migra\xE7\xE3o.",
30
+ not_ready: "Ainda falta preencher o que o cadastro na PlugNotas exige.",
31
+ no_cnpj: "A empresa n\xE3o tem um CNPJ de 14 d\xEDgitos.",
32
+ wrong_password: "A senha n\xE3o abre este certificado. Confira a senha do arquivo .pfx.",
33
+ not_a_certificate: "Este arquivo n\xE3o \xE9 um certificado A1 (.pfx ou .p12).",
34
+ no_certificate: "O arquivo n\xE3o traz nenhum certificado.",
35
+ certificate_of_another_cnpj: "Este certificado \xE9 de outro CNPJ.",
36
+ certificate_expired: "Este certificado est\xE1 vencido.",
37
+ not_base64: "N\xE3o foi poss\xEDvel ler o arquivo do certificado.",
38
+ invalid_setting: "Um dos valores n\xE3o \xE9 aceito.",
39
+ plugnotas_refused: "A PlugNotas recusou o envio.",
40
+ admin_only: "S\xF3 o suporte pode mudar isso."
41
+ };
42
+ var FIELD_SENTENCES = {
43
+ "nfce.cscId": "O ID do CSC tem s\xF3 n\xFAmeros.",
44
+ "nfce.series": "A s\xE9rie vai de 1 a 889.",
45
+ "nfe.series": "A s\xE9rie vai de 1 a 889.",
46
+ "nfce.nextNumber": "O pr\xF3ximo n\xFAmero precisa ser 1 ou maior.",
47
+ "nfe.nextNumber": "O pr\xF3ximo n\xFAmero precisa ser 1 ou maior.",
48
+ taxRegimeCode: "Escolha o regime tribut\xE1rio.",
49
+ specialTaxRegimeCode: "O regime especial vai de 0 a 11."
50
+ };
51
+ function emitterErrorSentence(error) {
52
+ if (error instanceof EmitterError) {
53
+ if (error.field && FIELD_SENTENCES[error.field]) return FIELD_SENTENCES[error.field];
54
+ return SENTENCES[error.code] ?? error.message;
55
+ }
56
+ return error instanceof Error ? error.message : String(error);
57
+ }
58
+ var GATE_LABELS = {
59
+ cnpj: "CNPJ da empresa",
60
+ legal_name: "Raz\xE3o social",
61
+ tax_regime: "Regime tribut\xE1rio",
62
+ email: "E-mail fiscal",
63
+ state_registration: "Inscri\xE7\xE3o estadual (ou ISENTO)",
64
+ address: "Endere\xE7o fiscal completo (rua, cidade, UF e CEP)",
65
+ ibge_city_code: "C\xF3digo IBGE da cidade",
66
+ certificate: "Certificado A1 enviado",
67
+ certificate_holder: "Certificado do mesmo CNPJ",
68
+ certificate_valid: "Certificado dentro da validade",
69
+ nfce_enabled: "NFC-e ligada",
70
+ nfce_series: "S\xE9rie da NFC-e",
71
+ csc: "CSC (ID e c\xF3digo) da NFC-e",
72
+ registered: "Empresa cadastrada na PlugNotas"
73
+ };
74
+ async function request(method, path, body) {
75
+ const base = serviceBaseUrl();
76
+ const supabase = getSupabaseClientOptional();
77
+ if (!base || !supabase) throw new EmitterError(503, "no_client", "the integration service is not configured");
78
+ const { data } = await supabase.auth.getSession();
79
+ const token = data?.session?.access_token;
80
+ if (!token) throw new EmitterError(401, "unauthorized", "sign in again");
81
+ const tenant = getActiveTenantId$1();
82
+ let response;
83
+ try {
84
+ response = await fetch(`${base}${path}`, {
85
+ method,
86
+ headers: {
87
+ authorization: `Bearer ${token}`,
88
+ ...tenant ? { "x-fayz-tenant": tenant } : {},
89
+ ...body === void 0 ? {} : { "content-type": "application/json" }
90
+ },
91
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
92
+ });
93
+ } catch (error) {
94
+ throw new EmitterError(503, "unavailable", error instanceof Error ? error.message : String(error));
95
+ }
96
+ const text = await response.text();
97
+ let payload = null;
98
+ try {
99
+ payload = text ? JSON.parse(text) : null;
100
+ } catch {
101
+ }
102
+ if (response.ok) return payload;
103
+ throw new EmitterError(
104
+ response.status,
105
+ payload?.error ?? "unavailable",
106
+ payload?.message ?? text.slice(0, 200),
107
+ Array.isArray(payload?.gates) ? payload.gates : [],
108
+ typeof payload?.field === "string" ? payload.field : null
109
+ );
110
+ }
111
+ var one = (legalEntityId, rest = "") => `/v1/fiscal/emitters/${encodeURIComponent(legalEntityId)}${rest}`;
112
+ async function base64Of(file) {
113
+ const bytes = new Uint8Array(await file.arrayBuffer());
114
+ let binary = "";
115
+ for (let i = 0; i < bytes.length; i += 32768) binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
116
+ return btoa(binary);
117
+ }
118
+ var emitters = {
119
+ list: async () => (await request("GET", "/v1/fiscal/emitters")).emitters,
120
+ /** Also asks PlugNotas what it holds, and lists what issuing still needs. */
121
+ read: (legalEntityId) => request("GET", one(legalEntityId)),
122
+ save: (legalEntityId, settings) => request("PATCH", one(legalEntityId), settings),
123
+ certificate: async (legalEntityId, file, password) => request("PUT", one(legalEntityId, "/certificate"), { pfxBase64: await base64Of(file), password, fileName: file.name }),
124
+ register: (legalEntityId) => request("PUT", one(legalEntityId, "/registration"))
125
+ };
126
+ var TAX_REGIMES = [
127
+ { value: 1, label: "Simples Nacional" },
128
+ { value: 2, label: "Simples Nacional \u2014 excesso" },
129
+ // Presumed and real profit are both "normal" in PlugNotas' words; the label
130
+ // says so for the store to find its own (V1).
131
+ { value: 3, label: "Regime Normal (Lucro Presumido / Lucro Real)" }
20
132
  ];
21
- var PLUGNOTAS_SETTINGS = [{
22
- key: "environment",
23
- label: "Ambiente fiscal",
24
- description: "Sandbox simula o emissor. Homologa\xE7\xE3o envia para a SEFAZ sem valor fiscal.",
25
- type: "select",
26
- options: [
27
- { value: "sandbox", label: "Sandbox simulado" },
28
- { value: "homologation", label: "Homologa\xE7\xE3o SEFAZ" }
29
- ],
30
- requiredToOperate: true,
31
- defaultValue: "sandbox"
32
- }];
33
- var INTERNAL_SETTINGS = [
34
- "certificateReady",
35
- "cscReady",
36
- "providerEmitterId",
37
- "certificateExpiresAt",
38
- "certificateFingerprint",
39
- // Non-secret operational data written by the host's server-side setup flow.
40
- // Declaring these keys keeps the shared connection store authoritative while
41
- // credentials (API key, A1 bytes/password, CSC) remain outside public JSON.
42
- "stateRegistration",
43
- "corporateName",
44
- "tradeName",
45
- "email",
46
- "taxRegime",
47
- "address",
48
- "series",
49
- "paymentMethod"
133
+ var SPECIAL_TAX_REGIMES = [
134
+ { value: 0, label: "Sem regime especial" },
135
+ { value: 1, label: "Microempresa municipal" },
136
+ { value: 2, label: "Estimativa" },
137
+ { value: 3, label: "Sociedade de profissionais" },
138
+ { value: 4, label: "Cooperativa" },
139
+ { value: 5, label: "MEI" },
140
+ { value: 6, label: "ME ou EPP" },
141
+ { value: 7, label: "Lucro Real" },
142
+ { value: 8, label: "Lucro Presumido" },
143
+ { value: 9, label: "Tributa\xE7\xE3o normal" },
144
+ { value: 10, label: "Simples Nacional com excesso do sublimite" },
145
+ { value: 11, label: "Empresa de responsabilidade limitada" }
50
146
  ];
51
- var DAY_MS = 24 * 60 * 60 * 1e3;
147
+ function EmittersPanel() {
148
+ const [list, setList] = React.useState(null);
149
+ const [error, setError] = React.useState(null);
150
+ const [openId, setOpenId] = React.useState(null);
151
+ const load = React.useCallback(() => {
152
+ setError(null);
153
+ emitters.list().then((rows) => {
154
+ setList(rows);
155
+ if (rows.length === 1) setOpenId(rows[0].legalEntityId);
156
+ }).catch((e) => setError(emitterErrorSentence(e)));
157
+ }, []);
158
+ React.useEffect(load, [load]);
159
+ if (error) {
160
+ return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col items-start gap-3 py-6", children: [
161
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: error }),
162
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: load, children: "Tentar de novo" })
163
+ ] }) });
164
+ }
165
+ if (!list) return /* @__PURE__ */ jsx(Loading, {});
166
+ if (!list.length) {
167
+ return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "py-6 text-sm text-muted-foreground", children: "Nenhuma unidade desta conta tem CNPJ ainda. Preencha a raz\xE3o social e o CNPJ em Configura\xE7\xF5es \u2192 Unidades para configurar a emiss\xE3o." }) });
168
+ }
169
+ return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: list.map((e) => /* @__PURE__ */ jsx(
170
+ EmitterCard,
171
+ {
172
+ summary: e,
173
+ open: openId === e.legalEntityId,
174
+ onToggle: () => setOpenId((id) => id === e.legalEntityId ? null : e.legalEntityId)
175
+ },
176
+ e.legalEntityId
177
+ )) });
178
+ }
179
+ function Loading() {
180
+ return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "space-y-3 py-6", children: [
181
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-48" }),
182
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" })
183
+ ] }) });
184
+ }
185
+ function EmitterCard({ summary, open, onToggle }) {
186
+ const [emitter, setEmitter] = React.useState(null);
187
+ const [error, setError] = React.useState(null);
188
+ const read = React.useCallback(() => {
189
+ setError(null);
190
+ emitters.read(summary.legalEntityId).then(setEmitter).catch((e) => setError(emitterErrorSentence(e)));
191
+ }, [summary.legalEntityId]);
192
+ React.useEffect(() => {
193
+ if (open && !emitter) read();
194
+ }, [open, emitter, read]);
195
+ const shown = emitter ?? summary;
196
+ const observed = shown.mode === "observe";
197
+ return /* @__PURE__ */ jsxs(Card, { children: [
198
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex-row items-start justify-between gap-3 space-y-0", children: [
199
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
200
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: shown.tradeName || shown.legalName || "Empresa sem nome" }),
201
+ /* @__PURE__ */ jsx(CardDescription, { children: [shown.legalName, shown.cnpj ? `CNPJ ${maskCpfCnpj(shown.cnpj)}` : null].filter(Boolean).join(" \xB7 ") })
202
+ ] }),
203
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
204
+ /* @__PURE__ */ jsx(StatusBadge, { emitter: shown }),
205
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onToggle, children: open ? "Fechar" : "Configurar" })
206
+ ] })
207
+ ] }),
208
+ open && /* @__PURE__ */ jsxs(CardContent, { className: "space-y-5", children: [
209
+ error && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border border-destructive/40 p-3 text-sm", children: [
210
+ /* @__PURE__ */ jsx("span", { children: error }),
211
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: read, children: "Tentar de novo" })
212
+ ] }),
213
+ !emitter && !error && /* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full" }),
214
+ emitter && observed && /* @__PURE__ */ jsx(Observed, { emitter }),
215
+ emitter && !observed && /* @__PURE__ */ jsx(Setup, { emitter, onChange: setEmitter })
216
+ ] })
217
+ ] });
218
+ }
219
+ function StatusBadge({ emitter }) {
220
+ if (emitter.mode === "observe") return /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: "gap-1 text-muted-foreground", children: [
221
+ /* @__PURE__ */ jsx(Eye, { className: "h-3 w-3" }),
222
+ "Emite pelo sistema anterior"
223
+ ] });
224
+ if (emitter.ready === true) return /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "border-emerald-500/40 bg-emerald-500/10 text-emerald-700 dark:text-emerald-400", children: "Pronto para emitir" });
225
+ if (emitter.certificate?.warning === "expired") return /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: "Certificado vencido" });
226
+ if (emitter.ready === false) return /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-muted-foreground", children: "Configura\xE7\xE3o incompleta" });
227
+ return null;
228
+ }
229
+ function Observed({ emitter }) {
230
+ const p = emitter.provider;
231
+ const cert = p?.certificate;
232
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 text-sm", children: [
233
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Esta empresa j\xE1 est\xE1 cadastrada na PlugNotas e emite pelo sistema anterior. Aqui ela fica s\xF3 para consulta, para que os dois sistemas n\xE3o mudem o mesmo cadastro. A troca \xE9 feita pelo suporte, quando a emiss\xE3o passar para c\xE1." }),
234
+ /* @__PURE__ */ jsxs("dl", { className: "grid gap-x-6 gap-y-1", style: { gridTemplateColumns: "auto 1fr" }, children: [
235
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "NFC-e" }),
236
+ /* @__PURE__ */ jsx("dd", { children: p?.nfce?.active ? `Ligada \xB7 ${p.nfce.production ? "Produ\xE7\xE3o" : "Homologa\xE7\xE3o"}${p.nfce.series.length ? ` \xB7 s\xE9rie ${p.nfce.series.join(", ")}` : ""}` : "Desligada" }),
237
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Notas recebidas (DF-e)" }),
238
+ /* @__PURE__ */ jsx("dd", { children: p?.nfe?.dfe ? "Ligado" : "Desligado" }),
239
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Certificado A1" }),
240
+ /* @__PURE__ */ jsx("dd", { children: cert ? `${cert.expiresAt ? `v\xE1lido at\xE9 ${new Date(cert.expiresAt).toLocaleDateString("pt-BR")}` : "enviado"}` : "Nenhum" })
241
+ ] })
242
+ ] });
243
+ }
244
+ function Setup({ emitter, onChange }) {
245
+ const [draft, setDraft] = React.useState(() => draftOf(emitter));
246
+ const [cscToken, setCscToken] = React.useState("");
247
+ const [saving, setSaving] = React.useState(false);
248
+ const [registering, setRegistering] = React.useState(false);
249
+ const [missing, setMissing] = React.useState(null);
250
+ React.useEffect(() => setDraft(draftOf(emitter)), [emitter]);
251
+ const set = (patch) => setDraft((d) => ({ ...d, ...patch }));
252
+ const gates = missing ?? emitter.gates ?? [];
253
+ const save = async () => {
254
+ const input = {
255
+ taxRegimeCode: draft.taxRegimeCode,
256
+ specialTaxRegimeCode: draft.specialTaxRegimeCode,
257
+ nfce: {
258
+ enabled: draft.nfceEnabled,
259
+ production: draft.nfceProduction,
260
+ series: draft.nfceSeries === "" ? null : Number(draft.nfceSeries),
261
+ nextNumber: draft.nfceNextNumber === "" ? null : Number(draft.nfceNextNumber),
262
+ cscId: draft.cscId.trim() === "" ? null : draft.cscId.trim(),
263
+ ...cscToken ? { cscToken } : {}
264
+ }
265
+ };
266
+ setSaving(true);
267
+ try {
268
+ await emitters.save(emitter.legalEntityId, input);
269
+ setCscToken("");
270
+ const fresh = await emitters.read(emitter.legalEntityId);
271
+ onChange(fresh);
272
+ setMissing(null);
273
+ toast.success("Configura\xE7\xE3o fiscal salva");
274
+ return fresh;
275
+ } catch (e) {
276
+ toast.error("N\xE3o foi poss\xEDvel salvar", { description: emitterErrorSentence(e) });
277
+ return null;
278
+ } finally {
279
+ setSaving(false);
280
+ }
281
+ };
282
+ const register = async () => {
283
+ const saved = await save();
284
+ if (!saved) return;
285
+ setRegistering(true);
286
+ try {
287
+ const after = await emitters.register(emitter.legalEntityId);
288
+ onChange(after);
289
+ setMissing(null);
290
+ toast.success(after.provider?.registered ? "Empresa cadastrada na PlugNotas" : "Cadastro enviado");
291
+ } catch (e) {
292
+ if (e instanceof EmitterError && e.gates.length) setMissing(e.gates);
293
+ toast.error("N\xE3o foi poss\xEDvel cadastrar na PlugNotas", { description: emitterErrorSentence(e) });
294
+ } finally {
295
+ setRegistering(false);
296
+ }
297
+ };
298
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
299
+ /* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-md border p-4", children: [
300
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Empresa" }),
301
+ /* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
302
+ /* @__PURE__ */ jsx(Field, { label: "Regime tribut\xE1rio", children: /* @__PURE__ */ jsxs(Select, { value: draft.taxRegimeCode ? String(draft.taxRegimeCode) : void 0, onValueChange: (v) => set({ taxRegimeCode: Number(v) }), children: [
303
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Selecione" }) }),
304
+ /* @__PURE__ */ jsx(SelectContent, { children: TAX_REGIMES.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: String(o.value), children: o.label }, o.value)) })
305
+ ] }) }),
306
+ /* @__PURE__ */ jsx(Field, { label: "Regime tribut\xE1rio especial", children: /* @__PURE__ */ jsxs(Select, { value: String(draft.specialTaxRegimeCode), onValueChange: (v) => set({ specialTaxRegimeCode: Number(v) }), children: [
307
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
308
+ /* @__PURE__ */ jsx(SelectContent, { children: SPECIAL_TAX_REGIMES.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: String(o.value), children: o.label }, o.value)) })
309
+ ] }) })
310
+ ] }) }),
311
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Raz\xE3o social, CNPJ, inscri\xE7\xF5es e endere\xE7o v\xEAm de Configura\xE7\xF5es \u2192 Unidades \u2192 Dados da empresa." })
312
+ ] }),
313
+ /* @__PURE__ */ jsx(Certificate, { emitter, onChange }),
314
+ /* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-md border p-4", children: [
315
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
316
+ /* @__PURE__ */ jsxs("div", { children: [
317
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "NFC-e \u2014 Cupom do Consumidor" }),
318
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Vendas no balc\xE3o e na comanda" })
319
+ ] }),
320
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
321
+ /* @__PURE__ */ jsxs(Select, { value: draft.nfceProduction ? "prod" : "hom", onValueChange: (v) => set({ nfceProduction: v === "prod" }), disabled: !draft.nfceEnabled, children: [
322
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-36", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
323
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
324
+ /* @__PURE__ */ jsx(SelectItem, { value: "hom", children: "Homologa\xE7\xE3o" }),
325
+ /* @__PURE__ */ jsx(SelectItem, { value: "prod", children: "Produ\xE7\xE3o" })
326
+ ] })
327
+ ] }),
328
+ /* @__PURE__ */ jsx(Switch, { checked: draft.nfceEnabled, onCheckedChange: (v) => set({ nfceEnabled: v }) })
329
+ ] })
330
+ ] }),
331
+ draft.nfceEnabled && /* @__PURE__ */ jsx("div", { className: "fz-container space-y-4", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
332
+ /* @__PURE__ */ jsx(Field, { label: "S\xE9rie", hint: "Use uma s\xE9rie que nenhum outro sistema use para este CNPJ.", children: /* @__PURE__ */ jsx(Input, { inputMode: "numeric", value: draft.nfceSeries, onChange: (e) => set({ nfceSeries: e.target.value.replace(/\D/g, "") }) }) }),
333
+ /* @__PURE__ */ jsx(Field, { label: "Pr\xF3ximo n\xFAmero", hint: "S\xF3 para uma s\xE9rie nova: de onde a numera\xE7\xE3o come\xE7a.", children: /* @__PURE__ */ jsx(Input, { inputMode: "numeric", value: draft.nfceNextNumber, onChange: (e) => set({ nfceNextNumber: e.target.value.replace(/\D/g, "") }) }) }),
334
+ /* @__PURE__ */ jsx(Field, { label: "CSC \u2014 ID", children: /* @__PURE__ */ jsx(Input, { inputMode: "numeric", value: draft.cscId, onChange: (e) => set({ cscId: e.target.value }) }) }),
335
+ /* @__PURE__ */ jsx(Field, { label: "CSC \u2014 C\xF3digo", hint: "O CSC (ID num\xE9rico e c\xF3digo) \xE9 gerado no portal da SEFAZ do estado. N\xE3o \xE9 o login de nenhum sistema.", children: /* @__PURE__ */ jsx(
336
+ Input,
337
+ {
338
+ type: "password",
339
+ autoComplete: "new-password",
340
+ value: cscToken,
341
+ placeholder: emitter.nfce.cscTokenSet ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 (configurado, digite para trocar)" : "",
342
+ onChange: (e) => setCscToken(e.target.value)
343
+ }
344
+ ) })
345
+ ] }) })
346
+ ] }),
347
+ gates.length > 0 && /* @__PURE__ */ jsx("ul", { className: "flex flex-wrap gap-2", children: gates.map((g) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: g.ok ? "gap-1 border-emerald-500/40 text-emerald-700 dark:text-emerald-400" : "gap-1 text-muted-foreground", children: [
348
+ g.ok ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }),
349
+ GATE_LABELS[g.id] ?? g.id
350
+ ] }) }, g.id)) }),
351
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-3", children: [
352
+ emitter.providerSyncedAt && /* @__PURE__ */ jsxs("p", { className: "mr-auto text-xs text-muted-foreground", children: [
353
+ "Cadastro na PlugNotas atualizado em ",
354
+ new Date(emitter.providerSyncedAt).toLocaleString("pt-BR")
355
+ ] }),
356
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => void save(), disabled: saving || registering, children: saving && !registering ? "Salvando\u2026" : "Salvar" }),
357
+ /* @__PURE__ */ jsx(Button, { onClick: () => void register(), disabled: saving || registering, children: registering ? "Cadastrando\u2026" : emitter.provider?.registered ? "Salvar e atualizar na PlugNotas" : "Salvar e cadastrar na PlugNotas" })
358
+ ] })
359
+ ] });
360
+ }
361
+ function Certificate({ emitter, onChange }) {
362
+ const [file, setFile] = React.useState(null);
363
+ const [password, setPassword] = React.useState("");
364
+ const [sending, setSending] = React.useState(false);
365
+ const c = emitter.certificate;
366
+ const send = async () => {
367
+ if (!file) return;
368
+ setSending(true);
369
+ try {
370
+ await emitters.certificate(emitter.legalEntityId, file, password);
371
+ onChange(await emitters.read(emitter.legalEntityId));
372
+ setFile(null);
373
+ setPassword("");
374
+ toast.success("Certificado enviado");
375
+ } catch (e) {
376
+ toast.error("N\xE3o foi poss\xEDvel enviar o certificado", { description: emitterErrorSentence(e) });
377
+ } finally {
378
+ setSending(false);
379
+ }
380
+ };
381
+ return /* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-md border p-4", children: [
382
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
383
+ /* @__PURE__ */ jsxs("div", { children: [
384
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1.5 text-sm font-medium", children: [
385
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "h-4 w-4" }),
386
+ " Certificado digital A1"
387
+ ] }),
388
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: c ? [c.holder?.split(",")[0]?.replace(/^CN=/, ""), c.expiresAt ? `v\xE1lido at\xE9 ${new Date(c.expiresAt).toLocaleDateString("pt-BR")}` : null].filter(Boolean).join(" \xB7 ") : "Nenhum certificado enviado" })
389
+ ] }),
390
+ c && /* @__PURE__ */ jsx(Badge, { variant: c.warning === "expired" ? "destructive" : "secondary", children: c.warning === "expired" ? "Vencido" : c.warning === "expiring" ? "Vence em breve" : "Ativo" })
391
+ ] }),
392
+ /* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
393
+ /* @__PURE__ */ jsx(Field, { label: "Arquivo (.pfx ou .p12)", children: /* @__PURE__ */ jsx(Input, { type: "file", accept: ".pfx,.p12,application/x-pkcs12", onChange: (e) => setFile(e.target.files?.[0] ?? null) }) }),
394
+ /* @__PURE__ */ jsx(Field, { label: "Senha do certificado", children: /* @__PURE__ */ jsx(Input, { type: "password", autoComplete: "new-password", value: password, onChange: (e) => setPassword(e.target.value) }) })
395
+ ] }) }),
396
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
397
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "O arquivo vai direto para a PlugNotas e n\xE3o fica guardado no nosso sistema." }),
398
+ /* @__PURE__ */ jsx(Button, { size: "sm", disabled: !file || !password || sending, onClick: () => void send(), children: sending ? "Enviando\u2026" : c ? "Trocar certificado" : "Enviar certificado" })
399
+ ] })
400
+ ] });
401
+ }
402
+ function Field({ label, hint, children }) {
403
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
404
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: label }),
405
+ children,
406
+ hint && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: hint })
407
+ ] });
408
+ }
409
+ function draftOf(e) {
410
+ return {
411
+ taxRegimeCode: e.taxRegimeCode,
412
+ specialTaxRegimeCode: e.specialTaxRegimeCode,
413
+ nfceEnabled: e.nfce.enabled,
414
+ nfceProduction: e.nfce.production,
415
+ nfceSeries: e.nfce.series ? String(e.nfce.series) : "",
416
+ nfceNextNumber: e.nfce.nextNumber ? String(e.nfce.nextNumber) : "",
417
+ cscId: e.nfce.cscId ?? ""
418
+ };
419
+ }
420
+
421
+ // src/connectors/plugnotas.ts
422
+ var PLUGNOTAS_CONNECTOR_ID = "plugnotas";
423
+ var PLUGNOTAS_MARK = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%230F766E'/%3E%3Cpath d='M10 7h12v18l-2-1.5-2 1.5-2-1.5-2 1.5-2-1.5-2 1.5V7Z' fill='white'/%3E%3Cpath d='M13 12h6M13 16h6M13 20h4' stroke='%230F766E' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E";
52
424
  function digits(value) {
53
425
  return value.replace(/\D/g, "");
54
426
  }
@@ -64,43 +436,8 @@ function isValidCnpj(value) {
64
436
  const second = digit(`${normalized.slice(0, 12)}${first}`, [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]);
65
437
  return normalized.endsWith(`${first}${second}`);
66
438
  }
67
- function environment(value) {
68
- if (value === void 0 || value === null || value === "" || value === "sandbox") return "sandbox";
69
- if (value === "homologation") return value;
70
- throw new Error("Produ\xE7\xE3o n\xE3o est\xE1 dispon\xEDvel neste conector preview.");
71
- }
72
- function store() {
73
- return createConnectionStore(PLUGNOTAS_CONNECTOR_ID, {
74
- fields: [...PLUGNOTAS_FIELDS],
75
- settings: [...PLUGNOTAS_SETTINGS],
76
- settingsKeys: INTERNAL_SETTINGS
77
- });
78
- }
79
- function deriveStatus(connection) {
80
- if (!connection?.active) return { connected: false };
81
- const readiness = settingsStatus([...PLUGNOTAS_SETTINGS], connection);
82
- const certificateReady = connection.settings.certificateReady === true;
83
- const cscReady = connection.settings.cscReady === true;
84
- const expiry = typeof connection.settings.certificateExpiresAt === "string" ? Date.parse(connection.settings.certificateExpiresAt) : Number.NaN;
85
- const daysUntilExpiry = Number.isFinite(expiry) ? Math.ceil((expiry - Date.now()) / DAY_MS) : null;
86
- const expired = daysUntilExpiry !== null && daysUntilExpiry <= 0;
87
- const expiringSoon = daysUntilExpiry !== null && daysUntilExpiry > 0 && daysUntilExpiry <= 30;
88
- const missingSettings = [.../* @__PURE__ */ new Set([
89
- ...readiness.missingSettings ?? [],
90
- ...!certificateReady || expired ? ["certificate"] : [],
91
- ...!cscReady ? ["csc"] : []
92
- ])];
93
- return {
94
- connected: true,
95
- needsSetup: missingSettings.length > 0,
96
- ...missingSettings.length ? { missingSettings } : {},
97
- detail: expired ? "Certificado A1 vencido" : !certificateReady ? "Envie o certificado A1 do emitente." : !cscReady ? "Confirme o CSC e o ID Token do emitente." : connection.settings.environment === "homologation" ? "Emitente pronto em homologa\xE7\xE3o" : "Emitente pronto no sandbox",
98
- ...expiringSoon ? { issues: [`O certificado A1 vence em ${daysUntilExpiry} dia${daysUntilExpiry === 1 ? "" : "s"}.`] } : {},
99
- plane: "sync"
100
- };
101
- }
102
- function createPlugNotasConnector(options = {}) {
103
- const connectionStore = store();
439
+ var store = createConnectionStore(PLUGNOTAS_CONNECTOR_ID, {});
440
+ function createPlugNotasConnector() {
104
441
  return {
105
442
  id: PLUGNOTAS_CONNECTOR_ID,
106
443
  // PlugBank and PlugNotas are different products from the same vendor. The
@@ -108,8 +445,8 @@ function createPlugNotasConnector(options = {}) {
108
445
  // other in the generated connector map.
109
446
  catalogSlug: "plugnotas",
110
447
  hostPluginId: "financial",
111
- name: "PlugNotas \xB7 NFC-e",
112
- description: "Emita NFC-e pela TecnoSpeed sem expor certificado, senha ou API key no navegador.",
448
+ name: "PlugNotas \xB7 Notas fiscais",
449
+ description: "Emita NFC-e pela TecnoSpeed. Certificado, CSC e cadastro da empresa ficam no servidor, nunca no navegador.",
113
450
  icon: "ReceiptText",
114
451
  logoUrl: PLUGNOTAS_MARK,
115
452
  authKind: "api-key",
@@ -117,76 +454,33 @@ function createPlugNotasConnector(options = {}) {
117
454
  direction: "outbound",
118
455
  planes: ["sync"],
119
456
  docsUrl: "https://docs.plugnotas.com.br/",
120
- fields: [...PLUGNOTAS_FIELDS],
121
- settings: [...PLUGNOTAS_SETTINGS],
457
+ fields: [],
122
458
  permissions: [{
123
459
  kind: "data",
124
460
  resource: "fiscal-document",
125
461
  actions: ["read", "write"],
126
- reason: "Enviar a NFC-e e reconciliar situa\xE7\xE3o, chave de acesso e artefatos no Financeiro.",
462
+ reason: "Cadastrar a empresa na PlugNotas e emitir as notas das vendas.",
127
463
  sensitive: true
128
464
  }],
129
- invokedAt: [{
130
- key: "financial.nfce.issue",
131
- label: "Quando um operador confirma a emiss\xE3o da NFC-e de um pedido conclu\xEDdo",
132
- plane: "sync",
133
- trigger: "manual",
134
- touches: ["fiscal-document"]
135
- }],
136
465
  howItSyncs: [
137
- "A emiss\xE3o cria primeiro um comando dur\xE1vel e s\xF3 depois chama o provedor.",
138
- "Webhook e recupera\xE7\xE3o consultiva convergem o mesmo comando, sem reenviar no escuro."
466
+ "Cada empresa (CNPJ) \xE9 configurada na aba Emitentes: regime, certificado A1, CSC e s\xE9rie da NFC-e.",
467
+ "O cadastro na PlugNotas \xE9 feito pelo nosso servidor, que confere tudo o que a nota exige antes de enviar.",
468
+ "Uma empresa que j\xE1 emite pelo sistema anterior aparece s\xF3 para consulta, para os dois sistemas n\xE3o mudarem o mesmo cadastro."
139
469
  ],
140
- ...connectionStatus(PLUGNOTAS_CONNECTOR_ID, deriveStatus, { store }),
141
- ...connectionSettings(connectionStore),
142
- async testConnection(values) {
143
- const emitterCnpj = digits(values.emitterCnpj ?? "");
144
- if (!isValidCnpj(emitterCnpj)) return { ok: false, message: "Informe um CNPJ v\xE1lido." };
145
- let selected;
146
- try {
147
- selected = environment(values.environment);
148
- } catch (error) {
149
- return { ok: false, message: error instanceof Error ? error.message : "Ambiente inv\xE1lido." };
150
- }
151
- return options.client?.testEmitter ? options.client.testEmitter({ emitterCnpj, environment: selected }) : { ok: true };
470
+ ...connectionStatus(PLUGNOTAS_CONNECTOR_ID, (connection) => ({
471
+ connected: !!connection?.active,
472
+ detail: "Configure cada empresa na aba Emitentes.",
473
+ plane: "sync"
474
+ }), { store: () => store }),
475
+ saveConnection: async () => {
476
+ await store.save({ active: true });
152
477
  },
153
- async saveConnection(values) {
154
- const emitterCnpj = digits(values.emitterCnpj ?? "");
155
- if (!isValidCnpj(emitterCnpj)) throw new Error("Informe um CNPJ v\xE1lido.");
156
- await connectionStore.save({
157
- active: true,
158
- displayName: `Emitente ${emitterCnpj.slice(-6)}`,
159
- providerAccountKey: emitterCnpj,
160
- settings: {
161
- emitterCnpj,
162
- environment: environment(values.environment),
163
- certificateReady: false,
164
- cscReady: false
165
- }
166
- });
167
- },
168
- ...options.client?.disconnectEmitter ? { disconnect: () => options.client.disconnectEmitter() } : {},
169
- disconnectNote: "As notas existentes permanecem no Financeiro; somente novas emiss\xF5es s\xE3o interrompidas.",
170
- ExtraPanel: options.SetupPanel
478
+ OperatePanel: EmittersPanel,
479
+ operateLabel: "Emitentes",
480
+ disconnect: () => store.disconnect(),
481
+ disconnectNote: "As notas j\xE1 emitidas continuam no Financeiro, e o cadastro das empresas na PlugNotas n\xE3o \xE9 apagado."
171
482
  };
172
483
  }
173
- async function markPlugNotasEmitterReady(input) {
174
- const emitterCnpj = digits(input.emitterCnpj);
175
- if (!isValidCnpj(emitterCnpj)) throw new Error("Informe um CNPJ v\xE1lido.");
176
- await store().save({
177
- active: true,
178
- providerAccountKey: emitterCnpj,
179
- settings: {
180
- emitterCnpj,
181
- environment: environment(input.environment),
182
- certificateReady: input.certificateReady,
183
- cscReady: input.cscReady,
184
- providerEmitterId: input.providerEmitterId,
185
- certificateExpiresAt: input.certificateExpiresAt,
186
- certificateFingerprint: input.certificateFingerprint
187
- }
188
- });
189
- }
190
484
 
191
485
  // src/migrations/index.ts
192
486
  var MIGRATION_000_BASELINE = `-- Fiscal Brasil / PlugNotas baseline.
@@ -6455,7 +6749,7 @@ function suggestedStockUnit(item, units, productUnits, productId) {
6455
6749
  const code = NOTE_UNIT_CODES[String(item.unit ?? "").trim().toUpperCase().replace(/\d+$/, "")];
6456
6750
  return code ? units.find((u) => u.abbreviation.toLowerCase() === code)?.id ?? null : null;
6457
6751
  }
6458
- var draftOf = (item) => ({
6752
+ var draftOf2 = (item) => ({
6459
6753
  productId: item.productId,
6460
6754
  productLabel: "",
6461
6755
  entryType: item.entryType ?? null,
@@ -6545,7 +6839,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
6545
6839
  setDrafts(Object.fromEntries(detail.items.map((i) => [
6546
6840
  i.id,
6547
6841
  {
6548
- ...draftOf(i),
6842
+ ...draftOf2(i),
6549
6843
  productLabel: i.productId && nomes.get(i.productId) || "",
6550
6844
  // Stocked lines keep what was recorded; open ones get a unit instead of a blank.
6551
6845
  stockUnitId: i.stockUnitId ?? (i.stockMovementId ? null : suggestedStockUnit(i, u, unidadesDoProduto, i.productId))
@@ -6598,7 +6892,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
6598
6892
  const buildDecisions = useCallback((rascunhos) => {
6599
6893
  if (!note) return [];
6600
6894
  return note.items.map((item) => {
6601
- const d = rascunhos[item.id] ?? draftOf(item);
6895
+ const d = rascunhos[item.id] ?? draftOf2(item);
6602
6896
  return {
6603
6897
  id: item.id,
6604
6898
  productId: isStockBearing(d.entryType) ? d.productId : null,
@@ -6623,7 +6917,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
6623
6917
  }).length;
6624
6918
  const missingType = decisions.filter((d) => !d.entryType).length;
6625
6919
  const suspectFactor = (item, d) => packagingCount(item.unit) !== null && isStockBearing(d.entryType) && ((Number(d.conversionFactor.replace(",", ".")) || 1) === 1 || !d.stockUnitId);
6626
- const unverifiedFactor = note ? note.items.filter((i) => suspectFactor(i, drafts[i.id] ?? draftOf(i))).length : 0;
6920
+ const unverifiedFactor = note ? note.items.filter((i) => suspectFactor(i, drafts[i.id] ?? draftOf2(i))).length : 0;
6627
6921
  const instalmentSum = instalments.reduce((acc, d) => acc + (Number(d.amount) || 0), 0);
6628
6922
  const hasPayable = Boolean(note?.invoiceId);
6629
6923
  const pendingStock = (note?.items ?? []).some((i) => !i.stockMovementId && (!i.entryType || isStockBearing(i.entryType)));
@@ -6652,13 +6946,13 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
6652
6946
  }, [note, buildDecisions, defaultAccount, location, instalments]);
6653
6947
  const pendingItems = useMemo(
6654
6948
  () => (note?.items ?? []).filter((item) => {
6655
- const d = drafts[item.id] ?? draftOf(item);
6949
+ const d = drafts[item.id] ?? draftOf2(item);
6656
6950
  return !d.entryType || isStockBearing(d.entryType) && !d.productId;
6657
6951
  }),
6658
6952
  [note, drafts]
6659
6953
  );
6660
6954
  const machineItems = useMemo(
6661
- () => (note?.items ?? []).filter((item) => (drafts[item.id] ?? draftOf(item)).suggestedBy !== null),
6955
+ () => (note?.items ?? []).filter((item) => (drafts[item.id] ?? draftOf2(item)).suggestedBy !== null),
6662
6956
  [note, drafts]
6663
6957
  );
6664
6958
  const sugerir = useCallback(async (reavaliar2 = false) => {
@@ -6969,17 +7263,17 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
6969
7263
  /* @__PURE__ */ jsx("dt", { className: "text-xs text-muted-foreground", children: "Fornecedor" }),
6970
7264
  /* @__PURE__ */ jsx("dd", { className: "text-sm", children: note.supplierPartyId ? /* @__PURE__ */ jsx(PersonLink, { personId: note.supplierPartyId, name: note.supplierName || "\u2014", kind: "supplier" }) : note.supplierName || "\u2014" })
6971
7265
  ] }),
6972
- /* @__PURE__ */ jsx(Field, { label: "CNPJ", value: note.supplierCnpj ?? "\u2014", mono: true }),
6973
- /* @__PURE__ */ jsx(Field, { label: "Natureza da opera\xE7\xE3o", value: note.natureOperation ?? "\u2014" }),
7266
+ /* @__PURE__ */ jsx(Field2, { label: "CNPJ", value: note.supplierCnpj ?? "\u2014", mono: true }),
7267
+ /* @__PURE__ */ jsx(Field2, { label: "Natureza da opera\xE7\xE3o", value: note.natureOperation ?? "\u2014" }),
6974
7268
  /* @__PURE__ */ jsx(
6975
- Field,
7269
+ Field2,
6976
7270
  {
6977
7271
  label: "Manifesta\xE7\xE3o",
6978
7272
  value: `${note.manifestationStatus ?? "n\xE3o manifestada"}${note.manifestationAttempts ? ` \xB7 ${note.manifestationAttempts} tentativa(s)` : ""}`
6979
7273
  }
6980
7274
  ),
6981
- /* @__PURE__ */ jsx(Field, { label: "Manifestada em", value: day3(note.manifestedAt) }),
6982
- /* @__PURE__ */ jsx(Field, { label: "Recebida em", value: day3(note.receivedAt) })
7275
+ /* @__PURE__ */ jsx(Field2, { label: "Manifestada em", value: day3(note.manifestedAt) }),
7276
+ /* @__PURE__ */ jsx(Field2, { label: "Recebida em", value: day3(note.receivedAt) })
6983
7277
  ] }),
6984
7278
  !note.supplierPartyId && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center justify-between gap-2 rounded-md border border-input bg-muted/40 px-3 py-2", children: [
6985
7279
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: note.supplierCnpj ? /* @__PURE__ */ jsx(Fragment, { children: "Este fornecedor ainda n\xE3o est\xE1 no cadastro. Ao aceitar, ele \xE9 cadastrado com o nome e o CNPJ da nota." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -7123,7 +7417,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
7123
7417
  )
7124
7418
  ] }),
7125
7419
  note.items.map((item) => {
7126
- const d = drafts[item.id] ?? draftOf(item);
7420
+ const d = drafts[item.id] ?? draftOf2(item);
7127
7421
  const factor = Number(d.conversionFactor.replace(",", ".")) || 1;
7128
7422
  const shortage = d.shortageQuantity === "" ? 0 : Number(d.shortageQuantity.replace(",", ".")) || 0;
7129
7423
  const stockQty = item.quantity * factor - shortage;
@@ -7178,14 +7472,14 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
7178
7472
  item.description
7179
7473
  ] }),
7180
7474
  /* @__PURE__ */ jsxs("dl", { className: "mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-xs sm:grid-cols-4", children: [
7181
- /* @__PURE__ */ jsx(Field, { small: true, label: "Qtd", value: `${qty(item.quantity)} ${item.unit ?? ""}` }),
7182
- /* @__PURE__ */ jsx(Field, { small: true, label: "Vl. unit.", value: money3(item.unitPrice) }),
7183
- /* @__PURE__ */ jsx(Field, { small: true, label: "Total", value: money3(item.total) }),
7184
- /* @__PURE__ */ jsx(Field, { small: true, label: "C\xF3d", value: item.supplierProductCode || "\u2014", mono: true }),
7185
- /* @__PURE__ */ jsx(Field, { small: true, label: "EAN", value: item.eanBarcode ?? "\u2014", mono: true }),
7186
- /* @__PURE__ */ jsx(Field, { small: true, label: "NCM", value: item.ncm ?? "\u2014", mono: true }),
7187
- /* @__PURE__ */ jsx(Field, { small: true, label: "CFOP", value: item.cfop ?? "\u2014", mono: true }),
7188
- /* @__PURE__ */ jsx(Field, { small: true, label: "CST/CSOSN", value: item.icmsCst ?? item.icmsCsosn ?? "\u2014", mono: true })
7475
+ /* @__PURE__ */ jsx(Field2, { small: true, label: "Qtd", value: `${qty(item.quantity)} ${item.unit ?? ""}` }),
7476
+ /* @__PURE__ */ jsx(Field2, { small: true, label: "Vl. unit.", value: money3(item.unitPrice) }),
7477
+ /* @__PURE__ */ jsx(Field2, { small: true, label: "Total", value: money3(item.total) }),
7478
+ /* @__PURE__ */ jsx(Field2, { small: true, label: "C\xF3d", value: item.supplierProductCode || "\u2014", mono: true }),
7479
+ /* @__PURE__ */ jsx(Field2, { small: true, label: "EAN", value: item.eanBarcode ?? "\u2014", mono: true }),
7480
+ /* @__PURE__ */ jsx(Field2, { small: true, label: "NCM", value: item.ncm ?? "\u2014", mono: true }),
7481
+ /* @__PURE__ */ jsx(Field2, { small: true, label: "CFOP", value: item.cfop ?? "\u2014", mono: true }),
7482
+ /* @__PURE__ */ jsx(Field2, { small: true, label: "CST/CSOSN", value: item.icmsCst ?? item.icmsCsosn ?? "\u2014", mono: true })
7189
7483
  ] })
7190
7484
  ] }),
7191
7485
  /* @__PURE__ */ jsxs("div", { className: "space-y-3 lg:border-l lg:pl-4", children: [
@@ -7529,7 +7823,7 @@ function ItemState({ done, ready }) {
7529
7823
  }
7530
7824
  return /* @__PURE__ */ jsx("span", { className: `inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${ready ? "bg-muted text-muted-foreground" : "bg-warning-soft text-warning-soft-foreground"}`, children: ready ? "A conferir" : "Falta decidir" });
7531
7825
  }
7532
- function Field({ label, value, mono, small }) {
7826
+ function Field2({ label, value, mono, small }) {
7533
7827
  return /* @__PURE__ */ jsxs("div", { children: [
7534
7828
  /* @__PURE__ */ jsx("dt", { className: `text-muted-foreground ${small ? "text-[10px]" : "text-xs"}`, children: label }),
7535
7829
  /* @__PURE__ */ jsx("dd", { className: `${mono ? "tabular-nums" : ""} ${small ? "text-xs" : "text-sm"}`, children: value })
@@ -7645,26 +7939,17 @@ var COLUMNS = [
7645
7939
  ].join(", ");
7646
7940
  var sb2 = () => getSupabaseClientOptional();
7647
7941
  async function uploadCertificate(legalEntityId, file, password) {
7648
- const buf = new Uint8Array(await file.arrayBuffer());
7649
- let bin = "";
7650
- for (let i = 0; i < buf.length; i++) bin += String.fromCharCode(buf[i]);
7651
- const { data, error } = await sb2().functions.invoke("fiscalbr-certificate", {
7652
- body: { legalEntityId, pfxBase64: btoa(bin), password, fileName: file.name, tenantId: getActiveTenantId$1() ?? null }
7653
- });
7654
- if (error) {
7655
- let body = null;
7656
- try {
7657
- body = await error?.context?.json?.();
7658
- } catch {
7659
- }
7660
- throw new Error(body?.error || error.message || "Falha ao falar com o servi\xE7o fiscal.");
7942
+ try {
7943
+ const emitter = await emitters.certificate(legalEntityId, file, password);
7944
+ return { cn: emitter.certificate?.holder ?? "", expiresAt: emitter.certificate?.expiresAt ?? "", warning: null };
7945
+ } catch (error) {
7946
+ throw new Error(emitterErrorSentence(error));
7661
7947
  }
7662
- return data;
7663
7948
  }
7664
7949
  function useEmitterSettings(legalEntityId) {
7665
- const [row, setRow] = React6.useState(null);
7666
- const [loading, setLoading] = React6.useState(true);
7667
- React6.useEffect(() => {
7950
+ const [row, setRow] = React.useState(null);
7951
+ const [loading, setLoading] = React.useState(true);
7952
+ React.useEffect(() => {
7668
7953
  if (!legalEntityId) {
7669
7954
  setLoading(false);
7670
7955
  return;
@@ -7680,7 +7965,7 @@ function useEmitterSettings(legalEntityId) {
7680
7965
  alive = false;
7681
7966
  };
7682
7967
  }, [legalEntityId]);
7683
- const save = React6.useCallback(async (tenantId, patch) => {
7968
+ const save = React.useCallback(async (tenantId, patch) => {
7684
7969
  if (!legalEntityId) return false;
7685
7970
  const payload = { ...patch, tenant_id: tenantId, legal_entity_id: legalEntityId, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
7686
7971
  const { data, error } = await sb2().from("plg_fiscalbr_emitter_settings").upsert(payload, { onConflict: "legal_entity_id" }).select(COLUMNS).single();
@@ -7722,7 +8007,7 @@ function ToggleRow({ title, description, checked, onChange, disabled, children }
7722
8007
  ] })
7723
8008
  ] });
7724
8009
  }
7725
- function Field2({ label, hint, children }) {
8010
+ function Field3({ label, hint, children }) {
7726
8011
  return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
7727
8012
  /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: label }),
7728
8013
  children,
@@ -7733,7 +8018,7 @@ function SummaryCard({ title, lines, badge, onConfigure }) {
7733
8018
  return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardHeader, { className: "flex-row items-start justify-between gap-3 space-y-0", children: [
7734
8019
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
7735
8020
  /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: title }),
7736
- /* @__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: [
8021
+ /* @__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(React.Fragment, { children: [
7737
8022
  /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: l.label }),
7738
8023
  /* @__PURE__ */ jsx("dd", { className: "text-foreground", children: l.value })
7739
8024
  ] }, l.label)) })
@@ -7754,12 +8039,12 @@ function SummaryCard({ title, lines, badge, onConfigure }) {
7754
8039
  var fmtDateTime = (v) => v ? new Date(v).toLocaleString("pt-BR") : null;
7755
8040
  function UnitCertificateWidget({ item }) {
7756
8041
  const { row, loading, save, setRow } = useEmitterSettings(item?.legalEntityId);
7757
- const [open, setOpen] = React6.useState(false);
7758
- const [file, setFile] = React6.useState(null);
7759
- const [password, setPassword] = React6.useState("");
7760
- const [sending, setSending] = React6.useState(false);
8042
+ const [open, setOpen] = React.useState(false);
8043
+ const [file, setFile] = React.useState(null);
8044
+ const [password, setPassword] = React.useState("");
8045
+ const [sending, setSending] = React.useState(false);
7761
8046
  const expiredNow = !!row?.certificate_expires_at && Date.parse(row.certificate_expires_at) < Date.now();
7762
- React6.useEffect(() => {
8047
+ React.useEffect(() => {
7763
8048
  if (expiredNow) setOpen(true);
7764
8049
  }, [expiredNow]);
7765
8050
  if (!item?.id) return null;
@@ -7829,7 +8114,7 @@ function UnitCertificateWidget({ item }) {
7829
8114
  hasCertificate && /* @__PURE__ */ jsx(Badge, { variant: expired ? "destructive" : "secondary", children: expired ? "Vencido" : "Ativo" })
7830
8115
  ] }),
7831
8116
  /* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
7832
- /* @__PURE__ */ jsx(Field2, { label: "Arquivo (.pfx)", children: /* @__PURE__ */ jsx(
8117
+ /* @__PURE__ */ jsx(Field3, { label: "Arquivo (.pfx)", children: /* @__PURE__ */ jsx(
7833
8118
  Input,
7834
8119
  {
7835
8120
  type: "file",
@@ -7838,7 +8123,7 @@ function UnitCertificateWidget({ item }) {
7838
8123
  onChange: (e) => setFile(e.target.files?.[0] ?? null)
7839
8124
  }
7840
8125
  ) }),
7841
- /* @__PURE__ */ jsx(Field2, { label: "Senha do certificado", children: /* @__PURE__ */ jsx(
8126
+ /* @__PURE__ */ jsx(Field3, { label: "Senha do certificado", children: /* @__PURE__ */ jsx(
7842
8127
  Input,
7843
8128
  {
7844
8129
  type: "password",
@@ -7859,15 +8144,15 @@ function UnitCertificateWidget({ item }) {
7859
8144
  onClick: () => {
7860
8145
  if (!file) return;
7861
8146
  setSending(true);
7862
- void uploadCertificate(item.legalEntityId, file, password).then((res) => {
8147
+ void uploadCertificate(item.legalEntityId, file, password).then(async (res) => {
7863
8148
  setRow((r) => ({
7864
8149
  ...r ?? {},
7865
8150
  certificate_cn: res.cn,
7866
8151
  certificate_expires_at: res.expiresAt,
7867
8152
  certificate_uploaded_at: (/* @__PURE__ */ new Date()).toISOString(),
7868
- dfe_sync_enabled: true,
7869
8153
  dfe_last_error: res.warning ?? null
7870
8154
  }));
8155
+ if (!row.dfe_sync_enabled) await save(item.tenantId, { dfe_sync_enabled: true });
7871
8156
  setFile(null);
7872
8157
  setPassword("");
7873
8158
  if (res.warning) toast.warning("Certificado enviado, com um aviso", { description: res.warning });
@@ -7908,12 +8193,14 @@ function EnvSelect({ value, onChange, disabled }) {
7908
8193
  ] });
7909
8194
  }
7910
8195
  function UnitFiscalWidget({ item }) {
7911
- const { row, loading, save } = useEmitterSettings(item?.legalEntityId);
7912
- const [draft, setDraft] = React6.useState({});
7913
- const [cscToken, setCscToken] = React6.useState("");
7914
- const [saving, setSaving] = React6.useState(false);
7915
- const [open, setOpen] = React6.useState(false);
7916
- React6.useEffect(() => {
8196
+ const { row, loading, save, setRow } = useEmitterSettings(item?.legalEntityId);
8197
+ const [draft, setDraft] = React.useState({});
8198
+ const [cscToken, setCscToken] = React.useState("");
8199
+ const [saving, setSaving] = React.useState(false);
8200
+ const [open, setOpen] = React.useState(false);
8201
+ const [registering, setRegistering] = React.useState(false);
8202
+ const [missing, setMissing] = React.useState([]);
8203
+ React.useEffect(() => {
7917
8204
  if (row) setDraft(row);
7918
8205
  }, [row]);
7919
8206
  if (!item?.id) return null;
@@ -7936,8 +8223,29 @@ function UnitFiscalWidget({ item }) {
7936
8223
  { ok: !!row.provider_synced_at, label: "Empresa registrada para emitir" }
7937
8224
  ];
7938
8225
  const ready = (draft.nfse_enabled || draft.nfce_enabled) && checks.every((c) => c.ok);
8226
+ const saveNfce = async () => {
8227
+ try {
8228
+ await emitters.save(item.legalEntityId, {
8229
+ nfce: {
8230
+ enabled: !!draft.nfce_enabled,
8231
+ production: !!draft.nfce_production,
8232
+ series: draft.nfce_series ? Number(draft.nfce_series) : null,
8233
+ cscId: draft.nfce_csc_id ? String(draft.nfce_csc_id).trim() : null,
8234
+ ...cscToken ? { cscToken } : {}
8235
+ }
8236
+ });
8237
+ return true;
8238
+ } catch (error) {
8239
+ toast.error("N\xE3o foi poss\xEDvel salvar a NFC-e", { description: emitterErrorSentence(error) });
8240
+ return false;
8241
+ }
8242
+ };
7939
8243
  const onSave = async () => {
7940
8244
  setSaving(true);
8245
+ if (!await saveNfce()) {
8246
+ setSaving(false);
8247
+ return;
8248
+ }
7941
8249
  const {
7942
8250
  id: _id,
7943
8251
  legal_entity_id: _le,
@@ -7947,6 +8255,10 @@ function UnitFiscalWidget({ item }) {
7947
8255
  certificate_uploaded_at: _u,
7948
8256
  dfe_last_sync_at: _d,
7949
8257
  provider_synced_at: _p,
8258
+ nfce_enabled: _ne,
8259
+ nfce_production: _np,
8260
+ nfce_series: _ns,
8261
+ nfce_csc_id: _nc,
7950
8262
  ...patch
7951
8263
  } = draft;
7952
8264
  const issRate = patch.nfse_iss_rate === "" || patch.nfse_iss_rate == null ? null : Number(String(patch.nfse_iss_rate).replace(",", "."));
@@ -7954,9 +8266,7 @@ function UnitFiscalWidget({ item }) {
7954
8266
  ...patch,
7955
8267
  nfse_iss_rate: Number.isFinite(issRate) ? issRate : null,
7956
8268
  nfse_iss_taxation: patch.nfse_iss_taxation ? Number(patch.nfse_iss_taxation) : null,
7957
- nfse_simples_regime: patch.nfse_simples_regime && patch.nfse_simples_regime !== "auto" ? Number(patch.nfse_simples_regime) : null,
7958
- nfce_series: patch.nfce_series ? Number(patch.nfce_series) : null,
7959
- ...cscToken ? { nfce_csc_token: cscToken } : {}
8269
+ nfse_simples_regime: patch.nfse_simples_regime && patch.nfse_simples_regime !== "auto" ? Number(patch.nfse_simples_regime) : null
7960
8270
  });
7961
8271
  setSaving(false);
7962
8272
  if (ok) {
@@ -7965,6 +8275,21 @@ function UnitFiscalWidget({ item }) {
7965
8275
  toast.success("Configura\xE7\xE3o fiscal salva");
7966
8276
  }
7967
8277
  };
8278
+ const onRegister = async () => {
8279
+ setRegistering(true);
8280
+ setMissing([]);
8281
+ try {
8282
+ if (!await saveNfce()) return;
8283
+ const after = await emitters.register(item.legalEntityId);
8284
+ setRow((r) => ({ ...r ?? {}, provider_synced_at: after.providerSyncedAt }));
8285
+ toast.success("Empresa cadastrada na PlugNotas");
8286
+ } catch (error) {
8287
+ if (error instanceof EmitterError) setMissing(error.gates.filter((g) => !g.ok).map((g) => GATE_LABELS[g.id] ?? g.id));
8288
+ toast.error("N\xE3o foi poss\xEDvel cadastrar na PlugNotas", { description: emitterErrorSentence(error) });
8289
+ } finally {
8290
+ setRegistering(false);
8291
+ }
8292
+ };
7968
8293
  if (!open) {
7969
8294
  const env = (prod) => prod ? "Produ\xE7\xE3o" : "Homologa\xE7\xE3o";
7970
8295
  return /* @__PURE__ */ jsx(
@@ -8012,24 +8337,24 @@ function UnitFiscalWidget({ item }) {
8012
8337
  ),
8013
8338
  draft.nfse_enabled && /* @__PURE__ */ jsxs("div", { className: "fz-container space-y-4", children: [
8014
8339
  /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
8015
- /* @__PURE__ */ jsx(Field2, { label: "C\xF3d. tributa\xE7\xE3o nacional", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_service_code_national ?? "", placeholder: "ex.: 06.02.01", onChange: (e) => set({ nfse_service_code_national: e.target.value }) }) }),
8016
- /* @__PURE__ */ jsx(Field2, { label: "C\xF3d. tributa\xE7\xE3o municipal", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_service_code_municipal ?? "", placeholder: "ex.: 001", onChange: (e) => set({ nfse_service_code_municipal: e.target.value }) }) }),
8017
- /* @__PURE__ */ jsx(Field2, { label: "C\xF3digo NBS", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_nbs_code ?? "", placeholder: "ex.: 1.2602.90.00", onChange: (e) => set({ nfse_nbs_code: e.target.value }) }) }),
8018
- /* @__PURE__ */ jsx(Field2, { label: "CNAE do servi\xE7o", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_cnae ?? "", placeholder: "ex.: 9602-5/02", onChange: (e) => set({ nfse_cnae: e.target.value }) }) }),
8019
- /* @__PURE__ */ jsx(Field2, { label: "Al\xEDquota ISS (%)", children: /* @__PURE__ */ jsx(Input, { inputMode: "decimal", value: draft.nfse_iss_rate ?? "", placeholder: "ex.: 5", onChange: (e) => set({ nfse_iss_rate: e.target.value }) }) }),
8020
- /* @__PURE__ */ jsx(Field2, { label: "Tributa\xE7\xE3o ISS", children: /* @__PURE__ */ jsxs(Select, { value: draft.nfse_iss_taxation != null ? String(draft.nfse_iss_taxation) : void 0, onValueChange: (v) => set({ nfse_iss_taxation: v }), children: [
8340
+ /* @__PURE__ */ jsx(Field3, { label: "C\xF3d. tributa\xE7\xE3o nacional", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_service_code_national ?? "", placeholder: "ex.: 06.02.01", onChange: (e) => set({ nfse_service_code_national: e.target.value }) }) }),
8341
+ /* @__PURE__ */ jsx(Field3, { label: "C\xF3d. tributa\xE7\xE3o municipal", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_service_code_municipal ?? "", placeholder: "ex.: 001", onChange: (e) => set({ nfse_service_code_municipal: e.target.value }) }) }),
8342
+ /* @__PURE__ */ jsx(Field3, { label: "C\xF3digo NBS", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_nbs_code ?? "", placeholder: "ex.: 1.2602.90.00", onChange: (e) => set({ nfse_nbs_code: e.target.value }) }) }),
8343
+ /* @__PURE__ */ jsx(Field3, { label: "CNAE do servi\xE7o", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_cnae ?? "", placeholder: "ex.: 9602-5/02", onChange: (e) => set({ nfse_cnae: e.target.value }) }) }),
8344
+ /* @__PURE__ */ jsx(Field3, { label: "Al\xEDquota ISS (%)", children: /* @__PURE__ */ jsx(Input, { inputMode: "decimal", value: draft.nfse_iss_rate ?? "", placeholder: "ex.: 5", onChange: (e) => set({ nfse_iss_rate: e.target.value }) }) }),
8345
+ /* @__PURE__ */ jsx(Field3, { label: "Tributa\xE7\xE3o ISS", children: /* @__PURE__ */ jsxs(Select, { value: draft.nfse_iss_taxation != null ? String(draft.nfse_iss_taxation) : void 0, onValueChange: (v) => set({ nfse_iss_taxation: v }), children: [
8021
8346
  /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Selecione" }) }),
8022
8347
  /* @__PURE__ */ jsx(SelectContent, { children: ISS_TAXATION.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
8023
8348
  ] }) }),
8024
- /* @__PURE__ */ jsx(Field2, { label: "IBS/CBS \u2014 CST", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_ibscbs_cst ?? "", placeholder: "ex.: 000", onChange: (e) => set({ nfse_ibscbs_cst: e.target.value }) }) }),
8025
- /* @__PURE__ */ jsx(Field2, { label: "IBS/CBS \u2014 Classifica\xE7\xE3o", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_ibscbs_classification ?? "", placeholder: "ex.: 000001", onChange: (e) => set({ nfse_ibscbs_classification: e.target.value }) }) }),
8026
- isSimples && /* @__PURE__ */ jsx(Field2, { label: "Apura\xE7\xE3o dos tributos no Simples", children: /* @__PURE__ */ jsxs(Select, { value: draft.nfse_simples_regime != null ? String(draft.nfse_simples_regime) : "auto", onValueChange: (v) => set({ nfse_simples_regime: v }), children: [
8349
+ /* @__PURE__ */ jsx(Field3, { label: "IBS/CBS \u2014 CST", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_ibscbs_cst ?? "", placeholder: "ex.: 000", onChange: (e) => set({ nfse_ibscbs_cst: e.target.value }) }) }),
8350
+ /* @__PURE__ */ jsx(Field3, { label: "IBS/CBS \u2014 Classifica\xE7\xE3o", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_ibscbs_classification ?? "", placeholder: "ex.: 000001", onChange: (e) => set({ nfse_ibscbs_classification: e.target.value }) }) }),
8351
+ isSimples && /* @__PURE__ */ jsx(Field3, { label: "Apura\xE7\xE3o dos tributos no Simples", children: /* @__PURE__ */ jsxs(Select, { value: draft.nfse_simples_regime != null ? String(draft.nfse_simples_regime) : "auto", onValueChange: (v) => set({ nfse_simples_regime: v }), children: [
8027
8352
  /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
8028
8353
  /* @__PURE__ */ jsx(SelectContent, { children: SIMPLES_REGIME.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
8029
8354
  ] }) })
8030
8355
  ] }),
8031
8356
  /* @__PURE__ */ jsx(ToggleRow, { title: "Emitir pelo valor l\xEDquido", checked: !!draft.nfse_net_value, onChange: (v) => set({ nfse_net_value: v }) }),
8032
- /* @__PURE__ */ jsx(Field2, { label: "Informa\xE7\xF5es complementares da nota", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_additional_info ?? "", placeholder: "ex.: Totais Aproximados dos Tributos cfe. Lei n\xBA 12.741/2012\u2026", onChange: (e) => set({ nfse_additional_info: e.target.value }) }) })
8357
+ /* @__PURE__ */ jsx(Field3, { label: "Informa\xE7\xF5es complementares da nota", children: /* @__PURE__ */ jsx(Input, { value: draft.nfse_additional_info ?? "", placeholder: "ex.: Totais Aproximados dos Tributos cfe. Lei n\xBA 12.741/2012\u2026", onChange: (e) => set({ nfse_additional_info: e.target.value }) }) })
8033
8358
  ] })
8034
8359
  ] }),
8035
8360
  /* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-md border p-4", children: [
@@ -8045,14 +8370,14 @@ function UnitFiscalWidget({ item }) {
8045
8370
  ),
8046
8371
  draft.nfce_enabled && /* @__PURE__ */ jsxs("div", { className: "fz-container space-y-4", children: [
8047
8372
  /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
8048
- /* @__PURE__ */ jsx(Field2, { label: "S\xE9rie", children: /* @__PURE__ */ jsx(Input, { inputMode: "numeric", value: draft.nfce_series ?? "", onChange: (e) => set({ nfce_series: e.target.value.replace(/\D/g, "") }) }) }),
8049
- /* @__PURE__ */ jsx(Field2, { label: "CSC \u2014 ID", children: /* @__PURE__ */ jsx(Input, { value: draft.nfce_csc_id ?? "", onChange: (e) => set({ nfce_csc_id: e.target.value }) }) })
8373
+ /* @__PURE__ */ jsx(Field3, { label: "S\xE9rie", children: /* @__PURE__ */ jsx(Input, { inputMode: "numeric", value: draft.nfce_series ?? "", onChange: (e) => set({ nfce_series: e.target.value.replace(/\D/g, "") }) }) }),
8374
+ /* @__PURE__ */ jsx(Field3, { label: "CSC \u2014 ID", children: /* @__PURE__ */ jsx(Input, { value: draft.nfce_csc_id ?? "", onChange: (e) => set({ nfce_csc_id: e.target.value }) }) })
8050
8375
  ] }),
8051
- /* @__PURE__ */ jsx(Field2, { label: "CSC \u2014 Token", hint: "O CSC (id num\xE9rico + token) \xE9 gerado no portal da SEFAZ do estado \u2014 n\xE3o \xE9 o login de nenhum sistema.", children: /* @__PURE__ */ jsx(Input, { type: "password", value: cscToken, placeholder: row.nfce_csc_token_set ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 (configurado \u2014 digite para trocar)" : "", onChange: (e) => setCscToken(e.target.value) }) }),
8376
+ /* @__PURE__ */ jsx(Field3, { label: "CSC \u2014 Token", hint: "O CSC (id num\xE9rico + token) \xE9 gerado no portal da SEFAZ do estado \u2014 n\xE3o \xE9 o login de nenhum sistema.", children: /* @__PURE__ */ jsx(Input, { type: "password", value: cscToken, placeholder: row.nfce_csc_token_set ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 (configurado \u2014 digite para trocar)" : "", onChange: (e) => setCscToken(e.target.value) }) }),
8052
8377
  /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
8053
- /* @__PURE__ */ jsx(Field2, { label: "Item gen\xE9rico \u2014 NCM", children: /* @__PURE__ */ jsx(Input, { value: generic.ncm ?? "", onChange: (e) => setGeneric({ ncm: e.target.value }) }) }),
8054
- /* @__PURE__ */ jsx(Field2, { label: "Item gen\xE9rico \u2014 CFOP", children: /* @__PURE__ */ jsx(Input, { value: generic.cfop ?? "", onChange: (e) => setGeneric({ cfop: e.target.value }) }) }),
8055
- /* @__PURE__ */ jsx(Field2, { label: "Item gen\xE9rico \u2014 CSOSN/CST", children: /* @__PURE__ */ jsx(Input, { value: generic.csosn ?? "", onChange: (e) => setGeneric({ csosn: e.target.value }) }) })
8378
+ /* @__PURE__ */ jsx(Field3, { label: "Item gen\xE9rico \u2014 NCM", children: /* @__PURE__ */ jsx(Input, { value: generic.ncm ?? "", onChange: (e) => setGeneric({ ncm: e.target.value }) }) }),
8379
+ /* @__PURE__ */ jsx(Field3, { label: "Item gen\xE9rico \u2014 CFOP", children: /* @__PURE__ */ jsx(Input, { value: generic.cfop ?? "", onChange: (e) => setGeneric({ cfop: e.target.value }) }) }),
8380
+ /* @__PURE__ */ jsx(Field3, { label: "Item gen\xE9rico \u2014 CSOSN/CST", children: /* @__PURE__ */ jsx(Input, { value: generic.csosn ?? "", onChange: (e) => setGeneric({ csosn: e.target.value }) }) })
8056
8381
  ] })
8057
8382
  ] })
8058
8383
  ] }),
@@ -8061,8 +8386,12 @@ function UnitFiscalWidget({ item }) {
8061
8386
  c.label
8062
8387
  ] }) }, c.label)) }),
8063
8388
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-3", children: [
8064
- /* @__PURE__ */ jsx("p", { className: "mr-auto text-xs text-muted-foreground", children: "Registrar a empresa no emissor ainda n\xE3o est\xE1 dispon\xEDvel nesta vers\xE3o \u2014 a configura\xE7\xE3o fica salva para quando estiver." }),
8065
- /* @__PURE__ */ jsx(Button, { onClick: () => void onSave(), disabled: saving, children: saving ? "Salvando\u2026" : "Salvar configura\xE7\xE3o" })
8389
+ missing.length > 0 && /* @__PURE__ */ jsxs("p", { className: "mr-auto text-xs text-destructive", children: [
8390
+ "Falta: ",
8391
+ missing.join(", ")
8392
+ ] }),
8393
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => void onRegister(), disabled: saving || registering, children: registering ? "Cadastrando\u2026" : row.provider_synced_at ? "Atualizar na PlugNotas" : "Cadastrar na PlugNotas" }),
8394
+ /* @__PURE__ */ jsx(Button, { onClick: () => void onSave(), disabled: saving || registering, children: saving ? "Salvando\u2026" : "Salvar configura\xE7\xE3o" })
8066
8395
  ] })
8067
8396
  ] })
8068
8397
  ] });
@@ -8737,9 +9066,9 @@ function inboundNotesInventoryView(options = {}) {
8737
9066
  };
8738
9067
  }
8739
9068
  function InboundNoteDocumentModal({ movement, onClose }) {
8740
- const [noteId, setNoteId] = React6.useState(null);
8741
- const [erro, setErro] = React6.useState(null);
8742
- React6.useEffect(() => {
9069
+ const [noteId, setNoteId] = React.useState(null);
9070
+ const [erro, setErro] = React.useState(null);
9071
+ React.useEffect(() => {
8743
9072
  let vivo = true;
8744
9073
  const numero = nfeNumberOf(movement);
8745
9074
  const busca = movement.operationId ? findInboxNoteByOperation(movement.operationId).then((id) => id ?? (numero ? findInboxNoteByNumber(numero) : null)) : numero ? findInboxNoteByNumber(numero) : Promise.resolve(null);
@@ -8860,77 +9189,18 @@ function createPlugNotasPlugin(options = {}) {
8860
9189
  permission: { feature: "financial", action: "read" }
8861
9190
  }
8862
9191
  ],
8863
- connectors: [createPlugNotasConnector(options)],
8864
- functions: [{
8865
- slug: "plugnotas-bridge",
8866
- ingress: "none",
8867
- verifyJwt: false,
8868
- secrets: [
8869
- "PLUGNOTAS_API_KEY",
8870
- "PLUGNOTAS_BASE_URL",
8871
- "PLUGNOTAS_ENVIRONMENT",
8872
- "PLUGNOTAS_WEBHOOK_SECRET",
8873
- "PLUGNOTAS_ARTIFACT_SIGNING_KEY",
8874
- "PLUGNOTAS_ALLOWED_ORIGINS",
8875
- "PLUGNOTAS_WORKER_SECRET",
8876
- "SUPABASE_URL",
8877
- "SUPABASE_ANON_KEY",
8878
- "SUPABASE_SERVICE_ROLE_KEY"
8879
- ],
8880
- grants: [
8881
- "plugnotas_register_emitter",
8882
- "plugnotas_disconnect_emitter",
8883
- "plugnotas_create_nfce_draft_for_order",
8884
- "plugnotas_prepare_nfce",
8885
- "plugnotas_prepare_nfce_cancel",
8886
- "plugnotas_apply_nfce_result",
8887
- "plugnotas_apply_nfce_webhook",
8888
- "plugnotas_get_artifact_locator",
8889
- "plugnotas_claim_nfce_recovery",
8890
- "plugnotas_record_nfce_attempt"
8891
- ]
8892
- }],
9192
+ // PlugNotas is reached by the integration service (control-sdk-backend
9193
+ // ADR 0008), never by an edge function of this plugin.
9194
+ connectors: [createPlugNotasConnector()],
8893
9195
  migrations: MIGRATIONS.map((entry) => ({
8894
9196
  id: `fiscal-br-${entry.id}`,
8895
9197
  version: "1.0.0",
8896
9198
  sql: entry.sql,
8897
9199
  description: "Durable PlugNotas delivery, recovery, webhook and emitter-registration boundary."
8898
- })),
8899
- diagnostics: [{
8900
- id: "plugnotas-nfce-ready",
8901
- description: "NFC-e requires the shared connection, order, item and financial fiscal-document contracts.",
8902
- requires: {
8903
- tables: [
8904
- "connections",
8905
- "orders",
8906
- "items",
8907
- "plg_financial_nfe",
8908
- "plg_fiscalbr_nfce_jobs",
8909
- "plg_fiscalbr_nfce_events",
8910
- "plg_fiscalbr_webhook_receipts"
8911
- ],
8912
- rpcs: [
8913
- "plugnotas_create_nfce_draft_for_order",
8914
- "plugnotas_prepare_nfce",
8915
- "plugnotas_apply_nfce_result",
8916
- "plugnotas_apply_nfce_webhook",
8917
- "plugnotas_claim_nfce_recovery"
8918
- ],
8919
- env: [
8920
- "PLUGNOTAS_API_KEY",
8921
- "PLUGNOTAS_BASE_URL",
8922
- "PLUGNOTAS_ENVIRONMENT",
8923
- "PLUGNOTAS_WEBHOOK_SECRET",
8924
- "PLUGNOTAS_ARTIFACT_SIGNING_KEY",
8925
- "PLUGNOTAS_ALLOWED_ORIGINS",
8926
- "PLUGNOTAS_WORKER_SECRET"
8927
- ]
8928
- },
8929
- level: "error"
8930
- }]
9200
+ }))
8931
9201
  });
8932
9202
  }
8933
9203
 
8934
- 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, listProductStockUnits, listProductsByIds, listStockLocations, listSupplierMap, markPlugNotasEmitterReady, moveNoteAttachmentsToInvoice, notesButtonState, onlyDigits, parseBoleto, parseNfeXml, paymentLabel, paymentTypeFromCode, pullNoteByKey, recipientLabel, refreshNote, rejectNote, removeNoteAttachment, saveNoteDecision, searchProducts, stageNoteFromXml, statusLabel, suggestNoteItems, summarizeNotes, syncNewNotes, toStagePayload };
9204
+ export { EVENT_LABEL, EmitterError, EmittersPanel, GATE_LABELS, InboundNoteDocumentModal, InboundNotesPage, InboundNotesView, InvoiceNotesButton, IssuedNotesView, MODEL_LABEL, NfeParseError, NoteReconcileView, PLUGNOTAS_CONNECTOR_ID, PullByKeyError, ReturnedNotesView, SPECIAL_TAX_REGIMES, STATUS_META, STOCK_BEARING_ENTRY_TYPES, TAX_REGIMES, TaxGroupsView, acceptNote, addNoteAttachment, cancelNote, createPlugNotasConnector, createPlugNotasFinancialProvider, createPlugNotasPlugin, emitterErrorSentence, emitters, 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, listProductStockUnits, listProductsByIds, listStockLocations, listSupplierMap, moveNoteAttachmentsToInvoice, notesButtonState, onlyDigits, parseBoleto, parseNfeXml, paymentLabel, paymentTypeFromCode, pullNoteByKey, recipientLabel, refreshNote, rejectNote, removeNoteAttachment, saveNoteDecision, searchProducts, stageNoteFromXml, statusLabel, suggestNoteItems, summarizeNotes, syncNewNotes, toStagePayload };
8935
9205
  //# sourceMappingURL=index.js.map
8936
9206
  //# sourceMappingURL=index.js.map