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
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
import { NFCE_QRCODE_URLS, NFCE_URL_CHAVE } from '../constants/qrcode.js';
|
|
3
|
-
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Extrai DigestValue da assinatura XML para compor QR Code v3.
|
|
7
|
-
* @param {string} xmlAssinado
|
|
8
|
-
*/
|
|
9
|
-
export function extrairDigestValor(xmlAssinado) {
|
|
10
|
-
const match = xmlAssinado.match(/<DigestValue>([^<]+)<\/DigestValue>/);
|
|
11
|
-
return match?.[1] ?? '';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Gera hash QR Code NFC-e versão 3.
|
|
16
|
-
* SHA1(chave + versao + tpAmb + cDest + vNF + vICMS + digVal + idCSC + CSC)
|
|
17
|
-
*/
|
|
18
|
-
export function gerarHashQrCodeV3(chaveAcesso, ambiente, cDest, vNF, vICMS, digVal, idCSC, csc) {
|
|
19
|
-
const vNFStr = Number(vNF).toFixed(2);
|
|
20
|
-
const vICMSStr = Number(vICMS).toFixed(2);
|
|
21
|
-
const id = idCSC.padStart(6, '0');
|
|
22
|
-
const input = `${chaveAcesso}3${ambiente}${cDest}${vNFStr}${vICMSStr}${digVal}${id}${csc}`;
|
|
23
|
-
return crypto.createHash('sha1').update(input).digest('hex').toUpperCase();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Monta URL do QR Code NFC-e versão 3 (layout exigido pelo XSD PL_009_V4).
|
|
28
|
-
* @param {{
|
|
29
|
-
* chaveAcesso: string,
|
|
30
|
-
* uf: string,
|
|
31
|
-
* ambiente?: number,
|
|
32
|
-
* cDest?: string,
|
|
33
|
-
* valorNF: number,
|
|
34
|
-
* valorICMS?: number,
|
|
35
|
-
* digVal: string,
|
|
36
|
-
* idCSC: string,
|
|
37
|
-
* csc: string,
|
|
38
|
-
* }} params
|
|
39
|
-
*/
|
|
40
|
-
export function gerarUrlQrCodeV3(params) {
|
|
41
|
-
const ambiente = params.ambiente ?? 2;
|
|
42
|
-
const uf = params.uf.toUpperCase();
|
|
43
|
-
const baseUrl = NFCE_QRCODE_URLS[uf]?.[ambiente];
|
|
44
|
-
|
|
45
|
-
if (!baseUrl) {
|
|
46
|
-
throw new GhinfeError(
|
|
47
|
-
`URL de QR Code NFC-e não configurada para UF ${uf} ambiente ${ambiente}`,
|
|
48
|
-
'NFCE_QRCODE_URL_MISSING'
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const cDest = (params.cDest ?? '').replace(/\D/g, '');
|
|
53
|
-
const vNFStr = Number(params.valorNF).toFixed(2);
|
|
54
|
-
const vICMSStr = Number(params.valorICMS ?? 0).toFixed(2);
|
|
55
|
-
const idCSC = params.idCSC.padStart(6, '0');
|
|
56
|
-
const hash = gerarHashQrCodeV3(
|
|
57
|
-
params.chaveAcesso,
|
|
58
|
-
ambiente,
|
|
59
|
-
cDest,
|
|
60
|
-
params.valorNF,
|
|
61
|
-
params.valorICMS ?? 0,
|
|
62
|
-
params.digVal,
|
|
63
|
-
idCSC,
|
|
64
|
-
params.csc
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
const p = `${params.chaveAcesso}|3|${ambiente}|${cDest}|${vNFStr}|${vICMSStr}|${params.digVal}|${idCSC}|${hash}`;
|
|
68
|
-
return `${baseUrl}?p=${p}`;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @param {string} uf
|
|
73
|
-
* @param {number} [ambiente]
|
|
74
|
-
*/
|
|
75
|
-
export function obterUrlChave(uf, ambiente = 2) {
|
|
76
|
-
const url = NFCE_URL_CHAVE[uf.toUpperCase()]?.[ambiente];
|
|
77
|
-
if (!url) {
|
|
78
|
-
return NFCE_QRCODE_URLS[uf.toUpperCase()]?.[ambiente] ?? 'https://www.nfce.fazenda.sp.gov.br/consulta';
|
|
79
|
-
}
|
|
80
|
-
return url;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Insere infNFeSupl no XML assinado da NFC-e.
|
|
85
|
-
* @param {string} xmlAssinado
|
|
86
|
-
* @param {string} qrCodeUrl
|
|
87
|
-
* @param {string} urlChave
|
|
88
|
-
*/
|
|
89
|
-
export function inserirInfNFeSupl(xmlAssinado, qrCodeUrl, urlChave) {
|
|
90
|
-
const supl = `<infNFeSupl><qrCode><![CDATA[${qrCodeUrl}]]></qrCode><urlChave>${urlChave}</urlChave></infNFeSupl>`;
|
|
91
|
-
return xmlAssinado.replace('</NFe>', `${supl}</NFe>`);
|
|
92
|
-
}
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { NFCE_QRCODE_URLS, NFCE_URL_CHAVE } from '../constants/qrcode.js';
|
|
3
|
+
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extrai DigestValue da assinatura XML para compor QR Code v3.
|
|
7
|
+
* @param {string} xmlAssinado
|
|
8
|
+
*/
|
|
9
|
+
export function extrairDigestValor(xmlAssinado) {
|
|
10
|
+
const match = xmlAssinado.match(/<DigestValue>([^<]+)<\/DigestValue>/);
|
|
11
|
+
return match?.[1] ?? '';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Gera hash QR Code NFC-e versão 3.
|
|
16
|
+
* SHA1(chave + versao + tpAmb + cDest + vNF + vICMS + digVal + idCSC + CSC)
|
|
17
|
+
*/
|
|
18
|
+
export function gerarHashQrCodeV3(chaveAcesso, ambiente, cDest, vNF, vICMS, digVal, idCSC, csc) {
|
|
19
|
+
const vNFStr = Number(vNF).toFixed(2);
|
|
20
|
+
const vICMSStr = Number(vICMS).toFixed(2);
|
|
21
|
+
const id = idCSC.padStart(6, '0');
|
|
22
|
+
const input = `${chaveAcesso}3${ambiente}${cDest}${vNFStr}${vICMSStr}${digVal}${id}${csc}`;
|
|
23
|
+
return crypto.createHash('sha1').update(input).digest('hex').toUpperCase();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Monta URL do QR Code NFC-e versão 3 (layout exigido pelo XSD PL_009_V4).
|
|
28
|
+
* @param {{
|
|
29
|
+
* chaveAcesso: string,
|
|
30
|
+
* uf: string,
|
|
31
|
+
* ambiente?: number,
|
|
32
|
+
* cDest?: string,
|
|
33
|
+
* valorNF: number,
|
|
34
|
+
* valorICMS?: number,
|
|
35
|
+
* digVal: string,
|
|
36
|
+
* idCSC: string,
|
|
37
|
+
* csc: string,
|
|
38
|
+
* }} params
|
|
39
|
+
*/
|
|
40
|
+
export function gerarUrlQrCodeV3(params) {
|
|
41
|
+
const ambiente = params.ambiente ?? 2;
|
|
42
|
+
const uf = params.uf.toUpperCase();
|
|
43
|
+
const baseUrl = NFCE_QRCODE_URLS[uf]?.[ambiente];
|
|
44
|
+
|
|
45
|
+
if (!baseUrl) {
|
|
46
|
+
throw new GhinfeError(
|
|
47
|
+
`URL de QR Code NFC-e não configurada para UF ${uf} ambiente ${ambiente}`,
|
|
48
|
+
'NFCE_QRCODE_URL_MISSING'
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const cDest = (params.cDest ?? '').replace(/\D/g, '');
|
|
53
|
+
const vNFStr = Number(params.valorNF).toFixed(2);
|
|
54
|
+
const vICMSStr = Number(params.valorICMS ?? 0).toFixed(2);
|
|
55
|
+
const idCSC = params.idCSC.padStart(6, '0');
|
|
56
|
+
const hash = gerarHashQrCodeV3(
|
|
57
|
+
params.chaveAcesso,
|
|
58
|
+
ambiente,
|
|
59
|
+
cDest,
|
|
60
|
+
params.valorNF,
|
|
61
|
+
params.valorICMS ?? 0,
|
|
62
|
+
params.digVal,
|
|
63
|
+
idCSC,
|
|
64
|
+
params.csc
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const p = `${params.chaveAcesso}|3|${ambiente}|${cDest}|${vNFStr}|${vICMSStr}|${params.digVal}|${idCSC}|${hash}`;
|
|
68
|
+
return `${baseUrl}?p=${p}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} uf
|
|
73
|
+
* @param {number} [ambiente]
|
|
74
|
+
*/
|
|
75
|
+
export function obterUrlChave(uf, ambiente = 2) {
|
|
76
|
+
const url = NFCE_URL_CHAVE[uf.toUpperCase()]?.[ambiente];
|
|
77
|
+
if (!url) {
|
|
78
|
+
return NFCE_QRCODE_URLS[uf.toUpperCase()]?.[ambiente] ?? 'https://www.nfce.fazenda.sp.gov.br/consulta';
|
|
79
|
+
}
|
|
80
|
+
return url;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Insere infNFeSupl no XML assinado da NFC-e.
|
|
85
|
+
* @param {string} xmlAssinado
|
|
86
|
+
* @param {string} qrCodeUrl
|
|
87
|
+
* @param {string} urlChave
|
|
88
|
+
*/
|
|
89
|
+
export function inserirInfNFeSupl(xmlAssinado, qrCodeUrl, urlChave) {
|
|
90
|
+
const supl = `<infNFeSupl><qrCode><![CDATA[${qrCodeUrl}]]></qrCode><urlChave>${urlChave}</urlChave></infNFeSupl>`;
|
|
91
|
+
return xmlAssinado.replace('</NFe>', `${supl}</NFe>`);
|
|
92
|
+
}
|
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
import { create } from 'xmlbuilder2';
|
|
2
|
-
import { UF_IBGE } from '../../constants/sefaz.js';
|
|
3
|
-
import { NFE_EVENTOS, CCE_COND_USO } from '../types/NFeEvento.js';
|
|
4
|
-
import { formatSefazDateTime } from '../../utils/date.utils.js';
|
|
5
|
-
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
6
|
-
|
|
7
|
-
const VERSAO_EVENTO = '1.00';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Monta XML de eventos NF-e (cancelamento, CC-e, etc.).
|
|
11
|
-
*/
|
|
12
|
-
export class NFeEventoXmlBuilder {
|
|
13
|
-
/**
|
|
14
|
-
* @param {import('../types/NFeEvento.js').NFeCancelamento} dados
|
|
15
|
-
* @returns {string}
|
|
16
|
-
*/
|
|
17
|
-
buildCancelamento(dados) {
|
|
18
|
-
this.#validarBase(dados);
|
|
19
|
-
if (!dados.protocolo) {
|
|
20
|
-
throw new GhinfeError('protocolo é obrigatório para cancelamento', 'CANCEL_PROTOCOLO_REQUIRED');
|
|
21
|
-
}
|
|
22
|
-
if (!dados.justificativa || dados.justificativa.trim().length < 15) {
|
|
23
|
-
throw new GhinfeError(
|
|
24
|
-
'justificativa deve ter no mínimo 15 caracteres',
|
|
25
|
-
'CANCEL_JUSTIFICATIVA_MIN'
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return this.#buildEvento(dados, NFE_EVENTOS.CANCELAMENTO, (det) => {
|
|
30
|
-
det.ele('descEvento').txt('Cancelamento').up();
|
|
31
|
-
det.ele('nProt').txt(dados.protocolo).up();
|
|
32
|
-
det.ele('xJust').txt(dados.justificativa.trim()).up();
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @param {import('../types/NFeEvento.js').NFeCartaCorrecao} dados
|
|
38
|
-
* @returns {string}
|
|
39
|
-
*/
|
|
40
|
-
buildCartaCorrecao(dados) {
|
|
41
|
-
this.#validarBase(dados);
|
|
42
|
-
if (!dados.correcao || dados.correcao.trim().length < 15) {
|
|
43
|
-
throw new GhinfeError(
|
|
44
|
-
'correcao deve ter no mínimo 15 caracteres',
|
|
45
|
-
'CCE_TEXTO_MIN'
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
if (dados.correcao.length > 1000) {
|
|
49
|
-
throw new GhinfeError('correcao não pode exceder 1000 caracteres', 'CCE_TEXTO_MAX');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return this.#buildEvento(dados, NFE_EVENTOS.CARTA_CORRECAO, (det) => {
|
|
53
|
-
det.ele('descEvento').txt('Carta de Correcao').up();
|
|
54
|
-
det.ele('xCorrecao').txt(dados.correcao.trim()).up();
|
|
55
|
-
det.ele('xCondUso').txt(CCE_COND_USO).up();
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param {import('../types/NFeEvento.js').NFeEventoBase} dados
|
|
61
|
-
* @param {string} tpEvento
|
|
62
|
-
* @param {(det: import('xmlbuilder2').XMLBuilder) => void} buildDetEvento
|
|
63
|
-
*/
|
|
64
|
-
#buildEvento(dados, tpEvento, buildDetEvento) {
|
|
65
|
-
const sequencia = dados.sequencia ?? 1;
|
|
66
|
-
const idLote = dados.idLote ?? '1';
|
|
67
|
-
const cOrgao = UF_IBGE[dados.uf.toUpperCase()] ?? '35';
|
|
68
|
-
const dhEvento = formatSefazDateTime();
|
|
69
|
-
const idInfEvento = `ID${tpEvento}${dados.chaveAcesso}${String(sequencia).padStart(2, '0')}`;
|
|
70
|
-
|
|
71
|
-
const root = create({ version: '1.0', encoding: 'UTF-8' })
|
|
72
|
-
.ele('envEvento', { xmlns: 'http://www.portalfiscal.inf.br/nfe', versao: VERSAO_EVENTO })
|
|
73
|
-
.ele('idLote').txt(idLote).up();
|
|
74
|
-
|
|
75
|
-
const evento = root.ele('evento', { versao: VERSAO_EVENTO });
|
|
76
|
-
const infEvento = evento.ele('infEvento', { Id: idInfEvento });
|
|
77
|
-
|
|
78
|
-
infEvento
|
|
79
|
-
.ele('cOrgao').txt(cOrgao).up()
|
|
80
|
-
.ele('tpAmb').txt(String(dados.ambiente ?? 2)).up()
|
|
81
|
-
.ele('CNPJ').txt(dados.cnpj.replace(/\D/g, '')).up()
|
|
82
|
-
.ele('chNFe').txt(dados.chaveAcesso).up()
|
|
83
|
-
.ele('dhEvento').txt(dhEvento).up()
|
|
84
|
-
.ele('tpEvento').txt(tpEvento).up()
|
|
85
|
-
.ele('nSeqEvento').txt(String(sequencia)).up()
|
|
86
|
-
.ele('verEvento').txt(VERSAO_EVENTO).up();
|
|
87
|
-
|
|
88
|
-
const detEvento = infEvento.ele('detEvento', { versao: VERSAO_EVENTO });
|
|
89
|
-
buildDetEvento(detEvento);
|
|
90
|
-
detEvento.up();
|
|
91
|
-
infEvento.up();
|
|
92
|
-
evento.up();
|
|
93
|
-
|
|
94
|
-
return root.end({ prettyPrint: false, headless: false });
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** @param {import('../types/NFeEvento.js').NFeEventoBase} dados */
|
|
98
|
-
#validarBase(dados) {
|
|
99
|
-
const chave = dados.chaveAcesso?.replace(/\D/g, '');
|
|
100
|
-
if (!chave || chave.length !== 44) {
|
|
101
|
-
throw new GhinfeError('chaveAcesso deve conter 44 dígitos', 'INVALID_CHAVE');
|
|
102
|
-
}
|
|
103
|
-
if (!dados.cnpj) {
|
|
104
|
-
throw new GhinfeError('cnpj do autor do evento é obrigatório', 'INVALID_CNPJ');
|
|
105
|
-
}
|
|
106
|
-
if (!dados.uf) {
|
|
107
|
-
throw new GhinfeError('uf é obrigatória', 'INVALID_UF');
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
1
|
+
import { create } from 'xmlbuilder2';
|
|
2
|
+
import { UF_IBGE } from '../../constants/sefaz.js';
|
|
3
|
+
import { NFE_EVENTOS, CCE_COND_USO } from '../types/NFeEvento.js';
|
|
4
|
+
import { formatSefazDateTime } from '../../utils/date.utils.js';
|
|
5
|
+
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
6
|
+
|
|
7
|
+
const VERSAO_EVENTO = '1.00';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Monta XML de eventos NF-e (cancelamento, CC-e, etc.).
|
|
11
|
+
*/
|
|
12
|
+
export class NFeEventoXmlBuilder {
|
|
13
|
+
/**
|
|
14
|
+
* @param {import('../types/NFeEvento.js').NFeCancelamento} dados
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
buildCancelamento(dados) {
|
|
18
|
+
this.#validarBase(dados);
|
|
19
|
+
if (!dados.protocolo) {
|
|
20
|
+
throw new GhinfeError('protocolo é obrigatório para cancelamento', 'CANCEL_PROTOCOLO_REQUIRED');
|
|
21
|
+
}
|
|
22
|
+
if (!dados.justificativa || dados.justificativa.trim().length < 15) {
|
|
23
|
+
throw new GhinfeError(
|
|
24
|
+
'justificativa deve ter no mínimo 15 caracteres',
|
|
25
|
+
'CANCEL_JUSTIFICATIVA_MIN'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return this.#buildEvento(dados, NFE_EVENTOS.CANCELAMENTO, (det) => {
|
|
30
|
+
det.ele('descEvento').txt('Cancelamento').up();
|
|
31
|
+
det.ele('nProt').txt(dados.protocolo).up();
|
|
32
|
+
det.ele('xJust').txt(dados.justificativa.trim()).up();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {import('../types/NFeEvento.js').NFeCartaCorrecao} dados
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
buildCartaCorrecao(dados) {
|
|
41
|
+
this.#validarBase(dados);
|
|
42
|
+
if (!dados.correcao || dados.correcao.trim().length < 15) {
|
|
43
|
+
throw new GhinfeError(
|
|
44
|
+
'correcao deve ter no mínimo 15 caracteres',
|
|
45
|
+
'CCE_TEXTO_MIN'
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (dados.correcao.length > 1000) {
|
|
49
|
+
throw new GhinfeError('correcao não pode exceder 1000 caracteres', 'CCE_TEXTO_MAX');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return this.#buildEvento(dados, NFE_EVENTOS.CARTA_CORRECAO, (det) => {
|
|
53
|
+
det.ele('descEvento').txt('Carta de Correcao').up();
|
|
54
|
+
det.ele('xCorrecao').txt(dados.correcao.trim()).up();
|
|
55
|
+
det.ele('xCondUso').txt(CCE_COND_USO).up();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {import('../types/NFeEvento.js').NFeEventoBase} dados
|
|
61
|
+
* @param {string} tpEvento
|
|
62
|
+
* @param {(det: import('xmlbuilder2').XMLBuilder) => void} buildDetEvento
|
|
63
|
+
*/
|
|
64
|
+
#buildEvento(dados, tpEvento, buildDetEvento) {
|
|
65
|
+
const sequencia = dados.sequencia ?? 1;
|
|
66
|
+
const idLote = dados.idLote ?? '1';
|
|
67
|
+
const cOrgao = UF_IBGE[dados.uf.toUpperCase()] ?? '35';
|
|
68
|
+
const dhEvento = formatSefazDateTime();
|
|
69
|
+
const idInfEvento = `ID${tpEvento}${dados.chaveAcesso}${String(sequencia).padStart(2, '0')}`;
|
|
70
|
+
|
|
71
|
+
const root = create({ version: '1.0', encoding: 'UTF-8' })
|
|
72
|
+
.ele('envEvento', { xmlns: 'http://www.portalfiscal.inf.br/nfe', versao: VERSAO_EVENTO })
|
|
73
|
+
.ele('idLote').txt(idLote).up();
|
|
74
|
+
|
|
75
|
+
const evento = root.ele('evento', { versao: VERSAO_EVENTO });
|
|
76
|
+
const infEvento = evento.ele('infEvento', { Id: idInfEvento });
|
|
77
|
+
|
|
78
|
+
infEvento
|
|
79
|
+
.ele('cOrgao').txt(cOrgao).up()
|
|
80
|
+
.ele('tpAmb').txt(String(dados.ambiente ?? 2)).up()
|
|
81
|
+
.ele('CNPJ').txt(dados.cnpj.replace(/\D/g, '')).up()
|
|
82
|
+
.ele('chNFe').txt(dados.chaveAcesso).up()
|
|
83
|
+
.ele('dhEvento').txt(dhEvento).up()
|
|
84
|
+
.ele('tpEvento').txt(tpEvento).up()
|
|
85
|
+
.ele('nSeqEvento').txt(String(sequencia)).up()
|
|
86
|
+
.ele('verEvento').txt(VERSAO_EVENTO).up();
|
|
87
|
+
|
|
88
|
+
const detEvento = infEvento.ele('detEvento', { versao: VERSAO_EVENTO });
|
|
89
|
+
buildDetEvento(detEvento);
|
|
90
|
+
detEvento.up();
|
|
91
|
+
infEvento.up();
|
|
92
|
+
evento.up();
|
|
93
|
+
|
|
94
|
+
return root.end({ prettyPrint: false, headless: false });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @param {import('../types/NFeEvento.js').NFeEventoBase} dados */
|
|
98
|
+
#validarBase(dados) {
|
|
99
|
+
const chave = dados.chaveAcesso?.replace(/\D/g, '');
|
|
100
|
+
if (!chave || chave.length !== 44) {
|
|
101
|
+
throw new GhinfeError('chaveAcesso deve conter 44 dígitos', 'INVALID_CHAVE');
|
|
102
|
+
}
|
|
103
|
+
if (!dados.cnpj) {
|
|
104
|
+
throw new GhinfeError('cnpj do autor do evento é obrigatório', 'INVALID_CNPJ');
|
|
105
|
+
}
|
|
106
|
+
if (!dados.uf) {
|
|
107
|
+
throw new GhinfeError('uf é obrigatória', 'INVALID_UF');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|