@fayz-ai/plugin-fiscal-br 0.10.3 → 0.12.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/data/issued.d.ts +2 -2
- package/dist/data/issued.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1143 -314
- package/dist/index.js.map +1 -1
- package/dist/lib/emitters.d.ts +129 -0
- package/dist/lib/emitters.d.ts.map +1 -0
- package/dist/lib/notes.d.ts +42 -0
- package/dist/lib/notes.d.ts.map +1 -0
- package/dist/lib/service.d.ts +20 -0
- package/dist/lib/service.d.ts.map +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/views/EmittersPanel.d.ts +10 -0
- package/dist/views/EmittersPanel.d.ts.map +1 -0
- package/dist/views/OrderNoteCard.d.ts +5 -0
- package/dist/views/OrderNoteCard.d.ts.map +1 -0
- package/dist/views/UnitFiscalWidgets.d.ts.map +1 -1
- package/package.json +3 -3
- package/functions/fiscalbr-certificate/index.ts +0 -162
- package/functions/fiscalbr-plugnotas/index.ts +0 -222
package/dist/index.js
CHANGED
|
@@ -1,54 +1,439 @@
|
|
|
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, toast, Textarea, ComingSoon } 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, OrderDetailBlock, UNIT_DETAIL_ZONES, ORDER_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
|
+
}
|
|
20
|
+
};
|
|
21
|
+
async function send(method, path, body) {
|
|
22
|
+
const base = serviceBaseUrl();
|
|
23
|
+
const supabase = getSupabaseClientOptional();
|
|
24
|
+
if (!base || !supabase) throw new EmitterError(503, "no_client", "the integration service is not configured");
|
|
25
|
+
const { data } = await supabase.auth.getSession();
|
|
26
|
+
const token = data?.session?.access_token;
|
|
27
|
+
if (!token) throw new EmitterError(401, "unauthorized", "sign in again");
|
|
28
|
+
const tenant = getActiveTenantId$1();
|
|
29
|
+
try {
|
|
30
|
+
return await fetch(`${base}${path}`, {
|
|
31
|
+
method,
|
|
32
|
+
headers: {
|
|
33
|
+
authorization: `Bearer ${token}`,
|
|
34
|
+
...tenant ? { "x-fayz-tenant": tenant } : {},
|
|
35
|
+
...body === void 0 ? {} : { "content-type": "application/json" }
|
|
36
|
+
},
|
|
37
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw new EmitterError(503, "unavailable", error instanceof Error ? error.message : String(error));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function refusal(response) {
|
|
44
|
+
const text = await response.text();
|
|
45
|
+
let payload = null;
|
|
46
|
+
try {
|
|
47
|
+
payload = text ? JSON.parse(text) : null;
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
return new EmitterError(
|
|
51
|
+
response.status,
|
|
52
|
+
payload?.error ?? "unavailable",
|
|
53
|
+
payload?.message ?? text.slice(0, 200),
|
|
54
|
+
Array.isArray(payload?.gates) ? payload.gates : [],
|
|
55
|
+
typeof payload?.field === "string" ? payload.field : null
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
async function request(method, path, body) {
|
|
59
|
+
const response = await send(method, path, body);
|
|
60
|
+
if (!response.ok) throw await refusal(response);
|
|
61
|
+
const text = await response.text();
|
|
62
|
+
return text ? JSON.parse(text) : null;
|
|
63
|
+
}
|
|
64
|
+
async function requestFile(path) {
|
|
65
|
+
const response = await send("GET", path);
|
|
66
|
+
if (!response.ok) throw await refusal(response);
|
|
67
|
+
return response.blob();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/lib/emitters.ts
|
|
71
|
+
var SENTENCES = {
|
|
72
|
+
no_client: "O servi\xE7o de integra\xE7\xF5es n\xE3o est\xE1 configurado neste app.",
|
|
73
|
+
not_configured: "A emiss\xE3o fiscal ainda n\xE3o est\xE1 ligada no servidor.",
|
|
74
|
+
unauthorized: "Sua sess\xE3o expirou. Entre de novo.",
|
|
75
|
+
not_allowed: "Seu acesso n\xE3o permite alterar a configura\xE7\xE3o fiscal.",
|
|
76
|
+
unavailable: "N\xE3o foi poss\xEDvel falar com o servi\xE7o fiscal. Tente de novo em instantes.",
|
|
77
|
+
unknown_emitter: "Esta empresa n\xE3o foi encontrada nesta conta.",
|
|
78
|
+
cnpj_elsewhere: "Este CNPJ j\xE1 est\xE1 configurado para emiss\xE3o em outra conta.",
|
|
79
|
+
observed: "Esta empresa j\xE1 emite pelo sistema anterior. Ela fica s\xF3 para consulta aqui at\xE9 a migra\xE7\xE3o.",
|
|
80
|
+
not_ready: "Ainda falta preencher o que o cadastro na PlugNotas exige.",
|
|
81
|
+
no_cnpj: "A empresa n\xE3o tem um CNPJ de 14 d\xEDgitos.",
|
|
82
|
+
wrong_password: "A senha n\xE3o abre este certificado. Confira a senha do arquivo .pfx.",
|
|
83
|
+
not_a_certificate: "Este arquivo n\xE3o \xE9 um certificado A1 (.pfx ou .p12).",
|
|
84
|
+
no_certificate: "O arquivo n\xE3o traz nenhum certificado.",
|
|
85
|
+
certificate_of_another_cnpj: "Este certificado \xE9 de outro CNPJ.",
|
|
86
|
+
certificate_expired: "Este certificado est\xE1 vencido.",
|
|
87
|
+
not_base64: "N\xE3o foi poss\xEDvel ler o arquivo do certificado.",
|
|
88
|
+
invalid_setting: "Um dos valores n\xE3o \xE9 aceito.",
|
|
89
|
+
plugnotas_refused: "A PlugNotas recusou o envio.",
|
|
90
|
+
admin_only: "S\xF3 o suporte pode mudar isso."
|
|
91
|
+
};
|
|
92
|
+
var FIELD_SENTENCES = {
|
|
93
|
+
"nfce.cscId": "O ID do CSC tem s\xF3 n\xFAmeros.",
|
|
94
|
+
"nfce.series": "A s\xE9rie vai de 1 a 889.",
|
|
95
|
+
"nfe.series": "A s\xE9rie vai de 1 a 889.",
|
|
96
|
+
"nfce.nextNumber": "O pr\xF3ximo n\xFAmero precisa ser 1 ou maior.",
|
|
97
|
+
"nfe.nextNumber": "O pr\xF3ximo n\xFAmero precisa ser 1 ou maior.",
|
|
98
|
+
taxRegimeCode: "Escolha o regime tribut\xE1rio.",
|
|
99
|
+
specialTaxRegimeCode: "O regime especial vai de 0 a 11."
|
|
100
|
+
};
|
|
101
|
+
function emitterErrorSentence(error) {
|
|
102
|
+
if (error instanceof EmitterError) {
|
|
103
|
+
if (error.field && FIELD_SENTENCES[error.field]) return FIELD_SENTENCES[error.field];
|
|
104
|
+
return SENTENCES[error.code] ?? error.message;
|
|
19
105
|
}
|
|
106
|
+
return error instanceof Error ? error.message : String(error);
|
|
107
|
+
}
|
|
108
|
+
var GATE_LABELS = {
|
|
109
|
+
cnpj: "CNPJ da empresa",
|
|
110
|
+
legal_name: "Raz\xE3o social",
|
|
111
|
+
tax_regime: "Regime tribut\xE1rio",
|
|
112
|
+
email: "E-mail fiscal",
|
|
113
|
+
state_registration: "Inscri\xE7\xE3o estadual (ou ISENTO)",
|
|
114
|
+
address: "Endere\xE7o fiscal completo (rua, cidade, UF e CEP)",
|
|
115
|
+
ibge_city_code: "C\xF3digo IBGE da cidade",
|
|
116
|
+
certificate: "Certificado A1 enviado",
|
|
117
|
+
certificate_holder: "Certificado do mesmo CNPJ",
|
|
118
|
+
certificate_valid: "Certificado dentro da validade",
|
|
119
|
+
nfce_enabled: "NFC-e ligada",
|
|
120
|
+
nfce_series: "S\xE9rie da NFC-e",
|
|
121
|
+
csc: "CSC (ID e c\xF3digo) da NFC-e",
|
|
122
|
+
registered: "Empresa cadastrada na PlugNotas"
|
|
123
|
+
};
|
|
124
|
+
var one = (legalEntityId, rest = "") => `/v1/fiscal/emitters/${encodeURIComponent(legalEntityId)}${rest}`;
|
|
125
|
+
async function base64Of(file) {
|
|
126
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
127
|
+
let binary = "";
|
|
128
|
+
for (let i = 0; i < bytes.length; i += 32768) binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
129
|
+
return btoa(binary);
|
|
130
|
+
}
|
|
131
|
+
var emitters = {
|
|
132
|
+
list: async () => (await request("GET", "/v1/fiscal/emitters")).emitters,
|
|
133
|
+
/** Also asks PlugNotas what it holds, and lists what issuing still needs. */
|
|
134
|
+
read: (legalEntityId) => request("GET", one(legalEntityId)),
|
|
135
|
+
save: (legalEntityId, settings) => request("PATCH", one(legalEntityId), settings),
|
|
136
|
+
certificate: async (legalEntityId, file, password) => request("PUT", one(legalEntityId, "/certificate"), { pfxBase64: await base64Of(file), password, fileName: file.name }),
|
|
137
|
+
register: (legalEntityId) => request("PUT", one(legalEntityId, "/registration"))
|
|
138
|
+
};
|
|
139
|
+
var TAX_REGIMES = [
|
|
140
|
+
{ value: 1, label: "Simples Nacional" },
|
|
141
|
+
{ value: 2, label: "Simples Nacional \u2014 excesso" },
|
|
142
|
+
// Presumed and real profit are both "normal" in PlugNotas' words; the label
|
|
143
|
+
// says so for the store to find its own (V1).
|
|
144
|
+
{ value: 3, label: "Regime Normal (Lucro Presumido / Lucro Real)" }
|
|
20
145
|
];
|
|
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"
|
|
146
|
+
var SPECIAL_TAX_REGIMES = [
|
|
147
|
+
{ value: 0, label: "Sem regime especial" },
|
|
148
|
+
{ value: 1, label: "Microempresa municipal" },
|
|
149
|
+
{ value: 2, label: "Estimativa" },
|
|
150
|
+
{ value: 3, label: "Sociedade de profissionais" },
|
|
151
|
+
{ value: 4, label: "Cooperativa" },
|
|
152
|
+
{ value: 5, label: "MEI" },
|
|
153
|
+
{ value: 6, label: "ME ou EPP" },
|
|
154
|
+
{ value: 7, label: "Lucro Real" },
|
|
155
|
+
{ value: 8, label: "Lucro Presumido" },
|
|
156
|
+
{ value: 9, label: "Tributa\xE7\xE3o normal" },
|
|
157
|
+
{ value: 10, label: "Simples Nacional com excesso do sublimite" },
|
|
158
|
+
{ value: 11, label: "Empresa de responsabilidade limitada" }
|
|
50
159
|
];
|
|
51
|
-
|
|
160
|
+
function EmittersPanel() {
|
|
161
|
+
const [list, setList] = React.useState(null);
|
|
162
|
+
const [error, setError] = React.useState(null);
|
|
163
|
+
const [openId, setOpenId] = React.useState(null);
|
|
164
|
+
const load = React.useCallback(() => {
|
|
165
|
+
setError(null);
|
|
166
|
+
emitters.list().then((rows) => {
|
|
167
|
+
setList(rows);
|
|
168
|
+
if (rows.length === 1) setOpenId(rows[0].legalEntityId);
|
|
169
|
+
}).catch((e) => setError(emitterErrorSentence(e)));
|
|
170
|
+
}, []);
|
|
171
|
+
React.useEffect(load, [load]);
|
|
172
|
+
if (error) {
|
|
173
|
+
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col items-start gap-3 py-6", children: [
|
|
174
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: error }),
|
|
175
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: load, children: "Tentar de novo" })
|
|
176
|
+
] }) });
|
|
177
|
+
}
|
|
178
|
+
if (!list) return /* @__PURE__ */ jsx(Loading, {});
|
|
179
|
+
if (!list.length) {
|
|
180
|
+
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." }) });
|
|
181
|
+
}
|
|
182
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: list.map((e) => /* @__PURE__ */ jsx(
|
|
183
|
+
EmitterCard,
|
|
184
|
+
{
|
|
185
|
+
summary: e,
|
|
186
|
+
open: openId === e.legalEntityId,
|
|
187
|
+
onToggle: () => setOpenId((id) => id === e.legalEntityId ? null : e.legalEntityId)
|
|
188
|
+
},
|
|
189
|
+
e.legalEntityId
|
|
190
|
+
)) });
|
|
191
|
+
}
|
|
192
|
+
function Loading() {
|
|
193
|
+
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "space-y-3 py-6", children: [
|
|
194
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-48" }),
|
|
195
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" })
|
|
196
|
+
] }) });
|
|
197
|
+
}
|
|
198
|
+
function EmitterCard({ summary, open, onToggle }) {
|
|
199
|
+
const [emitter, setEmitter] = React.useState(null);
|
|
200
|
+
const [error, setError] = React.useState(null);
|
|
201
|
+
const read = React.useCallback(() => {
|
|
202
|
+
setError(null);
|
|
203
|
+
emitters.read(summary.legalEntityId).then(setEmitter).catch((e) => setError(emitterErrorSentence(e)));
|
|
204
|
+
}, [summary.legalEntityId]);
|
|
205
|
+
React.useEffect(() => {
|
|
206
|
+
if (open && !emitter) read();
|
|
207
|
+
}, [open, emitter, read]);
|
|
208
|
+
const shown = emitter ?? summary;
|
|
209
|
+
const observed = shown.mode === "observe";
|
|
210
|
+
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
211
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "flex-row items-start justify-between gap-3 space-y-0", children: [
|
|
212
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
213
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: shown.tradeName || shown.legalName || "Empresa sem nome" }),
|
|
214
|
+
/* @__PURE__ */ jsx(CardDescription, { children: [shown.legalName, shown.cnpj ? `CNPJ ${maskCpfCnpj(shown.cnpj)}` : null].filter(Boolean).join(" \xB7 ") })
|
|
215
|
+
] }),
|
|
216
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
217
|
+
/* @__PURE__ */ jsx(StatusBadge, { emitter: shown }),
|
|
218
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onToggle, children: open ? "Fechar" : "Configurar" })
|
|
219
|
+
] })
|
|
220
|
+
] }),
|
|
221
|
+
open && /* @__PURE__ */ jsxs(CardContent, { className: "space-y-5", children: [
|
|
222
|
+
error && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border border-destructive/40 p-3 text-sm", children: [
|
|
223
|
+
/* @__PURE__ */ jsx("span", { children: error }),
|
|
224
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: read, children: "Tentar de novo" })
|
|
225
|
+
] }),
|
|
226
|
+
!emitter && !error && /* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full" }),
|
|
227
|
+
emitter && observed && /* @__PURE__ */ jsx(Observed, { emitter }),
|
|
228
|
+
emitter && !observed && /* @__PURE__ */ jsx(Setup, { emitter, onChange: setEmitter })
|
|
229
|
+
] })
|
|
230
|
+
] });
|
|
231
|
+
}
|
|
232
|
+
function StatusBadge({ emitter }) {
|
|
233
|
+
if (emitter.mode === "observe") return /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: "gap-1 text-muted-foreground", children: [
|
|
234
|
+
/* @__PURE__ */ jsx(Eye, { className: "h-3 w-3" }),
|
|
235
|
+
"Emite pelo sistema anterior"
|
|
236
|
+
] });
|
|
237
|
+
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" });
|
|
238
|
+
if (emitter.certificate?.warning === "expired") return /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: "Certificado vencido" });
|
|
239
|
+
if (emitter.ready === false) return /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-muted-foreground", children: "Configura\xE7\xE3o incompleta" });
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
function Observed({ emitter }) {
|
|
243
|
+
const p = emitter.provider;
|
|
244
|
+
const cert = p?.certificate;
|
|
245
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 text-sm", children: [
|
|
246
|
+
/* @__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." }),
|
|
247
|
+
/* @__PURE__ */ jsxs("dl", { className: "grid gap-x-6 gap-y-1", style: { gridTemplateColumns: "auto 1fr" }, children: [
|
|
248
|
+
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "NFC-e" }),
|
|
249
|
+
/* @__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" }),
|
|
250
|
+
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Notas recebidas (DF-e)" }),
|
|
251
|
+
/* @__PURE__ */ jsx("dd", { children: p?.nfe?.dfe ? "Ligado" : "Desligado" }),
|
|
252
|
+
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Certificado A1" }),
|
|
253
|
+
/* @__PURE__ */ jsx("dd", { children: cert ? `${cert.expiresAt ? `v\xE1lido at\xE9 ${new Date(cert.expiresAt).toLocaleDateString("pt-BR")}` : "enviado"}` : "Nenhum" })
|
|
254
|
+
] })
|
|
255
|
+
] });
|
|
256
|
+
}
|
|
257
|
+
function Setup({ emitter, onChange }) {
|
|
258
|
+
const [draft, setDraft] = React.useState(() => draftOf(emitter));
|
|
259
|
+
const [cscToken, setCscToken] = React.useState("");
|
|
260
|
+
const [saving, setSaving] = React.useState(false);
|
|
261
|
+
const [registering, setRegistering] = React.useState(false);
|
|
262
|
+
const [missing, setMissing] = React.useState(null);
|
|
263
|
+
React.useEffect(() => setDraft(draftOf(emitter)), [emitter]);
|
|
264
|
+
const set = (patch) => setDraft((d) => ({ ...d, ...patch }));
|
|
265
|
+
const gates = missing ?? emitter.gates ?? [];
|
|
266
|
+
const save = async () => {
|
|
267
|
+
const input = {
|
|
268
|
+
taxRegimeCode: draft.taxRegimeCode,
|
|
269
|
+
specialTaxRegimeCode: draft.specialTaxRegimeCode,
|
|
270
|
+
nfce: {
|
|
271
|
+
enabled: draft.nfceEnabled,
|
|
272
|
+
production: draft.nfceProduction,
|
|
273
|
+
series: draft.nfceSeries === "" ? null : Number(draft.nfceSeries),
|
|
274
|
+
nextNumber: draft.nfceNextNumber === "" ? null : Number(draft.nfceNextNumber),
|
|
275
|
+
cscId: draft.cscId.trim() === "" ? null : draft.cscId.trim(),
|
|
276
|
+
...cscToken ? { cscToken } : {}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
setSaving(true);
|
|
280
|
+
try {
|
|
281
|
+
await emitters.save(emitter.legalEntityId, input);
|
|
282
|
+
setCscToken("");
|
|
283
|
+
const fresh = await emitters.read(emitter.legalEntityId);
|
|
284
|
+
onChange(fresh);
|
|
285
|
+
setMissing(null);
|
|
286
|
+
toast.success("Configura\xE7\xE3o fiscal salva");
|
|
287
|
+
return fresh;
|
|
288
|
+
} catch (e) {
|
|
289
|
+
toast.error("N\xE3o foi poss\xEDvel salvar", { description: emitterErrorSentence(e) });
|
|
290
|
+
return null;
|
|
291
|
+
} finally {
|
|
292
|
+
setSaving(false);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const register = async () => {
|
|
296
|
+
const saved = await save();
|
|
297
|
+
if (!saved) return;
|
|
298
|
+
setRegistering(true);
|
|
299
|
+
try {
|
|
300
|
+
const after = await emitters.register(emitter.legalEntityId);
|
|
301
|
+
onChange(after);
|
|
302
|
+
setMissing(null);
|
|
303
|
+
toast.success(after.provider?.registered ? "Empresa cadastrada na PlugNotas" : "Cadastro enviado");
|
|
304
|
+
} catch (e) {
|
|
305
|
+
if (e instanceof EmitterError && e.gates.length) setMissing(e.gates);
|
|
306
|
+
toast.error("N\xE3o foi poss\xEDvel cadastrar na PlugNotas", { description: emitterErrorSentence(e) });
|
|
307
|
+
} finally {
|
|
308
|
+
setRegistering(false);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
312
|
+
/* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-md border p-4", children: [
|
|
313
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Empresa" }),
|
|
314
|
+
/* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
|
|
315
|
+
/* @__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: [
|
|
316
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Selecione" }) }),
|
|
317
|
+
/* @__PURE__ */ jsx(SelectContent, { children: TAX_REGIMES.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: String(o.value), children: o.label }, o.value)) })
|
|
318
|
+
] }) }),
|
|
319
|
+
/* @__PURE__ */ jsx(Field, { label: "Regime tribut\xE1rio especial", children: /* @__PURE__ */ jsxs(Select, { value: String(draft.specialTaxRegimeCode), onValueChange: (v) => set({ specialTaxRegimeCode: Number(v) }), children: [
|
|
320
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
321
|
+
/* @__PURE__ */ jsx(SelectContent, { children: SPECIAL_TAX_REGIMES.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: String(o.value), children: o.label }, o.value)) })
|
|
322
|
+
] }) })
|
|
323
|
+
] }) }),
|
|
324
|
+
/* @__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." })
|
|
325
|
+
] }),
|
|
326
|
+
/* @__PURE__ */ jsx(Certificate, { emitter, onChange }),
|
|
327
|
+
/* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-md border p-4", children: [
|
|
328
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
329
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
330
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "NFC-e \u2014 Cupom do Consumidor" }),
|
|
331
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Vendas no balc\xE3o e na comanda" })
|
|
332
|
+
] }),
|
|
333
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
334
|
+
/* @__PURE__ */ jsxs(Select, { value: draft.nfceProduction ? "prod" : "hom", onValueChange: (v) => set({ nfceProduction: v === "prod" }), disabled: !draft.nfceEnabled, children: [
|
|
335
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-36", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
336
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
337
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "hom", children: "Homologa\xE7\xE3o" }),
|
|
338
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "prod", children: "Produ\xE7\xE3o" })
|
|
339
|
+
] })
|
|
340
|
+
] }),
|
|
341
|
+
/* @__PURE__ */ jsx(Switch, { checked: draft.nfceEnabled, onCheckedChange: (v) => set({ nfceEnabled: v }) })
|
|
342
|
+
] })
|
|
343
|
+
] }),
|
|
344
|
+
draft.nfceEnabled && /* @__PURE__ */ jsx("div", { className: "fz-container space-y-4", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
|
|
345
|
+
/* @__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, "") }) }) }),
|
|
346
|
+
/* @__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, "") }) }) }),
|
|
347
|
+
/* @__PURE__ */ jsx(Field, { label: "CSC \u2014 ID", children: /* @__PURE__ */ jsx(Input, { inputMode: "numeric", value: draft.cscId, onChange: (e) => set({ cscId: e.target.value }) }) }),
|
|
348
|
+
/* @__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(
|
|
349
|
+
Input,
|
|
350
|
+
{
|
|
351
|
+
type: "password",
|
|
352
|
+
autoComplete: "new-password",
|
|
353
|
+
value: cscToken,
|
|
354
|
+
placeholder: emitter.nfce.cscTokenSet ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 (configurado, digite para trocar)" : "",
|
|
355
|
+
onChange: (e) => setCscToken(e.target.value)
|
|
356
|
+
}
|
|
357
|
+
) })
|
|
358
|
+
] }) })
|
|
359
|
+
] }),
|
|
360
|
+
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: [
|
|
361
|
+
g.ok ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }),
|
|
362
|
+
GATE_LABELS[g.id] ?? g.id
|
|
363
|
+
] }) }, g.id)) }),
|
|
364
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-3", children: [
|
|
365
|
+
emitter.providerSyncedAt && /* @__PURE__ */ jsxs("p", { className: "mr-auto text-xs text-muted-foreground", children: [
|
|
366
|
+
"Cadastro na PlugNotas atualizado em ",
|
|
367
|
+
new Date(emitter.providerSyncedAt).toLocaleString("pt-BR")
|
|
368
|
+
] }),
|
|
369
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => void save(), disabled: saving || registering, children: saving && !registering ? "Salvando\u2026" : "Salvar" }),
|
|
370
|
+
/* @__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" })
|
|
371
|
+
] })
|
|
372
|
+
] });
|
|
373
|
+
}
|
|
374
|
+
function Certificate({ emitter, onChange }) {
|
|
375
|
+
const [file, setFile] = React.useState(null);
|
|
376
|
+
const [password, setPassword] = React.useState("");
|
|
377
|
+
const [sending, setSending] = React.useState(false);
|
|
378
|
+
const c = emitter.certificate;
|
|
379
|
+
const send2 = async () => {
|
|
380
|
+
if (!file) return;
|
|
381
|
+
setSending(true);
|
|
382
|
+
try {
|
|
383
|
+
await emitters.certificate(emitter.legalEntityId, file, password);
|
|
384
|
+
onChange(await emitters.read(emitter.legalEntityId));
|
|
385
|
+
setFile(null);
|
|
386
|
+
setPassword("");
|
|
387
|
+
toast.success("Certificado enviado");
|
|
388
|
+
} catch (e) {
|
|
389
|
+
toast.error("N\xE3o foi poss\xEDvel enviar o certificado", { description: emitterErrorSentence(e) });
|
|
390
|
+
} finally {
|
|
391
|
+
setSending(false);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
return /* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-md border p-4", children: [
|
|
395
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
396
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
397
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1.5 text-sm font-medium", children: [
|
|
398
|
+
/* @__PURE__ */ jsx(ShieldCheck, { className: "h-4 w-4" }),
|
|
399
|
+
" Certificado digital A1"
|
|
400
|
+
] }),
|
|
401
|
+
/* @__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" })
|
|
402
|
+
] }),
|
|
403
|
+
c && /* @__PURE__ */ jsx(Badge, { variant: c.warning === "expired" ? "destructive" : "secondary", children: c.warning === "expired" ? "Vencido" : c.warning === "expiring" ? "Vence em breve" : "Ativo" })
|
|
404
|
+
] }),
|
|
405
|
+
/* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
|
|
406
|
+
/* @__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) }) }),
|
|
407
|
+
/* @__PURE__ */ jsx(Field, { label: "Senha do certificado", children: /* @__PURE__ */ jsx(Input, { type: "password", autoComplete: "new-password", value: password, onChange: (e) => setPassword(e.target.value) }) })
|
|
408
|
+
] }) }),
|
|
409
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
410
|
+
/* @__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." }),
|
|
411
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", disabled: !file || !password || sending, onClick: () => void send2(), children: sending ? "Enviando\u2026" : c ? "Trocar certificado" : "Enviar certificado" })
|
|
412
|
+
] })
|
|
413
|
+
] });
|
|
414
|
+
}
|
|
415
|
+
function Field({ label, hint, children }) {
|
|
416
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
417
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: label }),
|
|
418
|
+
children,
|
|
419
|
+
hint && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: hint })
|
|
420
|
+
] });
|
|
421
|
+
}
|
|
422
|
+
function draftOf(e) {
|
|
423
|
+
return {
|
|
424
|
+
taxRegimeCode: e.taxRegimeCode,
|
|
425
|
+
specialTaxRegimeCode: e.specialTaxRegimeCode,
|
|
426
|
+
nfceEnabled: e.nfce.enabled,
|
|
427
|
+
nfceProduction: e.nfce.production,
|
|
428
|
+
nfceSeries: e.nfce.series ? String(e.nfce.series) : "",
|
|
429
|
+
nfceNextNumber: e.nfce.nextNumber ? String(e.nfce.nextNumber) : "",
|
|
430
|
+
cscId: e.nfce.cscId ?? ""
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// src/connectors/plugnotas.ts
|
|
435
|
+
var PLUGNOTAS_CONNECTOR_ID = "plugnotas";
|
|
436
|
+
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
437
|
function digits(value) {
|
|
53
438
|
return value.replace(/\D/g, "");
|
|
54
439
|
}
|
|
@@ -64,43 +449,8 @@ function isValidCnpj(value) {
|
|
|
64
449
|
const second = digit(`${normalized.slice(0, 12)}${first}`, [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]);
|
|
65
450
|
return normalized.endsWith(`${first}${second}`);
|
|
66
451
|
}
|
|
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();
|
|
452
|
+
var store = createConnectionStore(PLUGNOTAS_CONNECTOR_ID, {});
|
|
453
|
+
function createPlugNotasConnector() {
|
|
104
454
|
return {
|
|
105
455
|
id: PLUGNOTAS_CONNECTOR_ID,
|
|
106
456
|
// PlugBank and PlugNotas are different products from the same vendor. The
|
|
@@ -108,8 +458,8 @@ function createPlugNotasConnector(options = {}) {
|
|
|
108
458
|
// other in the generated connector map.
|
|
109
459
|
catalogSlug: "plugnotas",
|
|
110
460
|
hostPluginId: "financial",
|
|
111
|
-
name: "PlugNotas \xB7
|
|
112
|
-
description: "Emita NFC-e pela TecnoSpeed
|
|
461
|
+
name: "PlugNotas \xB7 Notas fiscais",
|
|
462
|
+
description: "Emita NFC-e pela TecnoSpeed. Certificado, CSC e cadastro da empresa ficam no servidor, nunca no navegador.",
|
|
113
463
|
icon: "ReceiptText",
|
|
114
464
|
logoUrl: PLUGNOTAS_MARK,
|
|
115
465
|
authKind: "api-key",
|
|
@@ -117,76 +467,33 @@ function createPlugNotasConnector(options = {}) {
|
|
|
117
467
|
direction: "outbound",
|
|
118
468
|
planes: ["sync"],
|
|
119
469
|
docsUrl: "https://docs.plugnotas.com.br/",
|
|
120
|
-
fields: [
|
|
121
|
-
settings: [...PLUGNOTAS_SETTINGS],
|
|
470
|
+
fields: [],
|
|
122
471
|
permissions: [{
|
|
123
472
|
kind: "data",
|
|
124
473
|
resource: "fiscal-document",
|
|
125
474
|
actions: ["read", "write"],
|
|
126
|
-
reason: "
|
|
475
|
+
reason: "Cadastrar a empresa na PlugNotas e emitir as notas das vendas.",
|
|
127
476
|
sensitive: true
|
|
128
477
|
}],
|
|
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
478
|
howItSyncs: [
|
|
137
|
-
"
|
|
138
|
-
"
|
|
479
|
+
"Cada empresa (CNPJ) \xE9 configurada na aba Emitentes: regime, certificado A1, CSC e s\xE9rie da NFC-e.",
|
|
480
|
+
"O cadastro na PlugNotas \xE9 feito pelo nosso servidor, que confere tudo o que a nota exige antes de enviar.",
|
|
481
|
+
"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
482
|
],
|
|
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 };
|
|
152
|
-
},
|
|
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
|
-
});
|
|
483
|
+
...connectionStatus(PLUGNOTAS_CONNECTOR_ID, (connection) => ({
|
|
484
|
+
connected: !!connection?.active,
|
|
485
|
+
detail: "Configure cada empresa na aba Emitentes.",
|
|
486
|
+
plane: "sync"
|
|
487
|
+
}), { store: () => store }),
|
|
488
|
+
saveConnection: async () => {
|
|
489
|
+
await store.save({ active: true });
|
|
167
490
|
},
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
491
|
+
OperatePanel: EmittersPanel,
|
|
492
|
+
operateLabel: "Emitentes",
|
|
493
|
+
disconnect: () => store.disconnect(),
|
|
494
|
+
disconnectNote: "As notas j\xE1 emitidas continuam no Financeiro, e o cadastro das empresas na PlugNotas n\xE3o \xE9 apagado."
|
|
171
495
|
};
|
|
172
496
|
}
|
|
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
497
|
|
|
191
498
|
// src/migrations/index.ts
|
|
192
499
|
var MIGRATION_000_BASELINE = `-- Fiscal Brasil / PlugNotas baseline.
|
|
@@ -4613,6 +4920,315 @@ BEGIN
|
|
|
4613
4920
|
END
|
|
4614
4921
|
$$;
|
|
4615
4922
|
`;
|
|
4923
|
+
var MIGRATION_031_THE_SERVICE_ISSUES_THE_NOTE = `-- ============================================================================
|
|
4924
|
+
-- 031 \u2014 the integration service issues the order's note
|
|
4925
|
+
--
|
|
4926
|
+
-- control-sdk-backend ADR 0008, step 2: an operator clicks "Emitir NFC-e" on
|
|
4927
|
+
-- an order and the integration service builds the note with the V1's rules,
|
|
4928
|
+
-- sends it to PlugNotas and records what comes back. The note is the record
|
|
4929
|
+
-- 005 already defined (public.documents + plg_fiscalbr_documents, linked to
|
|
4930
|
+
-- the order by plg_fiscalbr_document_orders); these are the service's doors to
|
|
4931
|
+
-- it, one function per action, the tenant explicit, open only to
|
|
4932
|
+
-- \`fayz_integration\`.
|
|
4933
|
+
--
|
|
4934
|
+
-- fiscalbr_order_for_note_for_service what the note is made of
|
|
4935
|
+
-- fiscalbr_note_open_for_service the note, opened before the call
|
|
4936
|
+
-- fiscalbr_note_apply_for_service what PlugNotas answered
|
|
4937
|
+
-- fiscalbr_notes_for_service an order's notes
|
|
4938
|
+
-- fiscalbr_note_for_service one note
|
|
4939
|
+
-- fiscalbr_notes_to_follow_for_service notes still waiting on PlugNotas
|
|
4940
|
+
--
|
|
4941
|
+
-- A note is opened BEFORE PlugNotas is called, with the idIntegracao it will
|
|
4942
|
+
-- carry, so a call whose answer is lost is found again by that id instead of
|
|
4943
|
+
-- being sent twice (the V1's FISCAL_PROVIDER_RESULT_UNCERTAIN rule).
|
|
4944
|
+
-- ============================================================================
|
|
4945
|
+
|
|
4946
|
+
-- What the note of an order is made of: the order, its lines with their tax
|
|
4947
|
+
-- data, what was paid and how, who bought and where it goes. The service
|
|
4948
|
+
-- applies the rules; this only gathers.
|
|
4949
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_order_for_note_for_service(p_tenant uuid, p_order uuid)
|
|
4950
|
+
RETURNS jsonb
|
|
4951
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER
|
|
4952
|
+
SET search_path TO ''
|
|
4953
|
+
AS $$
|
|
4954
|
+
DECLARE
|
|
4955
|
+
v_checkout jsonb;
|
|
4956
|
+
BEGIN
|
|
4957
|
+
-- The shop is optional: a site order says how it was paid on its checkout,
|
|
4958
|
+
-- read only where the shop's table exists.
|
|
4959
|
+
IF to_regclass('public.plg_shop_checkout_sessions') IS NOT NULL THEN
|
|
4960
|
+
EXECUTE $q$
|
|
4961
|
+
SELECT jsonb_build_object(
|
|
4962
|
+
'paymentMethod', s.payment_method, 'provider', s.provider, 'total', s.total, 'status', s.status,
|
|
4963
|
+
'customerName', s.customer_name, 'customerDocument', s.customer_document,
|
|
4964
|
+
'destinationState', s.shipping_address ->> 'state', 'destinationCity', s.shipping_address ->> 'city')
|
|
4965
|
+
FROM public.plg_shop_checkout_sessions s
|
|
4966
|
+
WHERE s.order_id = $1 AND s.tenant_id = $2 AND s.status = 'paid'
|
|
4967
|
+
ORDER BY s.updated_at DESC LIMIT 1 $q$
|
|
4968
|
+
INTO v_checkout USING p_order, p_tenant;
|
|
4969
|
+
END IF;
|
|
4970
|
+
|
|
4971
|
+
RETURN (SELECT jsonb_build_object(
|
|
4972
|
+
'orderId', o.id,
|
|
4973
|
+
'tenantId', o.tenant_id,
|
|
4974
|
+
'unitId', o.unit_id,
|
|
4975
|
+
'kind', o.kind,
|
|
4976
|
+
'status', o.status,
|
|
4977
|
+
'channel', o.channel,
|
|
4978
|
+
'createdAt', o.created_at,
|
|
4979
|
+
'subtotal', o.subtotal,
|
|
4980
|
+
'discount', o.discount,
|
|
4981
|
+
'shipping', o.shipping,
|
|
4982
|
+
'total', o.total,
|
|
4983
|
+
'legalEntityId', u.company_legal_entity_id,
|
|
4984
|
+
'items', coalesce((
|
|
4985
|
+
SELECT jsonb_agg(jsonb_build_object(
|
|
4986
|
+
'itemId', i.id,
|
|
4987
|
+
'productId', i.product_id,
|
|
4988
|
+
'code', coalesce(nullif(p.sku, ''), i.product_id::text, i.id::text),
|
|
4989
|
+
'name', i.name,
|
|
4990
|
+
'quantity', i.quantity,
|
|
4991
|
+
'unitPrice', i.unit_price,
|
|
4992
|
+
'discount', i.discount,
|
|
4993
|
+
'total', i.total,
|
|
4994
|
+
'cancelled', i.cancelled_at IS NOT NULL,
|
|
4995
|
+
-- The product's own value wins, field by field, over its group's (V1).
|
|
4996
|
+
'tax', CASE WHEN t.product_id IS NULL AND g.id IS NULL THEN NULL ELSE jsonb_build_object(
|
|
4997
|
+
'ncm', t.ncm,
|
|
4998
|
+
'cest', coalesce(t.cest, g.cest),
|
|
4999
|
+
'cfop', coalesce(nullif(t.cfop, ''), g.cfop),
|
|
5000
|
+
'cst', coalesce(nullif(t.cst, ''), g.cst),
|
|
5001
|
+
'csosn', g.csosn,
|
|
5002
|
+
'origin', coalesce(nullif(t.icms_origin, ''), g.icms_origin),
|
|
5003
|
+
'icmsRate', CASE WHEN coalesce(t.icms_rate, 0) > 0 THEN t.icms_rate ELSE g.icms_rate END,
|
|
5004
|
+
'icmsBaseMode', g.icms_bc_mode,
|
|
5005
|
+
'pisSituation', coalesce(nullif(t.pis_situation, ''), g.pis_situation),
|
|
5006
|
+
'pisRate', CASE WHEN coalesce(t.pis_rate, 0) > 0 THEN t.pis_rate ELSE g.pis_rate END,
|
|
5007
|
+
'cofinsSituation', coalesce(nullif(t.cofins_situation, ''), g.cofins_situation),
|
|
5008
|
+
'cofinsRate', CASE WHEN coalesce(t.cofins_rate, 0) > 0 THEN t.cofins_rate ELSE g.cofins_rate END,
|
|
5009
|
+
'ibsCbsSituation', coalesce(nullif(t.ibs_cbs_situation, ''), g.ibs_cbs_situation),
|
|
5010
|
+
'ibsCbsClassification', coalesce(nullif(t.ibs_cbs_classification, ''), g.ibs_cbs_classification),
|
|
5011
|
+
'group', g.name
|
|
5012
|
+
) END
|
|
5013
|
+
) ORDER BY i.sort_order, i.created_at)
|
|
5014
|
+
FROM public.items i
|
|
5015
|
+
LEFT JOIN public.products p ON p.id = i.product_id AND p.tenant_id = i.tenant_id
|
|
5016
|
+
LEFT JOIN public.plg_fiscalbr_product_tax_info t ON t.product_id = i.product_id AND t.tenant_id = i.tenant_id
|
|
5017
|
+
LEFT JOIN public.plg_fiscalbr_tax_groups g ON g.id = t.tax_group_id AND g.tenant_id = i.tenant_id
|
|
5018
|
+
WHERE i.order_id = o.id AND i.tenant_id = o.tenant_id
|
|
5019
|
+
), '[]'::jsonb),
|
|
5020
|
+
-- Anything that is not a site order is paid through the movements the
|
|
5021
|
+
-- order received, net of reversals, by method.
|
|
5022
|
+
'checkout', v_checkout,
|
|
5023
|
+
'payments', coalesce((
|
|
5024
|
+
SELECT jsonb_agg(jsonb_build_object('method', x.method, 'methodType', x.method_type, 'amount', x.amount))
|
|
5025
|
+
FROM (
|
|
5026
|
+
SELECT pm.name AS method, pt.name AS method_type,
|
|
5027
|
+
sum(CASE WHEN m.direction = 'credit' THEN m.paid_amount ELSE -m.paid_amount END) AS amount
|
|
5028
|
+
FROM public.plg_financial_movements m
|
|
5029
|
+
LEFT JOIN public.plg_financial_payment_methods pm ON pm.id = m.payment_method_id
|
|
5030
|
+
LEFT JOIN public.plg_financial_payment_method_types pt ON pt.id = pm.payment_method_type_id
|
|
5031
|
+
WHERE m.tenant_id = o.tenant_id AND m.movement_kind = 'payment' AND m.status = 'paid'
|
|
5032
|
+
AND (m.invoice_id = o.id OR m.financial_invoice_id IN (
|
|
5033
|
+
SELECT fi.id FROM public.plg_financial_invoices fi WHERE fi.order_id = o.id AND fi.tenant_id = o.tenant_id))
|
|
5034
|
+
GROUP BY pm.name, pt.name
|
|
5035
|
+
) x WHERE x.amount <> 0
|
|
5036
|
+
), '[]'::jsonb),
|
|
5037
|
+
'notes', coalesce((
|
|
5038
|
+
SELECT jsonb_agg(jsonb_build_object('documentId', d.document_id, 'model', d.model, 'status', d.status))
|
|
5039
|
+
FROM public.plg_fiscalbr_documents d
|
|
5040
|
+
WHERE d.tenant_id = o.tenant_id AND d.order_id = o.id
|
|
5041
|
+
), '[]'::jsonb)
|
|
5042
|
+
)
|
|
5043
|
+
FROM public.orders o
|
|
5044
|
+
LEFT JOIN app.units u ON u.id = o.unit_id AND u.tenant_id = o.tenant_id
|
|
5045
|
+
WHERE o.id = p_order AND o.tenant_id = p_tenant);
|
|
5046
|
+
END
|
|
5047
|
+
$$;
|
|
5048
|
+
|
|
5049
|
+
-- The note, opened before PlugNotas is called. One live note per order and
|
|
5050
|
+
-- model: a second "Emitir" while one is processing or authorized is refused
|
|
5051
|
+
-- (P0001 note_exists), so a double click never issues twice.
|
|
5052
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_note_open_for_service(
|
|
5053
|
+
p_tenant uuid, p_order uuid, p_model text, p_input jsonb
|
|
5054
|
+
)
|
|
5055
|
+
RETURNS jsonb
|
|
5056
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
5057
|
+
SET search_path TO ''
|
|
5058
|
+
AS $$
|
|
5059
|
+
DECLARE
|
|
5060
|
+
v_order public.orders%ROWTYPE;
|
|
5061
|
+
v_doc uuid := gen_random_uuid();
|
|
5062
|
+
v_integration text;
|
|
5063
|
+
v_existing uuid;
|
|
5064
|
+
BEGIN
|
|
5065
|
+
IF p_model NOT IN ('nfce', 'nfe') THEN
|
|
5066
|
+
RAISE EXCEPTION 'fiscalbr_note_open: model % is not issued here', p_model USING ERRCODE = '22023';
|
|
5067
|
+
END IF;
|
|
5068
|
+
SELECT * INTO v_order FROM public.orders WHERE id = p_order AND tenant_id = p_tenant FOR UPDATE;
|
|
5069
|
+
IF NOT FOUND THEN
|
|
5070
|
+
RAISE EXCEPTION 'fiscalbr_note_open: order % is not of tenant %', p_order, p_tenant USING ERRCODE = 'P0002';
|
|
5071
|
+
END IF;
|
|
5072
|
+
SELECT document_id INTO v_existing FROM public.plg_fiscalbr_documents
|
|
5073
|
+
WHERE tenant_id = p_tenant AND order_id = p_order AND model = p_model AND status IN ('processing', 'authorized')
|
|
5074
|
+
LIMIT 1;
|
|
5075
|
+
IF v_existing IS NOT NULL THEN
|
|
5076
|
+
RAISE EXCEPTION 'note_exists' USING ERRCODE = 'P0001', DETAIL = v_existing::text;
|
|
5077
|
+
END IF;
|
|
5078
|
+
|
|
5079
|
+
-- Ours, and unique per company at PlugNotas: how a lost answer is found again.
|
|
5080
|
+
v_integration := 'csb-' || v_doc::text;
|
|
5081
|
+
|
|
5082
|
+
INSERT INTO public.documents (id, tenant_id, kind, title, status, unit_id, subject_type, subject_id, created_by, metadata)
|
|
5083
|
+
VALUES (v_doc, p_tenant, 'fiscal_document', upper(p_model) || ' do pedido', 'processing', v_order.unit_id,
|
|
5084
|
+
'order', p_order, nullif(p_input ->> 'userId', '')::uuid, jsonb_build_object('source', 'integration-service'));
|
|
5085
|
+
|
|
5086
|
+
INSERT INTO public.plg_fiscalbr_documents (
|
|
5087
|
+
document_id, tenant_id, unit_id, order_id, model, environment, status, series, provider,
|
|
5088
|
+
provider_integration_id, total_value, recipient_snapshot, request_payload, source_snapshot, emitted_by_user_id, metadata)
|
|
5089
|
+
VALUES (
|
|
5090
|
+
v_doc, p_tenant, v_order.unit_id, p_order, p_model, p_input ->> 'environment', 'processing', p_input ->> 'series', 'plugnotas',
|
|
5091
|
+
v_integration, (p_input ->> 'total')::numeric, p_input -> 'recipient', p_input -> 'request', p_input -> 'source',
|
|
5092
|
+
nullif(p_input ->> 'userId', '')::uuid, jsonb_build_object('emitterCnpj', p_input ->> 'emitterCnpj'));
|
|
5093
|
+
|
|
5094
|
+
INSERT INTO public.plg_fiscalbr_document_orders (tenant_id, document_id, order_id)
|
|
5095
|
+
VALUES (p_tenant, v_doc, p_order);
|
|
5096
|
+
|
|
5097
|
+
INSERT INTO public.plg_fiscalbr_document_events (tenant_id, unit_id, document_id, event_type, status, created_by_user_id)
|
|
5098
|
+
VALUES (p_tenant, v_order.unit_id, v_doc, 'issue_requested', 'processing', nullif(p_input ->> 'userId', '')::uuid);
|
|
5099
|
+
|
|
5100
|
+
RETURN jsonb_build_object('documentId', v_doc, 'integrationId', v_integration);
|
|
5101
|
+
END
|
|
5102
|
+
$$;
|
|
5103
|
+
|
|
5104
|
+
-- What PlugNotas answered, on the note and on its public.documents row (the
|
|
5105
|
+
-- same note seen from two places, 005). Identity (model, unit, order) never moves.
|
|
5106
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_note_apply_for_service(
|
|
5107
|
+
p_tenant uuid, p_document uuid, p_patch jsonb, p_event jsonb DEFAULT NULL
|
|
5108
|
+
)
|
|
5109
|
+
RETURNS void
|
|
5110
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
5111
|
+
SET search_path TO ''
|
|
5112
|
+
AS $$
|
|
5113
|
+
DECLARE
|
|
5114
|
+
v public.plg_fiscalbr_documents%ROWTYPE;
|
|
5115
|
+
v_status text := p_patch ->> 'status';
|
|
5116
|
+
BEGIN
|
|
5117
|
+
SELECT * INTO v FROM public.plg_fiscalbr_documents
|
|
5118
|
+
WHERE document_id = p_document AND tenant_id = p_tenant FOR UPDATE;
|
|
5119
|
+
IF NOT FOUND THEN
|
|
5120
|
+
RAISE EXCEPTION 'fiscalbr_note_apply: note % is not of tenant %', p_document, p_tenant USING ERRCODE = 'P0002';
|
|
5121
|
+
END IF;
|
|
5122
|
+
IF v_status IS NOT NULL AND v_status NOT IN ('processing', 'authorized', 'rejected', 'error', 'cancelled', 'substituted') THEN
|
|
5123
|
+
RAISE EXCEPTION 'fiscalbr_note_apply: unknown status %', v_status USING ERRCODE = '22023';
|
|
5124
|
+
END IF;
|
|
5125
|
+
|
|
5126
|
+
UPDATE public.plg_fiscalbr_documents SET
|
|
5127
|
+
status = coalesce(v_status, status),
|
|
5128
|
+
number = coalesce(p_patch ->> 'number', number),
|
|
5129
|
+
series = coalesce(p_patch ->> 'series', series),
|
|
5130
|
+
access_key = coalesce(p_patch ->> 'access_key', access_key),
|
|
5131
|
+
protocol = coalesce(p_patch ->> 'protocol', protocol),
|
|
5132
|
+
provider_document_id = coalesce(p_patch ->> 'provider_document_id', provider_document_id),
|
|
5133
|
+
response_payload = coalesce(p_patch -> 'response_payload', response_payload),
|
|
5134
|
+
status_payload = coalesce(p_patch -> 'status_payload', status_payload),
|
|
5135
|
+
rejection_payload = CASE WHEN p_patch ? 'rejection_payload' THEN p_patch -> 'rejection_payload' ELSE rejection_payload END,
|
|
5136
|
+
xml = coalesce(p_patch ->> 'xml', xml),
|
|
5137
|
+
authorized_at = coalesce((p_patch ->> 'authorized_at')::timestamptz, authorized_at),
|
|
5138
|
+
last_polled_at = CASE WHEN p_patch ? 'polled' THEN now() ELSE last_polled_at END,
|
|
5139
|
+
cancelled_at = CASE WHEN p_patch ? 'cancelled_at' THEN (p_patch ->> 'cancelled_at')::timestamptz ELSE cancelled_at END,
|
|
5140
|
+
cancel_reason = CASE WHEN p_patch ? 'cancel_reason' THEN p_patch ->> 'cancel_reason' ELSE cancel_reason END,
|
|
5141
|
+
cancelled_by_user_id = CASE WHEN p_patch ? 'cancelled_by_user_id' THEN nullif(p_patch ->> 'cancelled_by_user_id', '')::uuid ELSE cancelled_by_user_id END,
|
|
5142
|
+
metadata = metadata || coalesce(p_patch -> 'metadata', '{}'::jsonb)
|
|
5143
|
+
WHERE document_id = p_document;
|
|
5144
|
+
|
|
5145
|
+
IF v_status IS NOT NULL THEN
|
|
5146
|
+
UPDATE public.documents SET status = v_status WHERE id = p_document AND status IS DISTINCT FROM v_status;
|
|
5147
|
+
END IF;
|
|
5148
|
+
|
|
5149
|
+
IF p_event IS NOT NULL THEN
|
|
5150
|
+
INSERT INTO public.plg_fiscalbr_document_events
|
|
5151
|
+
(tenant_id, unit_id, document_id, event_type, status, justification, response_payload, created_by_user_id)
|
|
5152
|
+
VALUES (v.tenant_id, v.unit_id, p_document, p_event ->> 'event_type', p_event ->> 'status',
|
|
5153
|
+
p_event ->> 'justification', p_event -> 'response_payload', nullif(p_event ->> 'userId', '')::uuid);
|
|
5154
|
+
END IF;
|
|
5155
|
+
END
|
|
5156
|
+
$$;
|
|
5157
|
+
|
|
5158
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_note_for_service(p_tenant uuid, p_document uuid)
|
|
5159
|
+
RETURNS jsonb
|
|
5160
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
5161
|
+
SET search_path TO ''
|
|
5162
|
+
AS $$
|
|
5163
|
+
SELECT jsonb_build_object(
|
|
5164
|
+
'documentId', d.document_id, 'tenantId', d.tenant_id, 'unitId', d.unit_id, 'orderId', d.order_id,
|
|
5165
|
+
'model', d.model, 'environment', d.environment, 'status', d.status, 'series', d.series, 'number', d.number,
|
|
5166
|
+
'accessKey', d.access_key, 'protocol', d.protocol, 'provider', d.provider,
|
|
5167
|
+
'providerDocumentId', d.provider_document_id, 'integrationId', d.provider_integration_id,
|
|
5168
|
+
'total', d.total_value, 'recipient', d.recipient_snapshot, 'rejection', d.rejection_payload,
|
|
5169
|
+
'hasXml', d.xml IS NOT NULL, 'authorizedAt', d.authorized_at, 'lastPolledAt', d.last_polled_at,
|
|
5170
|
+
'cancelledAt', d.cancelled_at, 'cancelReason', d.cancel_reason, 'createdAt', d.created_at,
|
|
5171
|
+
'emitterCnpj', d.metadata ->> 'emitterCnpj', 'metadata', d.metadata)
|
|
5172
|
+
FROM public.plg_fiscalbr_documents d
|
|
5173
|
+
WHERE d.document_id = p_document AND d.tenant_id = p_tenant
|
|
5174
|
+
$$;
|
|
5175
|
+
|
|
5176
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_notes_for_service(p_tenant uuid, p_order uuid)
|
|
5177
|
+
RETURNS SETOF jsonb
|
|
5178
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
5179
|
+
SET search_path TO ''
|
|
5180
|
+
AS $$
|
|
5181
|
+
SELECT public.fiscalbr_note_for_service(p_tenant, d.document_id)
|
|
5182
|
+
FROM public.plg_fiscalbr_documents d
|
|
5183
|
+
WHERE d.tenant_id = p_tenant AND d.order_id = p_order
|
|
5184
|
+
ORDER BY d.created_at DESC
|
|
5185
|
+
$$;
|
|
5186
|
+
|
|
5187
|
+
-- The XML is large and read only on download.
|
|
5188
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_note_xml_for_service(p_tenant uuid, p_document uuid)
|
|
5189
|
+
RETURNS text
|
|
5190
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
5191
|
+
SET search_path TO ''
|
|
5192
|
+
AS $$
|
|
5193
|
+
SELECT d.xml FROM public.plg_fiscalbr_documents d WHERE d.document_id = p_document AND d.tenant_id = p_tenant
|
|
5194
|
+
$$;
|
|
5195
|
+
|
|
5196
|
+
-- Notes this service sent that PlugNotas has not settled: the fallback when a
|
|
5197
|
+
-- webhook does not arrive. Oldest poll first.
|
|
5198
|
+
CREATE OR REPLACE FUNCTION public.fiscalbr_notes_to_follow_for_service(p_limit integer DEFAULT 25)
|
|
5199
|
+
RETURNS SETOF jsonb
|
|
5200
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
5201
|
+
SET search_path TO ''
|
|
5202
|
+
AS $$
|
|
5203
|
+
SELECT public.fiscalbr_note_for_service(d.tenant_id, d.document_id)
|
|
5204
|
+
FROM public.plg_fiscalbr_documents d
|
|
5205
|
+
WHERE d.provider = 'plugnotas' AND d.status = 'processing' AND d.provider_integration_id LIKE 'csb-%'
|
|
5206
|
+
AND d.created_at > now() - interval '7 days'
|
|
5207
|
+
ORDER BY coalesce(d.last_polled_at, d.created_at)
|
|
5208
|
+
LIMIT greatest(1, least(p_limit, 100))
|
|
5209
|
+
$$;
|
|
5210
|
+
|
|
5211
|
+
DO $$
|
|
5212
|
+
DECLARE
|
|
5213
|
+
f text;
|
|
5214
|
+
BEGIN
|
|
5215
|
+
FOREACH f IN ARRAY ARRAY[
|
|
5216
|
+
'public.fiscalbr_order_for_note_for_service(uuid, uuid)',
|
|
5217
|
+
'public.fiscalbr_note_open_for_service(uuid, uuid, text, jsonb)',
|
|
5218
|
+
'public.fiscalbr_note_apply_for_service(uuid, uuid, jsonb, jsonb)',
|
|
5219
|
+
'public.fiscalbr_note_for_service(uuid, uuid)',
|
|
5220
|
+
'public.fiscalbr_notes_for_service(uuid, uuid)',
|
|
5221
|
+
'public.fiscalbr_note_xml_for_service(uuid, uuid)',
|
|
5222
|
+
'public.fiscalbr_notes_to_follow_for_service(integer)'
|
|
5223
|
+
] LOOP
|
|
5224
|
+
EXECUTE format('REVOKE ALL ON FUNCTION %s FROM PUBLIC, anon, authenticated', f);
|
|
5225
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'fayz_integration') THEN
|
|
5226
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION %s TO fayz_integration', f);
|
|
5227
|
+
END IF;
|
|
5228
|
+
END LOOP;
|
|
5229
|
+
END
|
|
5230
|
+
$$;
|
|
5231
|
+
`;
|
|
4616
5232
|
var MIGRATIONS = [
|
|
4617
5233
|
{ id: "000_baseline", sql: MIGRATION_000_BASELINE },
|
|
4618
5234
|
{ id: "001_preserve_terminal_results_and_recover_leases", sql: MIGRATION_001_PRESERVE_TERMINAL_RESULTS_AND_RECOVER_LEASES },
|
|
@@ -4644,7 +5260,8 @@ var MIGRATIONS = [
|
|
|
4644
5260
|
{ id: "027_a_nota_com_conta_e_sem_estoque_termina_no_v2", sql: MIGRATION_027_A_NOTA_COM_CONTA_E_SEM_ESTOQUE_TERMINA_NO_V2 },
|
|
4645
5261
|
{ id: "028_a_linha_de_estoque_tem_unidade", sql: MIGRATION_028_A_LINHA_DE_ESTOQUE_TEM_UNIDADE },
|
|
4646
5262
|
{ id: "029_the_service_writes_the_intake_health", sql: MIGRATION_029_THE_SERVICE_WRITES_THE_INTAKE_HEALTH },
|
|
4647
|
-
{ id: "030_the_service_onboards_the_emitter", sql: MIGRATION_030_THE_SERVICE_ONBOARDS_THE_EMITTER }
|
|
5263
|
+
{ id: "030_the_service_onboards_the_emitter", sql: MIGRATION_030_THE_SERVICE_ONBOARDS_THE_EMITTER },
|
|
5264
|
+
{ id: "031_the_service_issues_the_note", sql: MIGRATION_031_THE_SERVICE_ISSUES_THE_NOTE }
|
|
4648
5265
|
];
|
|
4649
5266
|
var taxGroupEntity = {
|
|
4650
5267
|
name: "Grupo fiscal",
|
|
@@ -6455,7 +7072,7 @@ function suggestedStockUnit(item, units, productUnits, productId) {
|
|
|
6455
7072
|
const code = NOTE_UNIT_CODES[String(item.unit ?? "").trim().toUpperCase().replace(/\d+$/, "")];
|
|
6456
7073
|
return code ? units.find((u) => u.abbreviation.toLowerCase() === code)?.id ?? null : null;
|
|
6457
7074
|
}
|
|
6458
|
-
var
|
|
7075
|
+
var draftOf2 = (item) => ({
|
|
6459
7076
|
productId: item.productId,
|
|
6460
7077
|
productLabel: "",
|
|
6461
7078
|
entryType: item.entryType ?? null,
|
|
@@ -6545,7 +7162,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
6545
7162
|
setDrafts(Object.fromEntries(detail.items.map((i) => [
|
|
6546
7163
|
i.id,
|
|
6547
7164
|
{
|
|
6548
|
-
...
|
|
7165
|
+
...draftOf2(i),
|
|
6549
7166
|
productLabel: i.productId && nomes.get(i.productId) || "",
|
|
6550
7167
|
// Stocked lines keep what was recorded; open ones get a unit instead of a blank.
|
|
6551
7168
|
stockUnitId: i.stockUnitId ?? (i.stockMovementId ? null : suggestedStockUnit(i, u, unidadesDoProduto, i.productId))
|
|
@@ -6598,7 +7215,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
6598
7215
|
const buildDecisions = useCallback((rascunhos) => {
|
|
6599
7216
|
if (!note) return [];
|
|
6600
7217
|
return note.items.map((item) => {
|
|
6601
|
-
const d = rascunhos[item.id] ??
|
|
7218
|
+
const d = rascunhos[item.id] ?? draftOf2(item);
|
|
6602
7219
|
return {
|
|
6603
7220
|
id: item.id,
|
|
6604
7221
|
productId: isStockBearing(d.entryType) ? d.productId : null,
|
|
@@ -6623,7 +7240,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
6623
7240
|
}).length;
|
|
6624
7241
|
const missingType = decisions.filter((d) => !d.entryType).length;
|
|
6625
7242
|
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] ??
|
|
7243
|
+
const unverifiedFactor = note ? note.items.filter((i) => suspectFactor(i, drafts[i.id] ?? draftOf2(i))).length : 0;
|
|
6627
7244
|
const instalmentSum = instalments.reduce((acc, d) => acc + (Number(d.amount) || 0), 0);
|
|
6628
7245
|
const hasPayable = Boolean(note?.invoiceId);
|
|
6629
7246
|
const pendingStock = (note?.items ?? []).some((i) => !i.stockMovementId && (!i.entryType || isStockBearing(i.entryType)));
|
|
@@ -6652,13 +7269,13 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
6652
7269
|
}, [note, buildDecisions, defaultAccount, location, instalments]);
|
|
6653
7270
|
const pendingItems = useMemo(
|
|
6654
7271
|
() => (note?.items ?? []).filter((item) => {
|
|
6655
|
-
const d = drafts[item.id] ??
|
|
7272
|
+
const d = drafts[item.id] ?? draftOf2(item);
|
|
6656
7273
|
return !d.entryType || isStockBearing(d.entryType) && !d.productId;
|
|
6657
7274
|
}),
|
|
6658
7275
|
[note, drafts]
|
|
6659
7276
|
);
|
|
6660
7277
|
const machineItems = useMemo(
|
|
6661
|
-
() => (note?.items ?? []).filter((item) => (drafts[item.id] ??
|
|
7278
|
+
() => (note?.items ?? []).filter((item) => (drafts[item.id] ?? draftOf2(item)).suggestedBy !== null),
|
|
6662
7279
|
[note, drafts]
|
|
6663
7280
|
);
|
|
6664
7281
|
const sugerir = useCallback(async (reavaliar2 = false) => {
|
|
@@ -6969,17 +7586,17 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
6969
7586
|
/* @__PURE__ */ jsx("dt", { className: "text-xs text-muted-foreground", children: "Fornecedor" }),
|
|
6970
7587
|
/* @__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
7588
|
] }),
|
|
6972
|
-
/* @__PURE__ */ jsx(
|
|
6973
|
-
/* @__PURE__ */ jsx(
|
|
7589
|
+
/* @__PURE__ */ jsx(Field2, { label: "CNPJ", value: note.supplierCnpj ?? "\u2014", mono: true }),
|
|
7590
|
+
/* @__PURE__ */ jsx(Field2, { label: "Natureza da opera\xE7\xE3o", value: note.natureOperation ?? "\u2014" }),
|
|
6974
7591
|
/* @__PURE__ */ jsx(
|
|
6975
|
-
|
|
7592
|
+
Field2,
|
|
6976
7593
|
{
|
|
6977
7594
|
label: "Manifesta\xE7\xE3o",
|
|
6978
7595
|
value: `${note.manifestationStatus ?? "n\xE3o manifestada"}${note.manifestationAttempts ? ` \xB7 ${note.manifestationAttempts} tentativa(s)` : ""}`
|
|
6979
7596
|
}
|
|
6980
7597
|
),
|
|
6981
|
-
/* @__PURE__ */ jsx(
|
|
6982
|
-
/* @__PURE__ */ jsx(
|
|
7598
|
+
/* @__PURE__ */ jsx(Field2, { label: "Manifestada em", value: day3(note.manifestedAt) }),
|
|
7599
|
+
/* @__PURE__ */ jsx(Field2, { label: "Recebida em", value: day3(note.receivedAt) })
|
|
6983
7600
|
] }),
|
|
6984
7601
|
!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
7602
|
/* @__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 +7740,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
7123
7740
|
)
|
|
7124
7741
|
] }),
|
|
7125
7742
|
note.items.map((item) => {
|
|
7126
|
-
const d = drafts[item.id] ??
|
|
7743
|
+
const d = drafts[item.id] ?? draftOf2(item);
|
|
7127
7744
|
const factor = Number(d.conversionFactor.replace(",", ".")) || 1;
|
|
7128
7745
|
const shortage = d.shortageQuantity === "" ? 0 : Number(d.shortageQuantity.replace(",", ".")) || 0;
|
|
7129
7746
|
const stockQty = item.quantity * factor - shortage;
|
|
@@ -7178,14 +7795,14 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
|
|
|
7178
7795
|
item.description
|
|
7179
7796
|
] }),
|
|
7180
7797
|
/* @__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(
|
|
7798
|
+
/* @__PURE__ */ jsx(Field2, { small: true, label: "Qtd", value: `${qty(item.quantity)} ${item.unit ?? ""}` }),
|
|
7799
|
+
/* @__PURE__ */ jsx(Field2, { small: true, label: "Vl. unit.", value: money3(item.unitPrice) }),
|
|
7800
|
+
/* @__PURE__ */ jsx(Field2, { small: true, label: "Total", value: money3(item.total) }),
|
|
7801
|
+
/* @__PURE__ */ jsx(Field2, { small: true, label: "C\xF3d", value: item.supplierProductCode || "\u2014", mono: true }),
|
|
7802
|
+
/* @__PURE__ */ jsx(Field2, { small: true, label: "EAN", value: item.eanBarcode ?? "\u2014", mono: true }),
|
|
7803
|
+
/* @__PURE__ */ jsx(Field2, { small: true, label: "NCM", value: item.ncm ?? "\u2014", mono: true }),
|
|
7804
|
+
/* @__PURE__ */ jsx(Field2, { small: true, label: "CFOP", value: item.cfop ?? "\u2014", mono: true }),
|
|
7805
|
+
/* @__PURE__ */ jsx(Field2, { small: true, label: "CST/CSOSN", value: item.icmsCst ?? item.icmsCsosn ?? "\u2014", mono: true })
|
|
7189
7806
|
] })
|
|
7190
7807
|
] }),
|
|
7191
7808
|
/* @__PURE__ */ jsxs("div", { className: "space-y-3 lg:border-l lg:pl-4", children: [
|
|
@@ -7529,7 +8146,7 @@ function ItemState({ done, ready }) {
|
|
|
7529
8146
|
}
|
|
7530
8147
|
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
8148
|
}
|
|
7532
|
-
function
|
|
8149
|
+
function Field2({ label, value, mono, small }) {
|
|
7533
8150
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
7534
8151
|
/* @__PURE__ */ jsx("dt", { className: `text-muted-foreground ${small ? "text-[10px]" : "text-xs"}`, children: label }),
|
|
7535
8152
|
/* @__PURE__ */ jsx("dd", { className: `${mono ? "tabular-nums" : ""} ${small ? "text-xs" : "text-sm"}`, children: value })
|
|
@@ -7645,26 +8262,17 @@ var COLUMNS = [
|
|
|
7645
8262
|
].join(", ");
|
|
7646
8263
|
var sb2 = () => getSupabaseClientOptional();
|
|
7647
8264
|
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.");
|
|
8265
|
+
try {
|
|
8266
|
+
const emitter = await emitters.certificate(legalEntityId, file, password);
|
|
8267
|
+
return { cn: emitter.certificate?.holder ?? "", expiresAt: emitter.certificate?.expiresAt ?? "", warning: null };
|
|
8268
|
+
} catch (error) {
|
|
8269
|
+
throw new Error(emitterErrorSentence(error));
|
|
7661
8270
|
}
|
|
7662
|
-
return data;
|
|
7663
8271
|
}
|
|
7664
8272
|
function useEmitterSettings(legalEntityId) {
|
|
7665
|
-
const [row, setRow] =
|
|
7666
|
-
const [loading, setLoading] =
|
|
7667
|
-
|
|
8273
|
+
const [row, setRow] = React.useState(null);
|
|
8274
|
+
const [loading, setLoading] = React.useState(true);
|
|
8275
|
+
React.useEffect(() => {
|
|
7668
8276
|
if (!legalEntityId) {
|
|
7669
8277
|
setLoading(false);
|
|
7670
8278
|
return;
|
|
@@ -7680,7 +8288,7 @@ function useEmitterSettings(legalEntityId) {
|
|
|
7680
8288
|
alive = false;
|
|
7681
8289
|
};
|
|
7682
8290
|
}, [legalEntityId]);
|
|
7683
|
-
const save =
|
|
8291
|
+
const save = React.useCallback(async (tenantId, patch) => {
|
|
7684
8292
|
if (!legalEntityId) return false;
|
|
7685
8293
|
const payload = { ...patch, tenant_id: tenantId, legal_entity_id: legalEntityId, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
7686
8294
|
const { data, error } = await sb2().from("plg_fiscalbr_emitter_settings").upsert(payload, { onConflict: "legal_entity_id" }).select(COLUMNS).single();
|
|
@@ -7722,7 +8330,7 @@ function ToggleRow({ title, description, checked, onChange, disabled, children }
|
|
|
7722
8330
|
] })
|
|
7723
8331
|
] });
|
|
7724
8332
|
}
|
|
7725
|
-
function
|
|
8333
|
+
function Field3({ label, hint, children }) {
|
|
7726
8334
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
7727
8335
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: label }),
|
|
7728
8336
|
children,
|
|
@@ -7733,7 +8341,7 @@ function SummaryCard({ title, lines, badge, onConfigure }) {
|
|
|
7733
8341
|
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardHeader, { className: "flex-row items-start justify-between gap-3 space-y-0", children: [
|
|
7734
8342
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
7735
8343
|
/* @__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(
|
|
8344
|
+
/* @__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
8345
|
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: l.label }),
|
|
7738
8346
|
/* @__PURE__ */ jsx("dd", { className: "text-foreground", children: l.value })
|
|
7739
8347
|
] }, l.label)) })
|
|
@@ -7754,12 +8362,12 @@ function SummaryCard({ title, lines, badge, onConfigure }) {
|
|
|
7754
8362
|
var fmtDateTime = (v) => v ? new Date(v).toLocaleString("pt-BR") : null;
|
|
7755
8363
|
function UnitCertificateWidget({ item }) {
|
|
7756
8364
|
const { row, loading, save, setRow } = useEmitterSettings(item?.legalEntityId);
|
|
7757
|
-
const [open, setOpen] =
|
|
7758
|
-
const [file, setFile] =
|
|
7759
|
-
const [password, setPassword] =
|
|
7760
|
-
const [sending, setSending] =
|
|
8365
|
+
const [open, setOpen] = React.useState(false);
|
|
8366
|
+
const [file, setFile] = React.useState(null);
|
|
8367
|
+
const [password, setPassword] = React.useState("");
|
|
8368
|
+
const [sending, setSending] = React.useState(false);
|
|
7761
8369
|
const expiredNow = !!row?.certificate_expires_at && Date.parse(row.certificate_expires_at) < Date.now();
|
|
7762
|
-
|
|
8370
|
+
React.useEffect(() => {
|
|
7763
8371
|
if (expiredNow) setOpen(true);
|
|
7764
8372
|
}, [expiredNow]);
|
|
7765
8373
|
if (!item?.id) return null;
|
|
@@ -7829,7 +8437,7 @@ function UnitCertificateWidget({ item }) {
|
|
|
7829
8437
|
hasCertificate && /* @__PURE__ */ jsx(Badge, { variant: expired ? "destructive" : "secondary", children: expired ? "Vencido" : "Ativo" })
|
|
7830
8438
|
] }),
|
|
7831
8439
|
/* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
|
|
7832
|
-
/* @__PURE__ */ jsx(
|
|
8440
|
+
/* @__PURE__ */ jsx(Field3, { label: "Arquivo (.pfx)", children: /* @__PURE__ */ jsx(
|
|
7833
8441
|
Input,
|
|
7834
8442
|
{
|
|
7835
8443
|
type: "file",
|
|
@@ -7838,7 +8446,7 @@ function UnitCertificateWidget({ item }) {
|
|
|
7838
8446
|
onChange: (e) => setFile(e.target.files?.[0] ?? null)
|
|
7839
8447
|
}
|
|
7840
8448
|
) }),
|
|
7841
|
-
/* @__PURE__ */ jsx(
|
|
8449
|
+
/* @__PURE__ */ jsx(Field3, { label: "Senha do certificado", children: /* @__PURE__ */ jsx(
|
|
7842
8450
|
Input,
|
|
7843
8451
|
{
|
|
7844
8452
|
type: "password",
|
|
@@ -7859,15 +8467,15 @@ function UnitCertificateWidget({ item }) {
|
|
|
7859
8467
|
onClick: () => {
|
|
7860
8468
|
if (!file) return;
|
|
7861
8469
|
setSending(true);
|
|
7862
|
-
void uploadCertificate(item.legalEntityId, file, password).then((res) => {
|
|
8470
|
+
void uploadCertificate(item.legalEntityId, file, password).then(async (res) => {
|
|
7863
8471
|
setRow((r) => ({
|
|
7864
8472
|
...r ?? {},
|
|
7865
8473
|
certificate_cn: res.cn,
|
|
7866
8474
|
certificate_expires_at: res.expiresAt,
|
|
7867
8475
|
certificate_uploaded_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7868
|
-
dfe_sync_enabled: true,
|
|
7869
8476
|
dfe_last_error: res.warning ?? null
|
|
7870
8477
|
}));
|
|
8478
|
+
if (!row.dfe_sync_enabled) await save(item.tenantId, { dfe_sync_enabled: true });
|
|
7871
8479
|
setFile(null);
|
|
7872
8480
|
setPassword("");
|
|
7873
8481
|
if (res.warning) toast.warning("Certificado enviado, com um aviso", { description: res.warning });
|
|
@@ -7908,12 +8516,14 @@ function EnvSelect({ value, onChange, disabled }) {
|
|
|
7908
8516
|
] });
|
|
7909
8517
|
}
|
|
7910
8518
|
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
|
-
|
|
8519
|
+
const { row, loading, save, setRow } = useEmitterSettings(item?.legalEntityId);
|
|
8520
|
+
const [draft, setDraft] = React.useState({});
|
|
8521
|
+
const [cscToken, setCscToken] = React.useState("");
|
|
8522
|
+
const [saving, setSaving] = React.useState(false);
|
|
8523
|
+
const [open, setOpen] = React.useState(false);
|
|
8524
|
+
const [registering, setRegistering] = React.useState(false);
|
|
8525
|
+
const [missing, setMissing] = React.useState([]);
|
|
8526
|
+
React.useEffect(() => {
|
|
7917
8527
|
if (row) setDraft(row);
|
|
7918
8528
|
}, [row]);
|
|
7919
8529
|
if (!item?.id) return null;
|
|
@@ -7936,8 +8546,29 @@ function UnitFiscalWidget({ item }) {
|
|
|
7936
8546
|
{ ok: !!row.provider_synced_at, label: "Empresa registrada para emitir" }
|
|
7937
8547
|
];
|
|
7938
8548
|
const ready = (draft.nfse_enabled || draft.nfce_enabled) && checks.every((c) => c.ok);
|
|
8549
|
+
const saveNfce = async () => {
|
|
8550
|
+
try {
|
|
8551
|
+
await emitters.save(item.legalEntityId, {
|
|
8552
|
+
nfce: {
|
|
8553
|
+
enabled: !!draft.nfce_enabled,
|
|
8554
|
+
production: !!draft.nfce_production,
|
|
8555
|
+
series: draft.nfce_series ? Number(draft.nfce_series) : null,
|
|
8556
|
+
cscId: draft.nfce_csc_id ? String(draft.nfce_csc_id).trim() : null,
|
|
8557
|
+
...cscToken ? { cscToken } : {}
|
|
8558
|
+
}
|
|
8559
|
+
});
|
|
8560
|
+
return true;
|
|
8561
|
+
} catch (error) {
|
|
8562
|
+
toast.error("N\xE3o foi poss\xEDvel salvar a NFC-e", { description: emitterErrorSentence(error) });
|
|
8563
|
+
return false;
|
|
8564
|
+
}
|
|
8565
|
+
};
|
|
7939
8566
|
const onSave = async () => {
|
|
7940
8567
|
setSaving(true);
|
|
8568
|
+
if (!await saveNfce()) {
|
|
8569
|
+
setSaving(false);
|
|
8570
|
+
return;
|
|
8571
|
+
}
|
|
7941
8572
|
const {
|
|
7942
8573
|
id: _id,
|
|
7943
8574
|
legal_entity_id: _le,
|
|
@@ -7947,6 +8578,10 @@ function UnitFiscalWidget({ item }) {
|
|
|
7947
8578
|
certificate_uploaded_at: _u,
|
|
7948
8579
|
dfe_last_sync_at: _d,
|
|
7949
8580
|
provider_synced_at: _p,
|
|
8581
|
+
nfce_enabled: _ne,
|
|
8582
|
+
nfce_production: _np,
|
|
8583
|
+
nfce_series: _ns,
|
|
8584
|
+
nfce_csc_id: _nc,
|
|
7950
8585
|
...patch
|
|
7951
8586
|
} = draft;
|
|
7952
8587
|
const issRate = patch.nfse_iss_rate === "" || patch.nfse_iss_rate == null ? null : Number(String(patch.nfse_iss_rate).replace(",", "."));
|
|
@@ -7954,9 +8589,7 @@ function UnitFiscalWidget({ item }) {
|
|
|
7954
8589
|
...patch,
|
|
7955
8590
|
nfse_iss_rate: Number.isFinite(issRate) ? issRate : null,
|
|
7956
8591
|
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 } : {}
|
|
8592
|
+
nfse_simples_regime: patch.nfse_simples_regime && patch.nfse_simples_regime !== "auto" ? Number(patch.nfse_simples_regime) : null
|
|
7960
8593
|
});
|
|
7961
8594
|
setSaving(false);
|
|
7962
8595
|
if (ok) {
|
|
@@ -7965,6 +8598,21 @@ function UnitFiscalWidget({ item }) {
|
|
|
7965
8598
|
toast.success("Configura\xE7\xE3o fiscal salva");
|
|
7966
8599
|
}
|
|
7967
8600
|
};
|
|
8601
|
+
const onRegister = async () => {
|
|
8602
|
+
setRegistering(true);
|
|
8603
|
+
setMissing([]);
|
|
8604
|
+
try {
|
|
8605
|
+
if (!await saveNfce()) return;
|
|
8606
|
+
const after = await emitters.register(item.legalEntityId);
|
|
8607
|
+
setRow((r) => ({ ...r ?? {}, provider_synced_at: after.providerSyncedAt }));
|
|
8608
|
+
toast.success("Empresa cadastrada na PlugNotas");
|
|
8609
|
+
} catch (error) {
|
|
8610
|
+
if (error instanceof EmitterError) setMissing(error.gates.filter((g) => !g.ok).map((g) => GATE_LABELS[g.id] ?? g.id));
|
|
8611
|
+
toast.error("N\xE3o foi poss\xEDvel cadastrar na PlugNotas", { description: emitterErrorSentence(error) });
|
|
8612
|
+
} finally {
|
|
8613
|
+
setRegistering(false);
|
|
8614
|
+
}
|
|
8615
|
+
};
|
|
7968
8616
|
if (!open) {
|
|
7969
8617
|
const env = (prod) => prod ? "Produ\xE7\xE3o" : "Homologa\xE7\xE3o";
|
|
7970
8618
|
return /* @__PURE__ */ jsx(
|
|
@@ -8012,24 +8660,24 @@ function UnitFiscalWidget({ item }) {
|
|
|
8012
8660
|
),
|
|
8013
8661
|
draft.nfse_enabled && /* @__PURE__ */ jsxs("div", { className: "fz-container space-y-4", children: [
|
|
8014
8662
|
/* @__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(
|
|
8663
|
+
/* @__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 }) }) }),
|
|
8664
|
+
/* @__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 }) }) }),
|
|
8665
|
+
/* @__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 }) }) }),
|
|
8666
|
+
/* @__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 }) }) }),
|
|
8667
|
+
/* @__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 }) }) }),
|
|
8668
|
+
/* @__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
8669
|
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Selecione" }) }),
|
|
8022
8670
|
/* @__PURE__ */ jsx(SelectContent, { children: ISS_TAXATION.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
|
|
8023
8671
|
] }) }),
|
|
8024
|
-
/* @__PURE__ */ jsx(
|
|
8025
|
-
/* @__PURE__ */ jsx(
|
|
8026
|
-
isSimples && /* @__PURE__ */ jsx(
|
|
8672
|
+
/* @__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 }) }) }),
|
|
8673
|
+
/* @__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 }) }) }),
|
|
8674
|
+
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
8675
|
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
8028
8676
|
/* @__PURE__ */ jsx(SelectContent, { children: SIMPLES_REGIME.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
|
|
8029
8677
|
] }) })
|
|
8030
8678
|
] }),
|
|
8031
8679
|
/* @__PURE__ */ jsx(ToggleRow, { title: "Emitir pelo valor l\xEDquido", checked: !!draft.nfse_net_value, onChange: (v) => set({ nfse_net_value: v }) }),
|
|
8032
|
-
/* @__PURE__ */ jsx(
|
|
8680
|
+
/* @__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
8681
|
] })
|
|
8034
8682
|
] }),
|
|
8035
8683
|
/* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-md border p-4", children: [
|
|
@@ -8045,14 +8693,14 @@ function UnitFiscalWidget({ item }) {
|
|
|
8045
8693
|
),
|
|
8046
8694
|
draft.nfce_enabled && /* @__PURE__ */ jsxs("div", { className: "fz-container space-y-4", children: [
|
|
8047
8695
|
/* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
|
|
8048
|
-
/* @__PURE__ */ jsx(
|
|
8049
|
-
/* @__PURE__ */ jsx(
|
|
8696
|
+
/* @__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, "") }) }) }),
|
|
8697
|
+
/* @__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
8698
|
] }),
|
|
8051
|
-
/* @__PURE__ */ jsx(
|
|
8699
|
+
/* @__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
8700
|
/* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
|
|
8053
|
-
/* @__PURE__ */ jsx(
|
|
8054
|
-
/* @__PURE__ */ jsx(
|
|
8055
|
-
/* @__PURE__ */ jsx(
|
|
8701
|
+
/* @__PURE__ */ jsx(Field3, { label: "Item gen\xE9rico \u2014 NCM", children: /* @__PURE__ */ jsx(Input, { value: generic.ncm ?? "", onChange: (e) => setGeneric({ ncm: e.target.value }) }) }),
|
|
8702
|
+
/* @__PURE__ */ jsx(Field3, { label: "Item gen\xE9rico \u2014 CFOP", children: /* @__PURE__ */ jsx(Input, { value: generic.cfop ?? "", onChange: (e) => setGeneric({ cfop: e.target.value }) }) }),
|
|
8703
|
+
/* @__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
8704
|
] })
|
|
8057
8705
|
] })
|
|
8058
8706
|
] }),
|
|
@@ -8061,12 +8709,75 @@ function UnitFiscalWidget({ item }) {
|
|
|
8061
8709
|
c.label
|
|
8062
8710
|
] }) }, c.label)) }),
|
|
8063
8711
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-3", children: [
|
|
8064
|
-
/* @__PURE__ */
|
|
8065
|
-
|
|
8712
|
+
missing.length > 0 && /* @__PURE__ */ jsxs("p", { className: "mr-auto text-xs text-destructive", children: [
|
|
8713
|
+
"Falta: ",
|
|
8714
|
+
missing.join(", ")
|
|
8715
|
+
] }),
|
|
8716
|
+
/* @__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" }),
|
|
8717
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void onSave(), disabled: saving || registering, children: saving ? "Salvando\u2026" : "Salvar configura\xE7\xE3o" })
|
|
8066
8718
|
] })
|
|
8067
8719
|
] })
|
|
8068
8720
|
] });
|
|
8069
8721
|
}
|
|
8722
|
+
|
|
8723
|
+
// src/lib/notes.ts
|
|
8724
|
+
var CANCEL_MIN_JUSTIFICATION = 15;
|
|
8725
|
+
var SENTENCES2 = {
|
|
8726
|
+
no_client: "O servi\xE7o de integra\xE7\xF5es n\xE3o est\xE1 configurado neste app.",
|
|
8727
|
+
not_configured: "A emiss\xE3o fiscal ainda n\xE3o est\xE1 ligada no servidor.",
|
|
8728
|
+
unauthorized: "Sua sess\xE3o expirou. Entre de novo.",
|
|
8729
|
+
not_allowed: "Seu acesso n\xE3o permite emitir ou cancelar notas.",
|
|
8730
|
+
unavailable: "N\xE3o foi poss\xEDvel falar com o servi\xE7o fiscal. Tente de novo em instantes.",
|
|
8731
|
+
unknown_order: "Este pedido n\xE3o foi encontrado.",
|
|
8732
|
+
unknown_note: "Esta nota n\xE3o foi encontrada.",
|
|
8733
|
+
not_a_sale: "S\xF3 uma venda recebe NFC-e.",
|
|
8734
|
+
order_not_closed: "Este pedido ainda n\xE3o pode receber nota.",
|
|
8735
|
+
no_emitter: "A unidade deste pedido n\xE3o tem empresa (CNPJ) cadastrada para emitir.",
|
|
8736
|
+
emitter_not_owned: "Esta empresa ainda emite pelo sistema anterior. Configure em Integra\xE7\xF5es \u2192 PlugNotas.",
|
|
8737
|
+
emitter_not_ready: "Falta configurar a empresa para emitir NFC-e (Integra\xE7\xF5es \u2192 PlugNotas).",
|
|
8738
|
+
interstate: "Este pedido vai para outro estado: precisa de NF-e, n\xE3o de NFC-e.",
|
|
8739
|
+
no_series: "A empresa n\xE3o tem s\xE9rie de NFC-e configurada.",
|
|
8740
|
+
no_items: "O pedido n\xE3o tem produtos para a nota.",
|
|
8741
|
+
products_without_ncm: "H\xE1 produtos sem cadastro fiscal (NCM).",
|
|
8742
|
+
icms_code_mismatch: "H\xE1 produtos com c\xF3digo de ICMS que n\xE3o combina com o regime da empresa.",
|
|
8743
|
+
not_paid: "O pedido n\xE3o tem pagamento recebido para a nota.",
|
|
8744
|
+
totals_differ: "Os itens e os pagamentos do pedido n\xE3o fecham no mesmo total.",
|
|
8745
|
+
invalid_text: "Um produto tem um nome que a SEFAZ n\xE3o aceita (s\xF3 emojis ou caracteres especiais).",
|
|
8746
|
+
note_exists: "Este pedido j\xE1 tem uma nota emitida ou em processamento.",
|
|
8747
|
+
not_authorized: "S\xF3 uma nota autorizada pode ser cancelada ou baixada.",
|
|
8748
|
+
justification_too_short: `A justificativa precisa ter pelo menos ${CANCEL_MIN_JUSTIFICATION} caracteres.`,
|
|
8749
|
+
cancel_deadline_passed: "O prazo de cancelamento desta nota j\xE1 passou.",
|
|
8750
|
+
cancel_refused: "A SEFAZ recusou o cancelamento.",
|
|
8751
|
+
plugnotas_refused: "A PlugNotas recusou o envio."
|
|
8752
|
+
};
|
|
8753
|
+
function noteErrorSentence(error) {
|
|
8754
|
+
if (error instanceof EmitterError) {
|
|
8755
|
+
const base = SENTENCES2[error.code] ?? error.message;
|
|
8756
|
+
return error.code === "products_without_ncm" || error.code === "icms_code_mismatch" ? `${base} ${error.message.split(": ").slice(1).join(": ")}`.trim() : base;
|
|
8757
|
+
}
|
|
8758
|
+
return error instanceof Error ? error.message : String(error);
|
|
8759
|
+
}
|
|
8760
|
+
var order = (orderId) => `/v1/fiscal/orders/${encodeURIComponent(orderId)}/notes`;
|
|
8761
|
+
var one2 = (documentId, rest = "") => `/v1/fiscal/notes/${encodeURIComponent(documentId)}${rest}`;
|
|
8762
|
+
var orderNotes = {
|
|
8763
|
+
list: async (orderId) => (await request("GET", order(orderId))).notes,
|
|
8764
|
+
issueNfce: (orderId) => request("POST", order(orderId), { model: "nfce" }),
|
|
8765
|
+
/** Reads PlugNotas again while the note is processing. */
|
|
8766
|
+
read: (documentId) => request("GET", one2(documentId)),
|
|
8767
|
+
cancel: (documentId, justification) => request("PUT", one2(documentId, "/cancellation"), { justification }),
|
|
8768
|
+
pdf: (documentId) => requestFile(one2(documentId, "/pdf")),
|
|
8769
|
+
xml: (documentId) => requestFile(one2(documentId, "/xml"))
|
|
8770
|
+
};
|
|
8771
|
+
var NOTE_STATUS_LABEL = {
|
|
8772
|
+
processing: "Processando",
|
|
8773
|
+
authorized: "Autorizada",
|
|
8774
|
+
rejected: "Rejeitada",
|
|
8775
|
+
error: "Erro",
|
|
8776
|
+
cancelled: "Cancelada",
|
|
8777
|
+
substituted: "Substitu\xEDda"
|
|
8778
|
+
};
|
|
8779
|
+
|
|
8780
|
+
// src/data/issued.ts
|
|
8070
8781
|
function sb3() {
|
|
8071
8782
|
const supabase = getSupabaseClientOptional();
|
|
8072
8783
|
if (!supabase) throw new Error("Supabase not initialized");
|
|
@@ -8128,35 +8839,26 @@ async function listNoteEvents(documentId) {
|
|
|
8128
8839
|
createdAt: e.created_at
|
|
8129
8840
|
}));
|
|
8130
8841
|
}
|
|
8131
|
-
async function callIssuer(body) {
|
|
8132
|
-
const { data, error } = await sb3().functions.invoke("fiscalbr-plugnotas", { body: { ...body, tenantId: getActiveTenantId() } });
|
|
8133
|
-
if (error) {
|
|
8134
|
-
const ctx = error?.context;
|
|
8135
|
-
let message = error.message || "Falha ao falar com o emissor fiscal.";
|
|
8136
|
-
if (ctx && typeof ctx.json === "function") {
|
|
8137
|
-
try {
|
|
8138
|
-
const b = await ctx.json();
|
|
8139
|
-
if (b?.error) message = String(b.error);
|
|
8140
|
-
} catch {
|
|
8141
|
-
}
|
|
8142
|
-
}
|
|
8143
|
-
throw new Error(message);
|
|
8144
|
-
}
|
|
8145
|
-
if (data?.error) throw new Error(String(data.error));
|
|
8146
|
-
return data;
|
|
8147
|
-
}
|
|
8148
8842
|
async function refreshNote(documentId) {
|
|
8149
|
-
|
|
8150
|
-
|
|
8843
|
+
try {
|
|
8844
|
+
return (await orderNotes.read(documentId)).status;
|
|
8845
|
+
} catch (error) {
|
|
8846
|
+
throw new Error(noteErrorSentence(error));
|
|
8847
|
+
}
|
|
8151
8848
|
}
|
|
8152
|
-
async function cancelNote(documentId, justification
|
|
8153
|
-
|
|
8154
|
-
|
|
8849
|
+
async function cancelNote(documentId, justification) {
|
|
8850
|
+
try {
|
|
8851
|
+
return (await orderNotes.cancel(documentId, justification)).status;
|
|
8852
|
+
} catch (error) {
|
|
8853
|
+
throw new Error(noteErrorSentence(error));
|
|
8854
|
+
}
|
|
8155
8855
|
}
|
|
8156
8856
|
async function fetchNotePdf(documentId) {
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8857
|
+
try {
|
|
8858
|
+
return await orderNotes.pdf(documentId);
|
|
8859
|
+
} catch (error) {
|
|
8860
|
+
throw new Error(noteErrorSentence(error));
|
|
8861
|
+
}
|
|
8160
8862
|
}
|
|
8161
8863
|
|
|
8162
8864
|
// src/lib/issued-note.ts
|
|
@@ -8335,7 +9037,7 @@ function NoteRow({ note, canCancel, onChanged }) {
|
|
|
8335
9037
|
const [cancelling, setCancelling] = useState(false);
|
|
8336
9038
|
const [busy, setBusy] = useState(null);
|
|
8337
9039
|
const dimmed = isWithoutEffect(note.status);
|
|
8338
|
-
const
|
|
9040
|
+
const tone2 = (STATUS_META[note.status] ?? STATUS_META.error).tone;
|
|
8339
9041
|
const usable = note.status === "authorized" || note.status === "cancelled";
|
|
8340
9042
|
const inFlight = note.status === "processing" || note.status === "draft";
|
|
8341
9043
|
const cancellable = canCancel && note.status === "authorized" && !note.cancelledAt;
|
|
@@ -8383,7 +9085,7 @@ function NoteRow({ note, canCancel, onChanged }) {
|
|
|
8383
9085
|
"n\xBA ",
|
|
8384
9086
|
note.number
|
|
8385
9087
|
] }),
|
|
8386
|
-
/* @__PURE__ */ jsx(Badge, { variant: TONE_VARIANT[
|
|
9088
|
+
/* @__PURE__ */ jsx(Badge, { variant: TONE_VARIANT[tone2], children: statusLabel(note) }),
|
|
8387
9089
|
note.environment === "homologacao" && /* @__PURE__ */ jsx("span", { className: "rounded border border-destructive px-1 text-[10px] font-semibold text-destructive", children: "TESTE" }),
|
|
8388
9090
|
/* @__PURE__ */ jsx("span", { className: "ml-auto text-xs tabular-nums text-muted-foreground", children: dateTime(note.createdAt) })
|
|
8389
9091
|
] }),
|
|
@@ -8436,7 +9138,7 @@ function CancelNotePanel({ note, onClose, onDone }) {
|
|
|
8436
9138
|
setError(null);
|
|
8437
9139
|
setWorking("sending");
|
|
8438
9140
|
try {
|
|
8439
|
-
let status = await cancelNote(note.documentId, text.trim()
|
|
9141
|
+
let status = await cancelNote(note.documentId, text.trim());
|
|
8440
9142
|
setWorking("waiting");
|
|
8441
9143
|
for (const delay of POLL_DELAYS) {
|
|
8442
9144
|
if (status === "cancelled" || status === "authorized") break;
|
|
@@ -8562,6 +9264,184 @@ function NoteDetails({ note }) {
|
|
|
8562
9264
|
] })
|
|
8563
9265
|
] });
|
|
8564
9266
|
}
|
|
9267
|
+
var POLL_DELAYS2 = [3, 5, 8, 12, 20, 20];
|
|
9268
|
+
var tone = (status) => status === "authorized" ? "border-emerald-500/40 bg-emerald-500/10 text-emerald-700 dark:text-emerald-400" : status === "rejected" || status === "error" ? "border-destructive/40 bg-destructive/10 text-destructive" : "text-muted-foreground";
|
|
9269
|
+
async function openFile(blob, fileName, download) {
|
|
9270
|
+
const url = URL.createObjectURL(blob);
|
|
9271
|
+
if (download) {
|
|
9272
|
+
const a = document.createElement("a");
|
|
9273
|
+
a.href = url;
|
|
9274
|
+
a.download = fileName;
|
|
9275
|
+
a.click();
|
|
9276
|
+
} else {
|
|
9277
|
+
window.open(url, "_blank", "noopener");
|
|
9278
|
+
}
|
|
9279
|
+
setTimeout(() => URL.revokeObjectURL(url), 6e4);
|
|
9280
|
+
}
|
|
9281
|
+
function OrderNoteCard({ orderId }) {
|
|
9282
|
+
const [notes, setNotes] = React.useState(null);
|
|
9283
|
+
const [error, setError] = React.useState(null);
|
|
9284
|
+
const [issuing, setIssuing] = React.useState(false);
|
|
9285
|
+
const [cancelling, setCancelling] = React.useState(null);
|
|
9286
|
+
const load = React.useCallback(async () => {
|
|
9287
|
+
if (!orderId) return;
|
|
9288
|
+
try {
|
|
9289
|
+
setNotes(await orderNotes.list(orderId));
|
|
9290
|
+
setError(null);
|
|
9291
|
+
} catch (e) {
|
|
9292
|
+
setError(noteErrorSentence(e));
|
|
9293
|
+
}
|
|
9294
|
+
}, [orderId]);
|
|
9295
|
+
React.useEffect(() => {
|
|
9296
|
+
void load();
|
|
9297
|
+
}, [load]);
|
|
9298
|
+
const processing = notes?.find((n) => n.status === "processing" || n.cancellation === "processing");
|
|
9299
|
+
React.useEffect(() => {
|
|
9300
|
+
if (!processing) return;
|
|
9301
|
+
let alive = true;
|
|
9302
|
+
let step = 0;
|
|
9303
|
+
let timer;
|
|
9304
|
+
const tick = async () => {
|
|
9305
|
+
if (!alive || step >= POLL_DELAYS2.length) return;
|
|
9306
|
+
timer = setTimeout(async () => {
|
|
9307
|
+
step++;
|
|
9308
|
+
try {
|
|
9309
|
+
const fresh = await orderNotes.read(processing.documentId);
|
|
9310
|
+
if (!alive) return;
|
|
9311
|
+
setNotes((list) => list?.map((n) => n.documentId === fresh.documentId ? fresh : n) ?? null);
|
|
9312
|
+
if (fresh.status === "processing" || fresh.cancellation === "processing") void tick();
|
|
9313
|
+
else if (fresh.status === "authorized" && !fresh.cancellation) toast.success(`NFC-e n\xBA ${fresh.number ?? ""} autorizada`);
|
|
9314
|
+
else if (fresh.status === "rejected") toast.error("A SEFAZ rejeitou a NFC-e", { description: fresh.rejection?.message ?? void 0 });
|
|
9315
|
+
} catch {
|
|
9316
|
+
void tick();
|
|
9317
|
+
}
|
|
9318
|
+
}, POLL_DELAYS2[step] * 1e3);
|
|
9319
|
+
};
|
|
9320
|
+
void tick();
|
|
9321
|
+
return () => {
|
|
9322
|
+
alive = false;
|
|
9323
|
+
clearTimeout(timer);
|
|
9324
|
+
};
|
|
9325
|
+
}, [processing?.documentId, processing?.status, processing?.cancellation]);
|
|
9326
|
+
if (!orderId) return null;
|
|
9327
|
+
const live = notes?.find((n) => n.status === "processing" || n.status === "authorized");
|
|
9328
|
+
const issue = async () => {
|
|
9329
|
+
setIssuing(true);
|
|
9330
|
+
try {
|
|
9331
|
+
const note = await orderNotes.issueNfce(orderId);
|
|
9332
|
+
setNotes((list) => [note, ...list ?? []]);
|
|
9333
|
+
if (note.status === "rejected") toast.error("A NFC-e foi recusada", { description: note.rejection?.message ?? void 0 });
|
|
9334
|
+
else toast.success("NFC-e enviada. Aguardando a SEFAZ\u2026");
|
|
9335
|
+
} catch (e) {
|
|
9336
|
+
toast.error("N\xE3o foi poss\xEDvel emitir a NFC-e", { description: noteErrorSentence(e) });
|
|
9337
|
+
} finally {
|
|
9338
|
+
setIssuing(false);
|
|
9339
|
+
}
|
|
9340
|
+
};
|
|
9341
|
+
return /* @__PURE__ */ jsxs(
|
|
9342
|
+
OrderDetailBlock,
|
|
9343
|
+
{
|
|
9344
|
+
title: "Nota fiscal",
|
|
9345
|
+
actions: !live && notes !== null && !error ? /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => void issue(), disabled: issuing, children: [
|
|
9346
|
+
/* @__PURE__ */ jsx(FileText, { className: "mr-1.5 h-4 w-4" }),
|
|
9347
|
+
issuing ? "Emitindo\u2026" : "Emitir NFC-e"
|
|
9348
|
+
] }) : void 0,
|
|
9349
|
+
children: [
|
|
9350
|
+
error && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 text-sm", children: [
|
|
9351
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: error }),
|
|
9352
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => void load(), children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" }) })
|
|
9353
|
+
] }),
|
|
9354
|
+
!error && notes === null && /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }),
|
|
9355
|
+
!error && notes?.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Nenhuma nota emitida para este pedido." }),
|
|
9356
|
+
!error && notes && notes.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-3", children: notes.map((n) => /* @__PURE__ */ jsxs("li", { className: "space-y-1.5 text-sm", children: [
|
|
9357
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
9358
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
9359
|
+
n.model === "nfce" ? "NFC-e" : "NF-e",
|
|
9360
|
+
n.number ? ` n\xBA ${n.number}` : "",
|
|
9361
|
+
n.series ? ` \xB7 s\xE9rie ${n.series}` : ""
|
|
9362
|
+
] }),
|
|
9363
|
+
/* @__PURE__ */ jsx(Badge, { variant: "outline", className: tone(n.status), children: n.cancellation === "processing" ? "Cancelando\u2026" : n.uncertain ? "Aguardando a SEFAZ" : NOTE_STATUS_LABEL[n.status] }),
|
|
9364
|
+
n.environment === "homologation" && /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-muted-foreground", children: "Homologa\xE7\xE3o" })
|
|
9365
|
+
] }),
|
|
9366
|
+
n.accessKey && /* @__PURE__ */ jsx("p", { className: "break-all font-mono text-xs text-muted-foreground", children: n.accessKey.replace(/(\d{4})(?=\d)/g, "$1 ") }),
|
|
9367
|
+
(n.status === "rejected" || n.status === "error") && n.rejection?.message && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: n.rejection.message }),
|
|
9368
|
+
n.status === "cancelled" && n.cancelReason && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
9369
|
+
"Cancelada: ",
|
|
9370
|
+
n.cancelReason
|
|
9371
|
+
] }),
|
|
9372
|
+
(n.status === "authorized" || n.status === "cancelled") && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
9373
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => void orderNotes.pdf(n.documentId).then((b) => openFile(b, `nfce-${n.number ?? n.documentId}.pdf`, false)).catch((e) => toast.error("N\xE3o foi poss\xEDvel abrir o DANFE", { description: noteErrorSentence(e) })), children: [
|
|
9374
|
+
/* @__PURE__ */ jsx(FileText, { className: "mr-1.5 h-4 w-4" }),
|
|
9375
|
+
"DANFE"
|
|
9376
|
+
] }),
|
|
9377
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => void orderNotes.xml(n.documentId).then((b) => openFile(b, `nfce-${n.number ?? n.documentId}.xml`, true)).catch((e) => toast.error("N\xE3o foi poss\xEDvel baixar o XML", { description: noteErrorSentence(e) })), children: [
|
|
9378
|
+
/* @__PURE__ */ jsx(Download, { className: "mr-1.5 h-4 w-4" }),
|
|
9379
|
+
"XML"
|
|
9380
|
+
] }),
|
|
9381
|
+
n.status === "authorized" && !n.cancellation && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", className: "text-destructive", onClick: () => setCancelling(n), children: "Cancelar nota" })
|
|
9382
|
+
] })
|
|
9383
|
+
] }, n.documentId)) }),
|
|
9384
|
+
cancelling && /* @__PURE__ */ jsx(
|
|
9385
|
+
CancelDialog,
|
|
9386
|
+
{
|
|
9387
|
+
note: cancelling,
|
|
9388
|
+
onClose: () => setCancelling(null),
|
|
9389
|
+
onDone: (fresh) => {
|
|
9390
|
+
setNotes((list) => list?.map((x) => x.documentId === fresh.documentId ? fresh : x) ?? null);
|
|
9391
|
+
setCancelling(null);
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
)
|
|
9395
|
+
]
|
|
9396
|
+
}
|
|
9397
|
+
);
|
|
9398
|
+
}
|
|
9399
|
+
function CancelDialog({ note, onClose, onDone }) {
|
|
9400
|
+
const [text, setText] = React.useState("");
|
|
9401
|
+
const [sending, setSending] = React.useState(false);
|
|
9402
|
+
const ok = text.trim().length >= CANCEL_MIN_JUSTIFICATION;
|
|
9403
|
+
const send2 = async () => {
|
|
9404
|
+
setSending(true);
|
|
9405
|
+
try {
|
|
9406
|
+
const fresh = await orderNotes.cancel(note.documentId, text.trim());
|
|
9407
|
+
toast.success("Cancelamento enviado. Aguardando a SEFAZ\u2026");
|
|
9408
|
+
onDone(fresh);
|
|
9409
|
+
} catch (e) {
|
|
9410
|
+
toast.error("N\xE3o foi poss\xEDvel cancelar a nota", { description: noteErrorSentence(e) });
|
|
9411
|
+
} finally {
|
|
9412
|
+
setSending(false);
|
|
9413
|
+
}
|
|
9414
|
+
};
|
|
9415
|
+
return /* @__PURE__ */ jsx(Modal, { open: true, onOpenChange: (open) => {
|
|
9416
|
+
if (!open) onClose();
|
|
9417
|
+
}, children: /* @__PURE__ */ jsxs(ModalContent, { children: [
|
|
9418
|
+
/* @__PURE__ */ jsxs(ModalHeader, { children: [
|
|
9419
|
+
/* @__PURE__ */ jsxs(ModalTitle, { children: [
|
|
9420
|
+
"Cancelar NFC-e",
|
|
9421
|
+
note.number ? ` n\xBA ${note.number}` : ""
|
|
9422
|
+
] }),
|
|
9423
|
+
/* @__PURE__ */ jsx(ModalDescription, { children: "A SEFAZ s\xF3 aceita o cancelamento de uma NFC-e logo depois da emiss\xE3o (em geral, 30 minutos). Para emitir uma NF-e no lugar, cancele primeiro esta nota." })
|
|
9424
|
+
] }),
|
|
9425
|
+
/* @__PURE__ */ jsx(
|
|
9426
|
+
Textarea,
|
|
9427
|
+
{
|
|
9428
|
+
value: text,
|
|
9429
|
+
onChange: (e) => setText(e.target.value),
|
|
9430
|
+
placeholder: "Ex.: Emiss\xE3o em duplicidade por erro operacional",
|
|
9431
|
+
rows: 3
|
|
9432
|
+
}
|
|
9433
|
+
),
|
|
9434
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
9435
|
+
"Pelo menos ",
|
|
9436
|
+
CANCEL_MIN_JUSTIFICATION,
|
|
9437
|
+
" caracteres."
|
|
9438
|
+
] }),
|
|
9439
|
+
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
9440
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, disabled: sending, children: "Voltar" }),
|
|
9441
|
+
/* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: () => void send2(), disabled: !ok || sending, children: sending ? "Cancelando\u2026" : "Cancelar nota" })
|
|
9442
|
+
] })
|
|
9443
|
+
] }) });
|
|
9444
|
+
}
|
|
8565
9445
|
|
|
8566
9446
|
// src/lib/financial-provider.ts
|
|
8567
9447
|
function fiscalReader(provider) {
|
|
@@ -8737,9 +9617,9 @@ function inboundNotesInventoryView(options = {}) {
|
|
|
8737
9617
|
};
|
|
8738
9618
|
}
|
|
8739
9619
|
function InboundNoteDocumentModal({ movement, onClose }) {
|
|
8740
|
-
const [noteId, setNoteId] =
|
|
8741
|
-
const [erro, setErro] =
|
|
8742
|
-
|
|
9620
|
+
const [noteId, setNoteId] = React.useState(null);
|
|
9621
|
+
const [erro, setErro] = React.useState(null);
|
|
9622
|
+
React.useEffect(() => {
|
|
8743
9623
|
let vivo = true;
|
|
8744
9624
|
const numero = nfeNumberOf(movement);
|
|
8745
9625
|
const busca = movement.operationId ? findInboxNoteByOperation(movement.operationId).then((id) => id ?? (numero ? findInboxNoteByNumber(numero) : null)) : numero ? findInboxNoteByNumber(numero) : Promise.resolve(null);
|
|
@@ -8852,6 +9732,14 @@ function createPlugNotasPlugin(options = {}) {
|
|
|
8852
9732
|
order: 0,
|
|
8853
9733
|
permission: { feature: "financial", action: "read" }
|
|
8854
9734
|
},
|
|
9735
|
+
{
|
|
9736
|
+
// Issue, follow and cancel the order's NFC-e (control-sdk-backend ADR 0008).
|
|
9737
|
+
id: "fiscal-br-order-note",
|
|
9738
|
+
zone: ORDER_DETAIL_ZONES.fiscal,
|
|
9739
|
+
component: OrderNoteCard,
|
|
9740
|
+
order: 0,
|
|
9741
|
+
permission: { feature: "financial", action: "read" }
|
|
9742
|
+
},
|
|
8855
9743
|
{
|
|
8856
9744
|
id: "fiscal-br-unit-fiscal",
|
|
8857
9745
|
zone: UNIT_DETAIL_ZONES.fiscal,
|
|
@@ -8860,77 +9748,18 @@ function createPlugNotasPlugin(options = {}) {
|
|
|
8860
9748
|
permission: { feature: "financial", action: "read" }
|
|
8861
9749
|
}
|
|
8862
9750
|
],
|
|
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
|
-
}],
|
|
9751
|
+
// PlugNotas is reached by the integration service (control-sdk-backend
|
|
9752
|
+
// ADR 0008), never by an edge function of this plugin.
|
|
9753
|
+
connectors: [createPlugNotasConnector()],
|
|
8893
9754
|
migrations: MIGRATIONS.map((entry) => ({
|
|
8894
9755
|
id: `fiscal-br-${entry.id}`,
|
|
8895
9756
|
version: "1.0.0",
|
|
8896
9757
|
sql: entry.sql,
|
|
8897
9758
|
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
|
-
}]
|
|
9759
|
+
}))
|
|
8931
9760
|
});
|
|
8932
9761
|
}
|
|
8933
9762
|
|
|
8934
|
-
export { EVENT_LABEL, InboundNoteDocumentModal, InboundNotesPage, InboundNotesView, InvoiceNotesButton, IssuedNotesView, MODEL_LABEL, NfeParseError, NoteReconcileView,
|
|
9763
|
+
export { CANCEL_MIN_JUSTIFICATION, EVENT_LABEL, EmitterError, EmittersPanel, GATE_LABELS, InboundNoteDocumentModal, InboundNotesPage, InboundNotesView, InvoiceNotesButton, IssuedNotesView, MODEL_LABEL, NOTE_STATUS_LABEL, NfeParseError, NoteReconcileView, OrderNoteCard, 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, noteErrorSentence, notesButtonState, onlyDigits, orderNotes, parseBoleto, parseNfeXml, paymentLabel, paymentTypeFromCode, pullNoteByKey, recipientLabel, refreshNote, rejectNote, removeNoteAttachment, saveNoteDecision, searchProducts, stageNoteFromXml, statusLabel, suggestNoteItems, summarizeNotes, syncNewNotes, toStagePayload };
|
|
8935
9764
|
//# sourceMappingURL=index.js.map
|
|
8936
9765
|
//# sourceMappingURL=index.js.map
|