@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/connectors/plugnotas.d.ts +6 -30
- package/dist/connectors/plugnotas.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +555 -285
- package/dist/index.js.map +1 -1
- package/dist/lib/emitters.d.ts +135 -0
- package/dist/lib/emitters.d.ts.map +1 -0
- package/dist/views/EmittersPanel.d.ts +10 -0
- package/dist/views/EmittersPanel.d.ts.map +1 -0
- package/dist/views/UnitFiscalWidgets.d.ts.map +1 -1
- package/package.json +5 -5
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,
|
|
3
|
-
import
|
|
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,
|
|
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 {
|
|
7
|
-
import {
|
|
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
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
label: "
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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
|
|
112
|
-
description: "Emita NFC-e pela TecnoSpeed
|
|
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: [
|
|
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: "
|
|
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
|
-
"
|
|
138
|
-
"
|
|
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,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
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
|
-
...
|
|
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] ??
|
|
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] ??
|
|
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] ??
|
|
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] ??
|
|
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(
|
|
6973
|
-
/* @__PURE__ */ jsx(
|
|
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
|
-
|
|
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(
|
|
6982
|
-
/* @__PURE__ */ jsx(
|
|
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] ??
|
|
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(
|
|
7182
|
-
/* @__PURE__ */ jsx(
|
|
7183
|
-
/* @__PURE__ */ jsx(
|
|
7184
|
-
/* @__PURE__ */ jsx(
|
|
7185
|
-
/* @__PURE__ */ jsx(
|
|
7186
|
-
/* @__PURE__ */ jsx(
|
|
7187
|
-
/* @__PURE__ */ jsx(
|
|
7188
|
-
/* @__PURE__ */ jsx(
|
|
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
|
|
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
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
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] =
|
|
7666
|
-
const [loading, setLoading] =
|
|
7667
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
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] =
|
|
7758
|
-
const [file, setFile] =
|
|
7759
|
-
const [password, setPassword] =
|
|
7760
|
-
const [sending, setSending] =
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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] =
|
|
7913
|
-
const [cscToken, setCscToken] =
|
|
7914
|
-
const [saving, setSaving] =
|
|
7915
|
-
const [open, setOpen] =
|
|
7916
|
-
|
|
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(
|
|
8016
|
-
/* @__PURE__ */ jsx(
|
|
8017
|
-
/* @__PURE__ */ jsx(
|
|
8018
|
-
/* @__PURE__ */ jsx(
|
|
8019
|
-
/* @__PURE__ */ jsx(
|
|
8020
|
-
/* @__PURE__ */ jsx(
|
|
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(
|
|
8025
|
-
/* @__PURE__ */ jsx(
|
|
8026
|
-
isSimples && /* @__PURE__ */ jsx(
|
|
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(
|
|
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(
|
|
8049
|
-
/* @__PURE__ */ jsx(
|
|
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(
|
|
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(
|
|
8054
|
-
/* @__PURE__ */ jsx(
|
|
8055
|
-
/* @__PURE__ */ jsx(
|
|
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__ */
|
|
8065
|
-
|
|
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] =
|
|
8741
|
-
const [erro, setErro] =
|
|
8742
|
-
|
|
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
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
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,
|
|
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
|