ghinfe 0.6.0 → 0.6.2
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/.env.example +24 -15
- package/LICENSE +21 -21
- package/README.md +491 -413
- package/docs/developer-guide.html +421 -421
- package/docs/openapi.yaml +428 -428
- package/examples/dce-homologacao.js +141 -0
- package/examples/emitir-nfce.js +51 -51
- package/examples/emitir-nfe.js +83 -83
- package/examples/emitir-nfse.js +46 -46
- package/examples/endpoints-sefaz.js +14 -14
- package/examples/eventos-nfe.js +49 -49
- package/examples/gerar-chave.js +25 -25
- package/examples/gerar-dace.js +59 -0
- package/examples/gerar-danfe.js +75 -75
- package/examples/gerar-dce.js +58 -0
- package/examples/layouts-nfse.js +51 -51
- package/examples/mock-sefaz.js +52 -52
- package/examples/nfse-eventos.js +57 -57
- package/examples/reforma-ibscbs.js +27 -27
- package/examples/reforma-nfe-xml.js +52 -52
- package/examples/status-sefaz.js +12 -12
- package/examples/validar-xsd.js +48 -48
- package/package.json +59 -68
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/consSitDCeTiposBasico_v1.00.xsd +123 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/consSitDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/consStatServDCeTiposBasico_v1.00.xsd +110 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/consStatServDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/dceTiposBasico_v1.00.xsd +1388 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/retConsSitDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/retConsStatServDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/tiposGeralDCe_v1.00.xsd +712 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/xmldsig-core-schema_v1.01.xsd +98 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/dceTiposBasico_v1.00.xsd +1410 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/dce_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/evCancDCe_v1.00.xsd +36 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/eventoDCeTiposBasico_v1.00.xsd +283 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/eventoDCe_v1.00.xsd +9 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/procEventoDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/retEventoDCe_v1.00.xsd +9 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/tiposGeralDCe_v1.00.xsd +712 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/xmldsig-core-schema_v1.01.xsd +98 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/procDCe_v1.00.xsd +37 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/retDCe_v1.00.xsd +9 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/tiposGeralDCe_v1.00.xsd +712 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/xmldsig-core-schema_v1.01.xsd +98 -0
- package/schemas/nfe/PL_009_V4/enviNFe_v3.10.xsd +8 -8
- package/schemas/nfe/PL_009_V4/xmldsig-core-schema_v1.01.xsd +97 -97
- package/scripts/download-schemas.js +39 -39
- package/scripts/test-xsd.js +34 -34
- package/src/api/server.js +316 -316
- package/src/constants/sefaz.js +69 -69
- package/src/danfe/DanfeService.js +205 -205
- package/src/danfe/xml-extract.js +69 -69
- package/src/dce/builders/DCeEventoXmlBuilder.js +86 -0
- package/src/dce/builders/DCeXmlBuilder.js +345 -0
- package/src/dce/constants/dce.js +87 -0
- package/src/dce/dace/DaceService.js +356 -0
- package/src/dce/dace/dace-extract.js +114 -0
- package/src/dce/services/DCeService.js +263 -0
- package/src/dce/services/DCeSoapService.js +235 -0
- package/src/dce/services/DCeXmlService.js +67 -0
- package/src/dce/types/DCeDocument.js +106 -0
- package/src/dce/utils/qrcode.utils.js +59 -0
- package/src/errors/GhinfeError.js +13 -13
- package/src/errors/SefazError.js +25 -25
- package/src/errors/XsdValidationError.js +15 -15
- package/src/index.js +84 -60
- package/src/nfce/builders/NFCeXmlBuilder.js +178 -178
- package/src/nfce/constants/qrcode.js +26 -26
- package/src/nfce/services/NFCeService.js +188 -188
- package/src/nfce/services/NFCeXmlService.js +38 -38
- package/src/nfce/types/NFCeDocument.js +42 -42
- package/src/nfce/utils/qrcode.utils.js +92 -92
- package/src/nfe/builders/NFeEventoXmlBuilder.js +110 -110
- package/src/nfe/builders/NFeXmlBuilder.js +215 -215
- package/src/nfe/mocks/sefaz-responses.js +195 -195
- package/src/nfe/services/NFeEventoService.js +95 -95
- package/src/nfe/services/NFeService.js +196 -196
- package/src/nfe/services/NFeSignService.js +127 -111
- package/src/nfe/services/NFeSoapService.js +437 -437
- package/src/nfe/services/NFeXmlService.js +41 -41
- package/src/nfe/services/NFeXsdService.js +56 -56
- package/src/nfe/types/NFeDocument.js +92 -92
- package/src/nfe/types/NFeEvento.js +44 -44
- package/src/nfse/NFSeMunicipioResolver.js +42 -42
- package/src/nfse/builders/NFSeEventoXmlBuilder.js +92 -92
- package/src/nfse/builders/NFSeXmlBuilder.js +16 -16
- package/src/nfse/constants/municipios.js +82 -82
- package/src/nfse/layouts/adapters.js +202 -202
- package/src/nfse/mocks/nfse-responses.js +142 -142
- package/src/nfse/services/NFSeService.js +191 -191
- package/src/nfse/services/NFSeSoapService.js +232 -232
- package/src/nfse/services/NFSeXmlService.js +12 -12
- package/src/nfse/types/NFSeDocument.js +54 -54
- package/src/queue/NFCeQueue.js +63 -63
- package/src/queue/NFSeQueue.js +59 -59
- package/src/queue/NFeQueue.js +76 -76
- package/src/reforma/ibscbs.js +175 -175
- package/src/sefaz/SefazEndpointResolver.js +95 -95
- package/src/sefaz/ca/icp-brasil-raiz-v10.pem +38 -0
- package/src/sefaz/endpoints.registry.js +205 -205
- package/src/sefaz/httpsAgent.js +33 -0
- package/src/shared/fiscal.helpers.js +53 -53
- package/src/utils/certificate.utils.js +69 -47
- package/src/utils/chave.utils.js +205 -117
- package/src/utils/code128.utils.js +67 -0
- package/src/utils/date.utils.js +26 -26
- package/src/utils/xml.utils.js +44 -44
- package/src/validation/XsdValidator.js +133 -114
- package/src/validation/schemas.js +18 -14
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { DCeXmlService } from './DCeXmlService.js';
|
|
2
|
+
import { DCeSoapService } from './DCeSoapService.js';
|
|
3
|
+
import { DCeEventoXmlBuilder } from '../builders/DCeEventoXmlBuilder.js';
|
|
4
|
+
import { DaceService } from '../dace/DaceService.js';
|
|
5
|
+
import { NFeSignService } from '../../nfe/services/NFeSignService.js';
|
|
6
|
+
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
7
|
+
import { gerarChaveAcessoDCe, parseChaveAcessoDCe } from '../../utils/chave.utils.js';
|
|
8
|
+
import { assinarChaveQrCodeDCe, gerarUrlQrCodeDCe } from '../utils/qrcode.utils.js';
|
|
9
|
+
import { DCE_MODELO, DCE_TIPO_EMITENTE, DCE_TIPO_EMISSAO } from '../constants/dce.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Orquestrador da DC-e (Declaração de Conteúdo eletrônica, modelo 99).
|
|
13
|
+
*/
|
|
14
|
+
export class DCeService {
|
|
15
|
+
/**
|
|
16
|
+
* @param {{
|
|
17
|
+
* certificado?: { pfx: Buffer|string, senha: string },
|
|
18
|
+
* ambiente?: number,
|
|
19
|
+
* timeout?: number,
|
|
20
|
+
* compactar?: boolean,
|
|
21
|
+
* validarXsd?: boolean,
|
|
22
|
+
* qrCodeUrl?: string,
|
|
23
|
+
* urlChave?: string,
|
|
24
|
+
* }} [options]
|
|
25
|
+
* certificado: obrigatório para comunicação com a SEFAZ (TLS mútuo e assinatura)
|
|
26
|
+
* compactar: envia a DC-e via dceAutorizacaoZip (GZip + Base64)
|
|
27
|
+
*/
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.options = options;
|
|
30
|
+
this.ambiente = options.ambiente ?? 2;
|
|
31
|
+
this.xmlService = new DCeXmlService({ validarXsd: options.validarXsd });
|
|
32
|
+
this.signService = new NFeSignService();
|
|
33
|
+
this.eventoBuilder = new DCeEventoXmlBuilder();
|
|
34
|
+
this.soapService = options.certificado
|
|
35
|
+
? new DCeSoapService({ certificado: options.certificado, ambiente: this.ambiente, timeout: options.timeout })
|
|
36
|
+
: null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#certificado() {
|
|
40
|
+
if (!this.options.certificado || !this.soapService) {
|
|
41
|
+
throw new GhinfeError(
|
|
42
|
+
'Informe options.certificado no DCeService para comunicar com a SEFAZ',
|
|
43
|
+
'DCE_CERTIFICADO_AUSENTE'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return this.options.certificado;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* CNPJ que compõe a chave: de quem emite/assina a DC-e.
|
|
51
|
+
* @param {import('../types/DCeDocument.js').DCeDocumento} documento
|
|
52
|
+
*/
|
|
53
|
+
#cnpjChave(documento) {
|
|
54
|
+
const tpEmit = documento.ide.tipoEmitente;
|
|
55
|
+
const origem = {
|
|
56
|
+
[DCE_TIPO_EMITENTE.FISCO]: ['fisco', documento.fisco],
|
|
57
|
+
[DCE_TIPO_EMITENTE.MARKETPLACE]: ['marketplace', documento.marketplace],
|
|
58
|
+
[DCE_TIPO_EMITENTE.EMISSOR_PROPRIO]: ['emitente', documento.emitente],
|
|
59
|
+
[DCE_TIPO_EMITENTE.TRANSPORTADORA]: ['transportadora', documento.transportadora],
|
|
60
|
+
[DCE_TIPO_EMITENTE.ECT]: ['ect', documento.ect],
|
|
61
|
+
}[tpEmit];
|
|
62
|
+
|
|
63
|
+
if (!origem) {
|
|
64
|
+
throw new GhinfeError(`ide.tipoEmitente inválido: ${tpEmit}`, 'DCE_TP_EMIT_INVALIDO');
|
|
65
|
+
}
|
|
66
|
+
const [campo, parte] = origem;
|
|
67
|
+
if (!parte?.cnpj) {
|
|
68
|
+
throw new GhinfeError(`${campo}.cnpj é obrigatório para tipoEmitente=${tpEmit}`, 'DCE_CNPJ_CHAVE_AUSENTE');
|
|
69
|
+
}
|
|
70
|
+
return parte.cnpj;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gera a chave de acesso (se ausente) e fixa a data de emissão usada no AAMM e no dhEmi.
|
|
75
|
+
* @param {import('../types/DCeDocument.js').DCeDocumento} documento
|
|
76
|
+
*/
|
|
77
|
+
resolverDocumento(documento) {
|
|
78
|
+
const ambiente = documento.ambiente ?? this.ambiente;
|
|
79
|
+
|
|
80
|
+
if (documento.chaveAcesso) {
|
|
81
|
+
const chave = parseChaveAcessoDCe(documento.chaveAcesso);
|
|
82
|
+
if (!chave.valida) {
|
|
83
|
+
throw new GhinfeError('chaveAcesso com dígito verificador inválido', 'CHAVE_DV_INVALIDO');
|
|
84
|
+
}
|
|
85
|
+
if (chave.modelo !== DCE_MODELO) {
|
|
86
|
+
throw new GhinfeError('chaveAcesso não é de DC-e (modelo 99)', 'DCE_MODELO_INVALIDO');
|
|
87
|
+
}
|
|
88
|
+
return { ...documento, ambiente };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const dataEmissao = documento.ide.dataEmissao ?? new Date();
|
|
92
|
+
const chaveAcesso = gerarChaveAcessoDCe({
|
|
93
|
+
uf: documento.ide.uf,
|
|
94
|
+
cnpj: this.#cnpjChave(documento),
|
|
95
|
+
serie: documento.ide.serie ?? 1,
|
|
96
|
+
numero: documento.ide.numero,
|
|
97
|
+
tipoEmitente: documento.ide.tipoEmitente,
|
|
98
|
+
tipoEmissao: documento.ide.tipoEmissao,
|
|
99
|
+
siteAutorizador: documento.ide.siteAutorizador,
|
|
100
|
+
codigoNumerico: documento.ide.codigoNumerico,
|
|
101
|
+
dataEmissao,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return { ...documento, ambiente, ide: { ...documento.ide, dataEmissao }, chaveAcesso };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Monta a URL do QR Code. Em contingência off-line exige o certificado para o parâmetro sign.
|
|
109
|
+
* @param {import('../types/DCeDocument.js').DCeDocumento} documento Já resolvido (com chaveAcesso)
|
|
110
|
+
* @param {{ pfx: Buffer|string, senha: string }} [certificado]
|
|
111
|
+
*/
|
|
112
|
+
gerarQrCode(documento, certificado) {
|
|
113
|
+
const { tipoEmissao } = parseChaveAcessoDCe(documento.chaveAcesso);
|
|
114
|
+
const offline = tipoEmissao === DCE_TIPO_EMISSAO.CONTINGENCIA_OFFLINE;
|
|
115
|
+
|
|
116
|
+
if (offline && !certificado) {
|
|
117
|
+
throw new GhinfeError(
|
|
118
|
+
'Certificado obrigatório para gerar o QR Code em contingência off-line',
|
|
119
|
+
'DCE_QRCODE_CERTIFICADO_AUSENTE'
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return gerarUrlQrCodeDCe({
|
|
124
|
+
chaveAcesso: documento.chaveAcesso,
|
|
125
|
+
ambiente: documento.ambiente ?? 2,
|
|
126
|
+
tipoEmissao,
|
|
127
|
+
emitente: documento.emitente,
|
|
128
|
+
sign: offline ? assinarChaveQrCodeDCe(documento.chaveAcesso, certificado.pfx, certificado.senha) : undefined,
|
|
129
|
+
baseUrl: this.options.qrCodeUrl,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Gera XML completo (com infDCeSupl) sem assinatura.
|
|
135
|
+
* Em contingência off-line use gerarXmlAssinado — o QR Code depende do certificado.
|
|
136
|
+
* @param {import('../types/DCeDocument.js').DCeDocumento} documento
|
|
137
|
+
* @param {{ validarXsd?: boolean }} [options]
|
|
138
|
+
*/
|
|
139
|
+
gerarXml(documento, options = {}) {
|
|
140
|
+
const doc = this.resolverDocumento(documento);
|
|
141
|
+
return this.xmlService.gerarXml(doc, {
|
|
142
|
+
validarXsd: options.validarXsd,
|
|
143
|
+
qrCode: this.gerarQrCode(doc),
|
|
144
|
+
urlChave: this.options.urlChave ?? this.options.qrCodeUrl,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {import('../types/DCeDocument.js').DCeDocumento} documento
|
|
150
|
+
* @param {{ pfx: Buffer|string, senha: string }} certificado
|
|
151
|
+
* @param {{ validarXsd?: boolean }} [options]
|
|
152
|
+
*/
|
|
153
|
+
gerarXmlAssinado(documento, certificado, options = {}) {
|
|
154
|
+
const doc = this.resolverDocumento(documento);
|
|
155
|
+
const qrCode = this.gerarQrCode(doc, certificado);
|
|
156
|
+
|
|
157
|
+
const xml = this.xmlService.gerarXml(doc, {
|
|
158
|
+
validarXsd: false,
|
|
159
|
+
qrCode,
|
|
160
|
+
urlChave: this.options.urlChave ?? this.options.qrCodeUrl,
|
|
161
|
+
});
|
|
162
|
+
const xmlAssinado = this.signService.assinarDCe(xml, certificado.pfx, certificado.senha);
|
|
163
|
+
|
|
164
|
+
if (options.validarXsd ?? this.xmlService.validarXsd) {
|
|
165
|
+
this.xmlService.validarXml(xmlAssinado);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return { chaveAcesso: doc.chaveAcesso, xml: xmlAssinado, qrCode };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** @param {string} xml */
|
|
172
|
+
validarXsd(xml) {
|
|
173
|
+
return this.xmlService.validarXml(xml);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Consulta status do web service de autorização (DCeStatusServico).
|
|
178
|
+
* @param {string} [uf]
|
|
179
|
+
*/
|
|
180
|
+
async consultarStatus(uf) {
|
|
181
|
+
this.#certificado();
|
|
182
|
+
return this.soapService.consultarStatus(uf);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Gera, assina, valida (XSD) e envia a DC-e para autorização.
|
|
187
|
+
* Lança SefazError em caso de rejeição.
|
|
188
|
+
* @param {import('../types/DCeDocument.js').DCeDocumento} documento
|
|
189
|
+
* @param {{ compactar?: boolean }} [options]
|
|
190
|
+
*/
|
|
191
|
+
async emitir(documento, options = {}) {
|
|
192
|
+
const certificado = this.#certificado();
|
|
193
|
+
const doc = this.resolverDocumento(documento);
|
|
194
|
+
|
|
195
|
+
if (doc.ambiente !== this.ambiente) {
|
|
196
|
+
throw new GhinfeError(
|
|
197
|
+
`documento.ambiente (${doc.ambiente}) difere do ambiente do serviço (${this.ambiente})`,
|
|
198
|
+
'DCE_AMBIENTE_DIVERGENTE'
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const { xml, qrCode } = this.gerarXmlAssinado(doc, certificado, { validarXsd: true });
|
|
203
|
+
const retorno = await this.soapService.autorizar(xml, {
|
|
204
|
+
compactar: options.compactar ?? this.options.compactar,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
return { chaveAcesso: doc.chaveAcesso, qrCode, xmlAssinado: xml, retorno };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Consulta situação da DC-e na SEFAZ (DCeConsulta).
|
|
212
|
+
* @param {string} chaveAcesso
|
|
213
|
+
*/
|
|
214
|
+
async consultar(chaveAcesso) {
|
|
215
|
+
this.#certificado();
|
|
216
|
+
return this.soapService.consultar(chaveAcesso);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Cancela a DC-e (evento 110111). Prazo: até 24h após a autorização.
|
|
221
|
+
* @param {{
|
|
222
|
+
* chaveAcesso: string,
|
|
223
|
+
* protocolo: string,
|
|
224
|
+
* justificativa: string,
|
|
225
|
+
* emitente: { cnpj?: string, cpf?: string, idOutros?: string },
|
|
226
|
+
* sequencia?: number,
|
|
227
|
+
* dataEvento?: Date,
|
|
228
|
+
* }} params emitente: mesmo usuário emitente (grupo emit) da DC-e
|
|
229
|
+
*/
|
|
230
|
+
async cancelar(params) {
|
|
231
|
+
const certificado = this.#certificado();
|
|
232
|
+
const xmlEvento = this.eventoBuilder.buildCancelamento({ ...params, ambiente: this.ambiente });
|
|
233
|
+
const xmlAssinado = this.signService.assinarEvento(xmlEvento, certificado.pfx, certificado.senha);
|
|
234
|
+
this.xmlService.validarEvento(xmlAssinado);
|
|
235
|
+
|
|
236
|
+
const retorno = await this.soapService.enviarEvento(xmlAssinado);
|
|
237
|
+
return { chaveAcesso: params.chaveAcesso, xmlEvento: xmlAssinado, retorno };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Gera a DACE em HTML a partir do dceProc (ou do resultado de emitir()).
|
|
242
|
+
* @param {string|Awaited<ReturnType<DCeService['emitir']>>} xmlOuResultado
|
|
243
|
+
* @param {Parameters<DaceService['gerar']>[1]} [opcoes]
|
|
244
|
+
*/
|
|
245
|
+
async gerarDace(xmlOuResultado, opcoes) {
|
|
246
|
+
const xml = typeof xmlOuResultado === 'string' ? xmlOuResultado : xmlOuResultado.retorno.xmlProc;
|
|
247
|
+
return new DaceService().gerar(xml, opcoes);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** @param {Awaited<ReturnType<DCeService['emitir']>>} resultado */
|
|
251
|
+
toPersistencePayload(resultado) {
|
|
252
|
+
return {
|
|
253
|
+
chave_acesso: resultado.chaveAcesso,
|
|
254
|
+
modelo: DCE_MODELO,
|
|
255
|
+
xml_dce: resultado.retorno.xmlProc,
|
|
256
|
+
protocolo: resultado.retorno.protocolo,
|
|
257
|
+
qrcode_url: resultado.qrCode,
|
|
258
|
+
status: resultado.retorno.cStat,
|
|
259
|
+
motivo: resultado.retorno.xMotivo,
|
|
260
|
+
autorizado_em: resultado.retorno.dhRecbto ? new Date(resultado.retorno.dhRecbto) : new Date(),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import zlib from 'zlib';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { criarHttpsAgentSefaz } from '../../sefaz/httpsAgent.js';
|
|
4
|
+
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
5
|
+
import { SefazError } from '../../errors/SefazError.js';
|
|
6
|
+
import { UF_IBGE } from '../../constants/sefaz.js';
|
|
7
|
+
import { DCE_NAMESPACE, DCE_VERSAO, DCE_WEBSERVICES, DCE_STATUS } from '../constants/dce.js';
|
|
8
|
+
|
|
9
|
+
const SERVICOS = {
|
|
10
|
+
autorizacao: { wsdl: 'DCeAutorizacao', metodo: 'dceAutorizacao' },
|
|
11
|
+
consulta: { wsdl: 'DCeConsulta', metodo: 'dceConsulta' },
|
|
12
|
+
status: { wsdl: 'DCeStatusServico', metodo: 'dceStatusServico' },
|
|
13
|
+
evento: { wsdl: 'DCeRecepcaoEvento', metodo: 'dceRecepcaoEvento' },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// O WSDL de DCeConsulta declara dceResultMsg, mas o servidor responde dceConsultaResult
|
|
17
|
+
const RESULTADO_SOAP = /<(dce\w*Result(?:Msg)?)(?:\s[^>]*)?>([\s\S]*?)<\/\1>/;
|
|
18
|
+
|
|
19
|
+
const semDeclaracao = (xml) => xml.replace(/^\s*<\?xml[^>]*\?>\s*/, '');
|
|
20
|
+
|
|
21
|
+
/** @param {string} xml @param {string} nome */
|
|
22
|
+
function tag(xml, nome) {
|
|
23
|
+
return xml?.match(new RegExp(`<${nome}(?:\\s[^>]*)?>([^<]*)</${nome}>`))?.[1];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** @param {string} xml @param {string} nome */
|
|
27
|
+
function grupo(xml, nome) {
|
|
28
|
+
return xml?.match(new RegExp(`<${nome}[\\s>][\\s\\S]*?</${nome}>`))?.[0];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Cliente SOAP 1.2 dos web services da DC-e, com TLS mútuo (certificado A1).
|
|
33
|
+
*/
|
|
34
|
+
export class DCeSoapService {
|
|
35
|
+
/**
|
|
36
|
+
* @param {{
|
|
37
|
+
* certificado: { pfx: Buffer|string, senha: string },
|
|
38
|
+
* ambiente?: number,
|
|
39
|
+
* timeout?: number,
|
|
40
|
+
* endpoints?: Partial<Record<keyof typeof SERVICOS, string>>,
|
|
41
|
+
* ca?: string|string[],
|
|
42
|
+
* }} options
|
|
43
|
+
*/
|
|
44
|
+
constructor(options) {
|
|
45
|
+
if (!options?.certificado) {
|
|
46
|
+
throw new GhinfeError('Certificado A1 obrigatório para comunicação com a SEFAZ', 'DCE_CERTIFICADO_AUSENTE');
|
|
47
|
+
}
|
|
48
|
+
this.ambiente = options.ambiente ?? 2;
|
|
49
|
+
this.timeout = options.timeout ?? 30000;
|
|
50
|
+
this.endpoints = { ...DCE_WEBSERVICES[this.ambiente], ...options.endpoints };
|
|
51
|
+
this.httpsAgent = criarHttpsAgentSefaz(options.certificado, { ca: options.ca });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {keyof typeof SERVICOS} servico
|
|
56
|
+
* @param {string} xmlDados
|
|
57
|
+
* @param {{ compactar?: boolean }} [options]
|
|
58
|
+
* @returns {Promise<string>} XML de retorno (conteúdo do elemento *Result)
|
|
59
|
+
*/
|
|
60
|
+
async #chamar(servico, xmlDados, options = {}) {
|
|
61
|
+
const { wsdl, metodo } = SERVICOS[servico];
|
|
62
|
+
const ns = `http://www.portalfiscal.inf.br/dce/wsdl/${wsdl}`;
|
|
63
|
+
const acao = options.compactar ? `${metodo}Zip` : metodo;
|
|
64
|
+
|
|
65
|
+
const corpo = options.compactar
|
|
66
|
+
? `<dceDadosMsgZip xmlns="${ns}">${zlib.gzipSync(Buffer.from(semDeclaracao(xmlDados), 'utf8')).toString('base64')}</dceDadosMsgZip>`
|
|
67
|
+
: `<dceDadosMsg xmlns="${ns}">${semDeclaracao(xmlDados)}</dceDadosMsg>`;
|
|
68
|
+
|
|
69
|
+
const envelope =
|
|
70
|
+
'<?xml version="1.0" encoding="utf-8"?>' +
|
|
71
|
+
'<soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">' +
|
|
72
|
+
`<soap12:Body>${corpo}</soap12:Body></soap12:Envelope>`;
|
|
73
|
+
|
|
74
|
+
let response;
|
|
75
|
+
try {
|
|
76
|
+
response = await axios.post(this.endpoints[servico], envelope, {
|
|
77
|
+
httpsAgent: this.httpsAgent,
|
|
78
|
+
timeout: this.timeout,
|
|
79
|
+
responseType: 'text',
|
|
80
|
+
headers: { 'Content-Type': `application/soap+xml; charset=utf-8; action="${ns}/${acao}"` },
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (axios.isAxiosError(error)) {
|
|
84
|
+
throw new GhinfeError(
|
|
85
|
+
`Falha na comunicação SOAP (${wsdl}): ${error.message}`,
|
|
86
|
+
'SEFAZ_HTTP_ERROR',
|
|
87
|
+
{ status: error.response?.status, data: error.response?.data }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const conteudo = response.data.match(RESULTADO_SOAP)?.[2];
|
|
94
|
+
if (!conteudo) {
|
|
95
|
+
throw new GhinfeError(`Resposta SOAP de ${wsdl} sem elemento de resultado`, 'SEFAZ_RESPOSTA_INVALIDA', {
|
|
96
|
+
data: response.data,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return conteudo;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Consulta status do serviço (DCeStatusServico).
|
|
104
|
+
* @param {string} [uf] UF consultada (o autorizador responde por todas)
|
|
105
|
+
*/
|
|
106
|
+
async consultarStatus(uf = 'PR') {
|
|
107
|
+
const cUF = UF_IBGE[uf.toUpperCase()];
|
|
108
|
+
const xml =
|
|
109
|
+
`<consStatServDCe xmlns="${DCE_NAMESPACE}" versao="${DCE_VERSAO}">` +
|
|
110
|
+
`<tpAmb>${this.ambiente}</tpAmb><cUF>${cUF}</cUF><xServ>STATUS</xServ></consStatServDCe>`;
|
|
111
|
+
|
|
112
|
+
const retorno = await this.#chamar('status', xml);
|
|
113
|
+
const cStat = tag(retorno, 'cStat');
|
|
114
|
+
return {
|
|
115
|
+
emOperacao: cStat === DCE_STATUS.SERVICO_EM_OPERACAO,
|
|
116
|
+
cStat,
|
|
117
|
+
xMotivo: tag(retorno, 'xMotivo'),
|
|
118
|
+
cUF: tag(retorno, 'cUF'),
|
|
119
|
+
dhRecbto: tag(retorno, 'dhRecbto'),
|
|
120
|
+
tMed: tag(retorno, 'tMed'),
|
|
121
|
+
xObs: tag(retorno, 'xObs'),
|
|
122
|
+
xmlRetorno: retorno,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Envia DC-e assinada para autorização (síncrono). Lança SefazError se não autorizada.
|
|
128
|
+
* @param {string} xmlAssinado
|
|
129
|
+
* @param {{ compactar?: boolean }} [options] compactar: usa dceAutorizacaoZip (GZip + Base64)
|
|
130
|
+
*/
|
|
131
|
+
async autorizar(xmlAssinado, options = {}) {
|
|
132
|
+
const retorno = await this.#chamar('autorizacao', xmlAssinado, options);
|
|
133
|
+
const protDCe = grupo(retorno, 'protDCe');
|
|
134
|
+
const fonte = protDCe ?? retorno;
|
|
135
|
+
const cStat = tag(fonte, 'cStat');
|
|
136
|
+
const xMotivo = tag(fonte, 'xMotivo');
|
|
137
|
+
|
|
138
|
+
if (cStat !== DCE_STATUS.AUTORIZADO) {
|
|
139
|
+
throw new SefazError(cStat ?? '999', xMotivo ?? 'Retorno não identificado', {
|
|
140
|
+
cStatRetorno: tag(retorno, 'cStat'),
|
|
141
|
+
xmlRetorno: retorno,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
autorizado: true,
|
|
147
|
+
cStat,
|
|
148
|
+
xMotivo,
|
|
149
|
+
protocolo: tag(protDCe, 'nProt'),
|
|
150
|
+
chaveAcesso: tag(protDCe, 'chDCe'),
|
|
151
|
+
dhRecbto: tag(protDCe, 'dhRecbto'),
|
|
152
|
+
digVal: tag(protDCe, 'digVal'),
|
|
153
|
+
xmlProtocolo: protDCe,
|
|
154
|
+
xmlProc: this.montarDCeProc(xmlAssinado, protDCe),
|
|
155
|
+
xmlRetorno: retorno,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Consulta situação da DC-e (DCeConsulta). Não lança erro para cStat de rejeição.
|
|
161
|
+
* @param {string} chaveAcesso
|
|
162
|
+
*/
|
|
163
|
+
async consultar(chaveAcesso) {
|
|
164
|
+
const xml =
|
|
165
|
+
`<consSitDCe xmlns="${DCE_NAMESPACE}" versao="${DCE_VERSAO}">` +
|
|
166
|
+
`<tpAmb>${this.ambiente}</tpAmb><xServ>CONSULTAR</xServ><chDCe>${chaveAcesso}</chDCe></consSitDCe>`;
|
|
167
|
+
|
|
168
|
+
const retorno = await this.#chamar('consulta', xml);
|
|
169
|
+
const protDCe = grupo(retorno, 'protDCe');
|
|
170
|
+
const cStat = tag(retorno, 'cStat');
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
cStat,
|
|
174
|
+
xMotivo: tag(retorno, 'xMotivo'),
|
|
175
|
+
autorizada: cStat === DCE_STATUS.AUTORIZADO,
|
|
176
|
+
cancelada: cStat === DCE_STATUS.CANCELADO,
|
|
177
|
+
protocolo: tag(protDCe, 'nProt'),
|
|
178
|
+
xmlProtocolo: protDCe,
|
|
179
|
+
eventos: [...retorno.matchAll(/<procEventoDCe[\s>][\s\S]*?<\/procEventoDCe>/g)].map((m) => m[0]),
|
|
180
|
+
xmlRetorno: retorno,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Envia evento assinado (DCeRecepcaoEvento). Lança SefazError se não registrado.
|
|
186
|
+
* @param {string} xmlEventoAssinado
|
|
187
|
+
*/
|
|
188
|
+
async enviarEvento(xmlEventoAssinado) {
|
|
189
|
+
const retorno = await this.#chamar('evento', xmlEventoAssinado);
|
|
190
|
+
const infEvento = grupo(retorno, 'infEvento') ?? retorno;
|
|
191
|
+
const cStat = tag(infEvento, 'cStat');
|
|
192
|
+
const xMotivo = tag(infEvento, 'xMotivo');
|
|
193
|
+
const sucesso = [
|
|
194
|
+
DCE_STATUS.EVENTO_REGISTRADO,
|
|
195
|
+
DCE_STATUS.EVENTO_REGISTRADO_SEM_VINCULO,
|
|
196
|
+
DCE_STATUS.CANCELAMENTO_FORA_PRAZO,
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
if (!sucesso.includes(cStat)) {
|
|
200
|
+
throw new SefazError(cStat ?? '999', xMotivo ?? 'Retorno não identificado', { xmlRetorno: retorno });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const retEvento = grupo(retorno, 'retEventoDCe');
|
|
204
|
+
return {
|
|
205
|
+
registrado: true,
|
|
206
|
+
cStat,
|
|
207
|
+
xMotivo,
|
|
208
|
+
protocolo: tag(infEvento, 'nProt'),
|
|
209
|
+
tpEvento: tag(infEvento, 'tpEvento'),
|
|
210
|
+
dhRegEvento: tag(infEvento, 'dhRegEvento'),
|
|
211
|
+
xmlRetorno: retorno,
|
|
212
|
+
xmlProc: retEvento
|
|
213
|
+
? `<?xml version="1.0" encoding="UTF-8"?><procEventoDCe xmlns="${DCE_NAMESPACE}" versao="${DCE_VERSAO}">` +
|
|
214
|
+
`${semDeclaracao(xmlEventoAssinado)}${retEvento}</procEventoDCe>`
|
|
215
|
+
: undefined,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Monta dceProc (DC-e assinada + protocolo de autorização) para armazenamento.
|
|
221
|
+
* @param {string} xmlAssinado
|
|
222
|
+
* @param {string} protDCe
|
|
223
|
+
*/
|
|
224
|
+
montarDCeProc(xmlAssinado, protDCe) {
|
|
225
|
+
// A SEFA-PR devolve protDCe sem o atributo versao, obrigatório em procDCe_v1.00.xsd
|
|
226
|
+
const prot = /^<protDCe[^>]*\sversao=/.test(protDCe)
|
|
227
|
+
? protDCe
|
|
228
|
+
: protDCe.replace(/^<protDCe/, `<protDCe versao="${DCE_VERSAO}"`);
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
`<?xml version="1.0" encoding="UTF-8"?><dceProc xmlns="${DCE_NAMESPACE}" versao="${DCE_VERSAO}">` +
|
|
232
|
+
`${semDeclaracao(xmlAssinado)}${prot}</dceProc>`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { DCeXmlBuilder } from '../builders/DCeXmlBuilder.js';
|
|
2
|
+
import { XsdValidator } from '../../validation/XsdValidator.js';
|
|
3
|
+
import { XSD_SCHEMAS } from '../../validation/schemas.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Serviço de geração e validação XSD da DC-e.
|
|
7
|
+
*/
|
|
8
|
+
export class DCeXmlService {
|
|
9
|
+
/**
|
|
10
|
+
* @param {{ validarXsd?: boolean }} [options]
|
|
11
|
+
*/
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
this.builder = new DCeXmlBuilder();
|
|
14
|
+
this.validarXsd = options.validarXsd ?? false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {import('../types/DCeDocument.js').DCeDocumento} documento
|
|
19
|
+
* @param {{ validarXsd?: boolean, qrCode?: string, urlChave?: string, sign?: string, qrCodeBaseUrl?: string }} [options]
|
|
20
|
+
*/
|
|
21
|
+
gerarXml(documento, options = {}) {
|
|
22
|
+
const { validarXsd, ...buildOptions } = options;
|
|
23
|
+
const xml = this.builder.build(documento, buildOptions);
|
|
24
|
+
|
|
25
|
+
if (validarXsd ?? this.validarXsd) {
|
|
26
|
+
this.validarXml(xml);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return xml;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Valida contra dce_v1.00.xsd. XML sem assinatura recebe Signature stub.
|
|
34
|
+
* @param {string} xml
|
|
35
|
+
*/
|
|
36
|
+
validarXml(xml) {
|
|
37
|
+
return XsdValidator.validar(xml, XSD_SCHEMAS.DCE, {
|
|
38
|
+
injetarAssinaturaStub: !xml.includes('</Signature>'),
|
|
39
|
+
modelo: '99',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Valida evento assinado (eventoDCe) e o detalhe do cancelamento (evCancDCe).
|
|
45
|
+
* @param {string} xmlEventoAssinado
|
|
46
|
+
*/
|
|
47
|
+
validarEvento(xmlEventoAssinado) {
|
|
48
|
+
XsdValidator.validar(xmlEventoAssinado, XSD_SCHEMAS.DCE_EVENTO);
|
|
49
|
+
|
|
50
|
+
const detalhe = xmlEventoAssinado.match(/<evCancDCe>[\s\S]*?<\/evCancDCe>/)?.[0];
|
|
51
|
+
if (detalhe) {
|
|
52
|
+
XsdValidator.validar(
|
|
53
|
+
detalhe.replace('<evCancDCe>', '<evCancDCe xmlns="http://www.portalfiscal.inf.br/dce">'),
|
|
54
|
+
XSD_SCHEMAS.DCE_EVENTO_CANCELAMENTO
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return { valido: true, avisos: [] };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** @param {string} xml */
|
|
61
|
+
tentarValidar(xml) {
|
|
62
|
+
return XsdValidator.tentarValidar(xml, XSD_SCHEMAS.DCE, {
|
|
63
|
+
injetarAssinaturaStub: !xml.includes('</Signature>'),
|
|
64
|
+
modelo: '99',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} DCeEndereco
|
|
3
|
+
* @property {string} logradouro
|
|
4
|
+
* @property {string} numero
|
|
5
|
+
* @property {string} [complemento]
|
|
6
|
+
* @property {string} bairro
|
|
7
|
+
* @property {string} codigoMunicipio IBGE 7 dígitos
|
|
8
|
+
* @property {string} municipio
|
|
9
|
+
* @property {string} uf
|
|
10
|
+
* @property {string} cep
|
|
11
|
+
* @property {string} [codigoPais] Padrão 1058 (Brasil)
|
|
12
|
+
* @property {string} [pais] Padrão "BRASIL"
|
|
13
|
+
* @property {string} [telefone]
|
|
14
|
+
* @property {string} [email] Somente destinatário
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Usuário emitente (quem remete o bem). Informe apenas um entre cnpj, cpf e idOutros.
|
|
19
|
+
* @typedef {Object} DCeParte
|
|
20
|
+
* @property {string} [cnpj]
|
|
21
|
+
* @property {string} [cpf]
|
|
22
|
+
* @property {string} [idOutros] Estrangeiro, indígena, quilombola etc.
|
|
23
|
+
* @property {string} razaoSocial
|
|
24
|
+
* @property {DCeEndereco} endereco
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} DCeFisco
|
|
29
|
+
* @property {string} cnpj
|
|
30
|
+
* @property {string} orgao
|
|
31
|
+
* @property {string} uf
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} DCeMarketplace
|
|
36
|
+
* @property {string} cnpj
|
|
37
|
+
* @property {string} razaoSocial
|
|
38
|
+
* @property {string} site
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Transportadora ou ECT (Correios) emitente da DC-e.
|
|
43
|
+
* @typedef {Object} DCeEmpresaEmissora
|
|
44
|
+
* @property {string} cnpj
|
|
45
|
+
* @property {string} razaoSocial
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @typedef {Object} DCeItem
|
|
50
|
+
* @property {string} descricao
|
|
51
|
+
* @property {string} ncm Capítulo (2 dígitos) ou NCM completo (8). "00000000" se não classificável
|
|
52
|
+
* @property {number} quantidade
|
|
53
|
+
* @property {number} valorUnitario
|
|
54
|
+
* @property {number} [valorTotal] Calculado (quantidade × valorUnitario) se omitido
|
|
55
|
+
* @property {string} [informacoesAdicionais]
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @typedef {Object} DCeObservacao
|
|
60
|
+
* @property {string} campo Até 20 caracteres
|
|
61
|
+
* @property {string} texto Até 60 caracteres
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} DCeInformacoesAdicionais
|
|
66
|
+
* @property {string} [fisco]
|
|
67
|
+
* @property {string} [complementares]
|
|
68
|
+
* @property {string} [marketplace]
|
|
69
|
+
* @property {string} [ect]
|
|
70
|
+
* @property {DCeObservacao[]} [obsEmitente]
|
|
71
|
+
* @property {DCeObservacao[]} [obsFisco]
|
|
72
|
+
* @property {DCeObservacao[]} [obsMarketplace] Também usado por transportadoras
|
|
73
|
+
* @property {DCeObservacao[]} [obsECT]
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @typedef {Object} DCeIde
|
|
78
|
+
* @property {string} uf UF do usuário emitente
|
|
79
|
+
* @property {number} [serie] 0 a 999 (padrão 1)
|
|
80
|
+
* @property {number} numero 1 a 999999999
|
|
81
|
+
* @property {number} tipoEmitente 0=Fisco, 1=Marketplace, 2=Emissor próprio, 3=Transportadora, 4=ECT
|
|
82
|
+
* @property {number} [tipoEmissao] 1=Normal, 2=Contingência off-line
|
|
83
|
+
* @property {number} [siteAutorizador] 0 quando o autorizador tem site único
|
|
84
|
+
* @property {string} [codigoNumerico] cDC (6 dígitos) — gerado se omitido
|
|
85
|
+
* @property {Date} [dataEmissao]
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @typedef {Object} DCeDocumento
|
|
90
|
+
* @property {string} [chaveAcesso] 44 dígitos — gerada automaticamente se omitida
|
|
91
|
+
* @property {number} [ambiente] 1=Produção, 2=Homologação
|
|
92
|
+
* @property {DCeIde} ide
|
|
93
|
+
* @property {DCeParte} emitente
|
|
94
|
+
* @property {DCeFisco} [fisco] Obrigatório se tipoEmitente=0
|
|
95
|
+
* @property {DCeMarketplace} [marketplace] Obrigatório se tipoEmitente=1
|
|
96
|
+
* @property {DCeEmpresaEmissora} [transportadora] Obrigatório se tipoEmitente=3
|
|
97
|
+
* @property {DCeEmpresaEmissora} [ect] Obrigatório se tipoEmitente=4
|
|
98
|
+
* @property {DCeParte} destinatario
|
|
99
|
+
* @property {Array<{ cnpj?: string, cpf?: string }>} [autorizadosXml] Até 10
|
|
100
|
+
* @property {DCeItem[]} itens
|
|
101
|
+
* @property {{ modalidade: number, cnpjTransportadora?: string }} transporte 0=Correios, 1=Próprio, 2=Transportadora
|
|
102
|
+
* @property {DCeInformacoesAdicionais} [informacoesAdicionais]
|
|
103
|
+
* @property {string} [solicitacaoFisco] JSON do pedido no app do Fisco (somente tipoEmitente=0)
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
export {};
|