@nfewizard/shared 1.0.2 → 1.1.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/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/adapters/SchemaLoader.js +1 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/adapters/SchemaLoader.js.map +1 -1
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/base/BaseNFe.d.ts +1 -1
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/base/GerarConsulta.js +1 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/base/GerarConsulta.js.map +1 -1
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/environment/LoadCertificate.d.ts +5 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/environment/LoadCertificate.js +30 -9
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/environment/LoadCertificate.js.map +1 -1
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/XmlParser.d.ts +65 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/XmlParser.js +189 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/XmlParser.js.map +1 -1
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/index.d.ts +2 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/index.js +1 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/index.js.map +1 -1
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/validateSchema.d.ts +96 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/validateSchema.js +252 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/validateSchema.js.map +1 -0
- package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
- package/README.md +47 -1
- package/dist/index.cjs +470 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +470 -10
- package/dist/index.mjs.map +1 -1
- package/dist/src/base/BaseNFe.d.ts +1 -1
- package/dist/src/environment/LoadCertificate.d.ts +5 -0
- package/dist/src/utils/XmlParser.d.ts +65 -0
- package/dist/src/utils/index.d.ts +2 -0
- package/dist/src/utils/validateSchema.d.ts +96 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -434,6 +434,195 @@ class XmlParser {
|
|
|
434
434
|
}
|
|
435
435
|
return jsonBody;
|
|
436
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Converte um XML de envio (`enviNFe`) ou uma `NFe` solo em uma
|
|
439
|
+
* estrutura JSON compatível com o tipo `NFe` esperado pelos serviços
|
|
440
|
+
* de Autorização de NFe/NFCe (`{ idLote, indSinc, NFe }`).
|
|
441
|
+
*
|
|
442
|
+
* Formatos aceitos:
|
|
443
|
+
* - Envelope `enviNFe` contendo uma ou mais `NFe`.
|
|
444
|
+
* - `nfeProc` (XML autorizado) — extrai a `NFe` interna.
|
|
445
|
+
* - `NFe` solo — empacota com defaults (`idLote=1`, `indSinc=1`).
|
|
446
|
+
*
|
|
447
|
+
* Defaults são aplicáveis somente para os campos do envelope; o
|
|
448
|
+
* conteúdo de `infNFe` não é alterado e segue o fluxo normal de
|
|
449
|
+
* geração/assinatura no service.
|
|
450
|
+
*
|
|
451
|
+
* @param xml String XML de entrada.
|
|
452
|
+
* @param overrides Permite sobrescrever `idLote`/`indSinc` quando o XML
|
|
453
|
+
* não contém envelope.
|
|
454
|
+
*/
|
|
455
|
+
convertXmlEnvioNFeToJson(xml, overrides) {
|
|
456
|
+
logger.info('Convertendo XML de envio para JSON', {
|
|
457
|
+
context: 'XmlParser',
|
|
458
|
+
method: 'convertXmlEnvioNFeToJson',
|
|
459
|
+
});
|
|
460
|
+
const jsonData = this.parseNfeLikeXml(xml);
|
|
461
|
+
let envelope = this.findInObj(jsonData, 'enviNFe');
|
|
462
|
+
if (!envelope) {
|
|
463
|
+
const nfeProc = this.findInObj(jsonData, 'nfeProc');
|
|
464
|
+
const nfeNode = nfeProc ? this.findInObj(nfeProc, 'NFe') : this.findInObj(jsonData, 'NFe');
|
|
465
|
+
if (!nfeNode) {
|
|
466
|
+
throw new Error('XML inválido: não foi possível localizar `enviNFe` ou `NFe`.');
|
|
467
|
+
}
|
|
468
|
+
envelope = {
|
|
469
|
+
idLote: overrides?.idLote ?? '1',
|
|
470
|
+
indSinc: overrides?.indSinc ?? 1,
|
|
471
|
+
NFe: nfeNode,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
// Normaliza CNPJ/CPF -> CNPJCPF nas NFe (formato esperado pela lib)
|
|
475
|
+
const nfeList = Array.isArray(envelope.NFe) ? envelope.NFe : [envelope.NFe];
|
|
476
|
+
nfeList.forEach((n) => this.normalizeNfeForLibFormat(n));
|
|
477
|
+
return {
|
|
478
|
+
idLote: envelope.idLote ?? overrides?.idLote ?? '1',
|
|
479
|
+
indSinc: Number(envelope.indSinc ?? overrides?.indSinc ?? 1),
|
|
480
|
+
NFe: envelope.NFe,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Converte um XML autorizado (`nfeProc`) ou uma `NFe` solo em uma
|
|
485
|
+
* estrutura JSON compatível com o gerador de DANFE
|
|
486
|
+
* (`{ NFe, protNFe? }`), além de retornar a chave de acesso quando
|
|
487
|
+
* disponível.
|
|
488
|
+
*
|
|
489
|
+
* @param xml String XML de entrada.
|
|
490
|
+
*/
|
|
491
|
+
convertXmlNfeProcToJson(xml) {
|
|
492
|
+
logger.info('Convertendo nfeProc para JSON', {
|
|
493
|
+
context: 'XmlParser',
|
|
494
|
+
method: 'convertXmlNfeProcToJson',
|
|
495
|
+
});
|
|
496
|
+
const jsonData = this.parseNfeLikeXml(xml);
|
|
497
|
+
const nfeProc = this.findInObj(jsonData, 'nfeProc');
|
|
498
|
+
const NFe = nfeProc ? this.findInObj(nfeProc, 'NFe') : this.findInObj(jsonData, 'NFe');
|
|
499
|
+
if (!NFe) {
|
|
500
|
+
throw new Error('XML inválido: não foi possível localizar `NFe` ou `nfeProc`.');
|
|
501
|
+
}
|
|
502
|
+
const protNFe = nfeProc ? this.findInObj(nfeProc, 'protNFe') : this.findInObj(jsonData, 'protNFe');
|
|
503
|
+
const chFromProt = protNFe?.infProt?.chNFe;
|
|
504
|
+
const idAttr = NFe?.infNFe?.Id ?? '';
|
|
505
|
+
const chFromId = typeof idAttr === 'string' ? idAttr.replace(/^NFe/, '') : '';
|
|
506
|
+
const chave = chFromProt || chFromId || '';
|
|
507
|
+
const data = { NFe };
|
|
508
|
+
if (protNFe)
|
|
509
|
+
data.protNFe = protNFe;
|
|
510
|
+
return { data, chave };
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Faz o parse de um XML de NFe/NFCe (envio, retorno ou solo) preservando
|
|
514
|
+
* atributos e elevando-os para o nível do elemento (ex.: `Id` em `infNFe`,
|
|
515
|
+
* `versao` em `NFe`, `nItem` em `det`). Necessário para alimentar o
|
|
516
|
+
* fluxo interno da lib que espera atributos como propriedades comuns.
|
|
517
|
+
*/
|
|
518
|
+
parseNfeLikeXml(xml) {
|
|
519
|
+
const jsonAsString = convert.xml2json(xml, {
|
|
520
|
+
compact: true,
|
|
521
|
+
spaces: 2,
|
|
522
|
+
ignoreAttributes: false,
|
|
523
|
+
ignoreDeclaration: true,
|
|
524
|
+
trim: true,
|
|
525
|
+
ignoreInstruction: true,
|
|
526
|
+
ignoreComment: true,
|
|
527
|
+
ignoreCdata: true,
|
|
528
|
+
ignoreDoctype: true,
|
|
529
|
+
textFn: this.removeJsonTextAttribute,
|
|
530
|
+
});
|
|
531
|
+
const parsed = JSON.parse(jsonAsString);
|
|
532
|
+
this.liftAttributes(parsed);
|
|
533
|
+
return parsed;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Eleva, recursivamente, as chaves de `_attributes` para o próprio nível
|
|
537
|
+
* do elemento e remove o nó `_attributes`. Compatível com a saída de
|
|
538
|
+
* `xml-js` em modo compacto.
|
|
539
|
+
*/
|
|
540
|
+
liftAttributes(node) {
|
|
541
|
+
if (!node || typeof node !== 'object')
|
|
542
|
+
return;
|
|
543
|
+
if (Array.isArray(node)) {
|
|
544
|
+
node.forEach(item => this.liftAttributes(item));
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (node._attributes && typeof node._attributes === 'object') {
|
|
548
|
+
for (const attrKey of Object.keys(node._attributes)) {
|
|
549
|
+
if (!(attrKey in node)) {
|
|
550
|
+
node[attrKey] = node._attributes[attrKey];
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
delete node._attributes;
|
|
554
|
+
}
|
|
555
|
+
for (const key of Object.keys(node)) {
|
|
556
|
+
const value = node[key];
|
|
557
|
+
if (value && typeof value === 'object') {
|
|
558
|
+
this.liftAttributes(value);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Normaliza uma `NFe` parseada de XML para o formato interno da lib:
|
|
564
|
+
* - converte `CNPJ`/`CPF`/`idEstrangeiro` em `CNPJCPF` em `emit`,
|
|
565
|
+
* `dest`, `transp.transporta` e `NFref.refNFP`.
|
|
566
|
+
* - remove atributos elevados que a lib re-aplica via `$` ao montar
|
|
567
|
+
* o XML final (`infNFe.versao`, `det.nItem`), evitando duplicidade
|
|
568
|
+
* no XML gerado.
|
|
569
|
+
*
|
|
570
|
+
* Observação: `infRespTec` mantém o campo `CNPJ` original — a lib não
|
|
571
|
+
* unifica documento para esse nó.
|
|
572
|
+
*/
|
|
573
|
+
normalizeNfeForLibFormat(nfeNode) {
|
|
574
|
+
const infNFe = nfeNode?.infNFe;
|
|
575
|
+
if (!infNFe || typeof infNFe !== 'object')
|
|
576
|
+
return;
|
|
577
|
+
// versao é re-aplicado pela lib via $ ao montar `infNFe`
|
|
578
|
+
delete infNFe.versao;
|
|
579
|
+
// O atributo `Id` no XML vem com prefixo "NFe" (ex.: "NFe4126..."),
|
|
580
|
+
// mas a lib espera apenas a chave de 44 dígitos em `infNFe.Id` —
|
|
581
|
+
// ela própria re-prefixa com "NFe" ao calcular `chaveAcesso`.
|
|
582
|
+
if (typeof infNFe.Id === 'string' && /^NFe\d{44}$/.test(infNFe.Id)) {
|
|
583
|
+
infNFe.Id = infNFe.Id.slice(3);
|
|
584
|
+
}
|
|
585
|
+
this.unifyDocFields(infNFe.emit);
|
|
586
|
+
this.unifyDocFields(infNFe.dest);
|
|
587
|
+
this.unifyDocFields(infNFe.transp?.transporta);
|
|
588
|
+
// nItem é re-aplicado pela lib via $ em cada `det` quando é array.
|
|
589
|
+
// Por isso normalizamos sempre para array (no XML, com 1 item, viraria
|
|
590
|
+
// objeto único e a lib não re-aplicaria nItem, quebrando a validação XSD).
|
|
591
|
+
const det = infNFe.det;
|
|
592
|
+
if (Array.isArray(det)) {
|
|
593
|
+
det.forEach((d) => { if (d && typeof d === 'object')
|
|
594
|
+
delete d.nItem; });
|
|
595
|
+
}
|
|
596
|
+
else if (det && typeof det === 'object') {
|
|
597
|
+
delete det.nItem;
|
|
598
|
+
infNFe.det = [det];
|
|
599
|
+
}
|
|
600
|
+
const NFref = infNFe.NFref;
|
|
601
|
+
if (Array.isArray(NFref)) {
|
|
602
|
+
NFref.forEach((ref) => this.unifyDocFields(ref?.refNFP));
|
|
603
|
+
}
|
|
604
|
+
else if (NFref && typeof NFref === 'object') {
|
|
605
|
+
this.unifyDocFields(NFref.refNFP);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Em um nó (ex.: `emit`, `dest`), unifica `CNPJ`/`CPF`/`idEstrangeiro`
|
|
610
|
+
* em um único campo `CNPJCPF`, removendo as chaves originais. Mantém a
|
|
611
|
+
* primeira não-vazia encontrada na ordem `CNPJ → CPF → idEstrangeiro`.
|
|
612
|
+
*/
|
|
613
|
+
unifyDocFields(target) {
|
|
614
|
+
if (!target || typeof target !== 'object')
|
|
615
|
+
return;
|
|
616
|
+
if (target.CNPJCPF)
|
|
617
|
+
return;
|
|
618
|
+
const candidate = target.CNPJ || target.CPF || target.idEstrangeiro;
|
|
619
|
+
if (candidate) {
|
|
620
|
+
target.CNPJCPF = candidate;
|
|
621
|
+
}
|
|
622
|
+
delete target.CNPJ;
|
|
623
|
+
delete target.CPF;
|
|
624
|
+
delete target.idEstrangeiro;
|
|
625
|
+
}
|
|
437
626
|
}
|
|
438
627
|
|
|
439
628
|
class BaseNFE {
|
|
@@ -743,6 +932,7 @@ class GerarConsulta {
|
|
|
743
932
|
const config = this.environment.getConfig();
|
|
744
933
|
// Capturando o schema path
|
|
745
934
|
const { schemaPath } = this.utility.getSchema(metodo);
|
|
935
|
+
logger.info(xmlConsulta);
|
|
746
936
|
// Valida Schema apenas se o schema path estiver definido
|
|
747
937
|
if (schemaPath) {
|
|
748
938
|
if (config.lib?.useForSchemaValidation !== 'validateSchemaJsBased') {
|
|
@@ -993,6 +1183,7 @@ const getSchema = (metodo) => {
|
|
|
993
1183
|
RecepcaoEvento: `${pathSchemas}/envEvento_v1.00.xsd`,
|
|
994
1184
|
NFeDistribuicaoDFe: `${pathSchemas}/distDFeInt_v1.01.xsd`,
|
|
995
1185
|
NFEAutorizacao: `${pathSchemas}/enviNFe_v4.00.xsd`,
|
|
1186
|
+
NFeAutorizacao: `${pathSchemas}/enviNFe_v4.00.xsd`,
|
|
996
1187
|
NFEInutilizacao: `${pathSchemas}/inutNFe_v4.00.xsd`,
|
|
997
1188
|
NFERetAutorizacao: `${pathSchemas}/consReciNFe_v4.00.xsd`,
|
|
998
1189
|
CTeDistribuicaoDFe: `${pathSchemas}/cte/distDFeInt_v1.00.xsd`,
|
|
@@ -3656,6 +3847,254 @@ const mountICMS = (icms) => {
|
|
|
3656
3847
|
return {};
|
|
3657
3848
|
};
|
|
3658
3849
|
|
|
3850
|
+
/*
|
|
3851
|
+
* This file is part of NFeWizard-io.
|
|
3852
|
+
*
|
|
3853
|
+
* NFeWizard-io is free software: you can redistribute it and/or modify
|
|
3854
|
+
* it under the terms of the GNU General Public License as published by
|
|
3855
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
3856
|
+
* (at your option) any later version.
|
|
3857
|
+
*
|
|
3858
|
+
* NFeWizard-io is distributed in the hope that it will be useful,
|
|
3859
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
3860
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
3861
|
+
* GNU General Public License for more details.
|
|
3862
|
+
*
|
|
3863
|
+
* You should have received a copy of the GNU General Public License
|
|
3864
|
+
* along with NFeWizard-io. If not, see <https://www.gnu.org/licenses/>.
|
|
3865
|
+
*/
|
|
3866
|
+
/** Remove o prefixo `{namespace}` deixando apenas o nome local do elemento. */
|
|
3867
|
+
function stripNamespace(name) {
|
|
3868
|
+
return name.replace(/^\{[^}]*\}/, '');
|
|
3869
|
+
}
|
|
3870
|
+
/**
|
|
3871
|
+
* Interpreta uma mensagem de erro do libxmljs2 / xsd-schema-validator e retorna
|
|
3872
|
+
* um `SchemaValidationIssue` com versão humanizada + metadados estruturados.
|
|
3873
|
+
*/
|
|
3874
|
+
function humanizeIssue(raw, line, column) {
|
|
3875
|
+
const issue = { raw, humanized: raw, line, column };
|
|
3876
|
+
// 1) Elemento com filhos ausentes
|
|
3877
|
+
// Element '{ns}Nome': Missing child element(s). Expected is one of ( {ns}a, {ns}b ).
|
|
3878
|
+
let m = raw.match(/Element\s+'([^']+)':\s*Missing child element\(s\)\.\s*Expected is one of\s*\(\s*([^)]+)\s*\)\.?/i);
|
|
3879
|
+
if (m) {
|
|
3880
|
+
const element = stripNamespace(m[1]);
|
|
3881
|
+
const expected = m[2].split(',').map((s) => stripNamespace(s.trim())).filter(Boolean);
|
|
3882
|
+
issue.element = element;
|
|
3883
|
+
issue.expected = expected;
|
|
3884
|
+
issue.humanized = `Elemento <${element}> está incompleto. Filho ausente, esperado um de: ${expected.join(', ')}.`;
|
|
3885
|
+
return issue;
|
|
3886
|
+
}
|
|
3887
|
+
// 2) Elemento não esperado
|
|
3888
|
+
// Element '{ns}Nome': This element is not expected. Expected is one of ( {ns}a, {ns}b ).
|
|
3889
|
+
m = raw.match(/Element\s+'([^']+)':\s*This element is not expected\.\s*(?:Expected is one of\s*\(\s*([^)]+)\s*\)\.?)?/i);
|
|
3890
|
+
if (m) {
|
|
3891
|
+
const element = stripNamespace(m[1]);
|
|
3892
|
+
issue.element = element;
|
|
3893
|
+
if (m[2]) {
|
|
3894
|
+
issue.expected = m[2].split(',').map((s) => stripNamespace(s.trim())).filter(Boolean);
|
|
3895
|
+
issue.humanized = `Elemento <${element}> inesperado nesta posição. Esperado um de: ${issue.expected.join(', ')}.`;
|
|
3896
|
+
}
|
|
3897
|
+
else {
|
|
3898
|
+
issue.humanized = `Elemento <${element}> inesperado nesta posição.`;
|
|
3899
|
+
}
|
|
3900
|
+
return issue;
|
|
3901
|
+
}
|
|
3902
|
+
// 3) Atributo inválido / faltando
|
|
3903
|
+
// Element '{ns}Nome', attribute 'attr': ...
|
|
3904
|
+
m = raw.match(/Element\s+'([^']+)',\s*attribute\s+'([^']+)':\s*(.+?)\.?$/i);
|
|
3905
|
+
if (m) {
|
|
3906
|
+
const element = stripNamespace(m[1]);
|
|
3907
|
+
const attribute = m[2];
|
|
3908
|
+
const desc = m[3];
|
|
3909
|
+
issue.element = element;
|
|
3910
|
+
issue.attribute = attribute;
|
|
3911
|
+
issue.humanized = `Elemento <${element}> — atributo "${attribute}": ${desc}.`;
|
|
3912
|
+
return issue;
|
|
3913
|
+
}
|
|
3914
|
+
// 4) Valor inválido para tipo
|
|
3915
|
+
// Element '{ns}Nome': [facet 'pattern'] The value '...' is not accepted by the pattern '...'.
|
|
3916
|
+
// Element '{ns}Nome': '...' is not a valid value of the atomic type 'TString'.
|
|
3917
|
+
m = raw.match(/Element\s+'([^']+)':\s*(?:\[facet[^\]]*\]\s*)?(.+?)\.?$/i);
|
|
3918
|
+
if (m) {
|
|
3919
|
+
const element = stripNamespace(m[1]);
|
|
3920
|
+
issue.element = element;
|
|
3921
|
+
issue.humanized = `Elemento <${element}>: ${m[2]}.`;
|
|
3922
|
+
return issue;
|
|
3923
|
+
}
|
|
3924
|
+
// 5) Sem padrão reconhecido — devolve raw como humanizado
|
|
3925
|
+
return issue;
|
|
3926
|
+
}
|
|
3927
|
+
/** Cria um issue "interno" quando o erro não veio do schema (ex.: XML mal formado). */
|
|
3928
|
+
function internalIssue(raw) {
|
|
3929
|
+
return { raw, humanized: raw };
|
|
3930
|
+
}
|
|
3931
|
+
/**
|
|
3932
|
+
* Monta o relatório textual estilo SEFAZ-RS pronto para `console.log`.
|
|
3933
|
+
*/
|
|
3934
|
+
function buildReport(metodo, schemaFile, errors) {
|
|
3935
|
+
const linhas = [];
|
|
3936
|
+
linhas.push('=== Resultado da Validação do Schema ===');
|
|
3937
|
+
linhas.push(`Tipo de Mensagem: ${metodo}`);
|
|
3938
|
+
linhas.push(`Schema XML: ${schemaFile}`);
|
|
3939
|
+
if (errors.length === 0) {
|
|
3940
|
+
linhas.push('Parser XML: Nenhum erro encontrado');
|
|
3941
|
+
linhas.push('Schema XML: Nenhum erro encontrado');
|
|
3942
|
+
linhas.push('Status: XML válido.');
|
|
3943
|
+
}
|
|
3944
|
+
else {
|
|
3945
|
+
linhas.push(`Status: ${errors.length} erro(s) de validação`);
|
|
3946
|
+
linhas.push('');
|
|
3947
|
+
errors.forEach((err, idx) => {
|
|
3948
|
+
const pos = err.line ? ` (linha ${err.line}${err.column ? `, col ${err.column}` : ''})` : '';
|
|
3949
|
+
linhas.push(` ${idx + 1}. ${err.humanized}${pos}`);
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
linhas.push('=========================================');
|
|
3953
|
+
return linhas.join('\n');
|
|
3954
|
+
}
|
|
3955
|
+
/** Converte a lista de erros em linhas para `console.table`. */
|
|
3956
|
+
function buildTableRows(errors) {
|
|
3957
|
+
if (errors.length === 0) {
|
|
3958
|
+
return [{ '#': 1, Linha: '', Elemento: '-', Mensagem: 'XML válido.' }];
|
|
3959
|
+
}
|
|
3960
|
+
return errors.map((err, idx) => ({
|
|
3961
|
+
'#': idx + 1,
|
|
3962
|
+
Linha: err.line ?? '',
|
|
3963
|
+
Elemento: err.element ?? '-',
|
|
3964
|
+
Mensagem: err.humanized,
|
|
3965
|
+
}));
|
|
3966
|
+
}
|
|
3967
|
+
function schemaFileName(schemaPath) {
|
|
3968
|
+
if (!schemaPath)
|
|
3969
|
+
return '-';
|
|
3970
|
+
const parts = schemaPath.split(/[\\/]/);
|
|
3971
|
+
return parts[parts.length - 1] || schemaPath;
|
|
3972
|
+
}
|
|
3973
|
+
function makeResult(success, metodo, schemaPath, errors) {
|
|
3974
|
+
const schema = schemaFileName(schemaPath);
|
|
3975
|
+
const report = buildReport(metodo, schema, errors);
|
|
3976
|
+
const message = success
|
|
3977
|
+
? 'XML válido.'
|
|
3978
|
+
: errors[0]?.humanized ?? 'Erro de validação não identificado.';
|
|
3979
|
+
return {
|
|
3980
|
+
success,
|
|
3981
|
+
message,
|
|
3982
|
+
errors,
|
|
3983
|
+
report,
|
|
3984
|
+
tableRows: buildTableRows(errors),
|
|
3985
|
+
metodo,
|
|
3986
|
+
schema,
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
3989
|
+
function runJsBased(xml, metodo) {
|
|
3990
|
+
return new Promise(async (resolve, reject) => {
|
|
3991
|
+
const { basePath, schemaPath } = getSchema(metodo);
|
|
3992
|
+
if (!schemaPath) {
|
|
3993
|
+
reject(makeResult(false, metodo, '', [internalIssue(`Schema XSD não encontrado para o método '${metodo}'. Verifique se o nome do método está correto.`)]));
|
|
3994
|
+
return;
|
|
3995
|
+
}
|
|
3996
|
+
try {
|
|
3997
|
+
const completeXSD = await xsdAssembler.assemble(schemaPath);
|
|
3998
|
+
const xmlDoc = libxmljs.parseXml(xml);
|
|
3999
|
+
const xsdDoc = libxmljs.parseXml(completeXSD, { baseUrl: `${basePath}/` });
|
|
4000
|
+
const isValid = xmlDoc.validate(xsdDoc);
|
|
4001
|
+
if (isValid) {
|
|
4002
|
+
resolve(makeResult(true, metodo, schemaPath, []));
|
|
4003
|
+
return;
|
|
4004
|
+
}
|
|
4005
|
+
const errors = (xmlDoc.validationErrors || []).map((e) => humanizeIssue(String(e.message ?? e).trim(), e.line, e.column));
|
|
4006
|
+
reject(makeResult(false, metodo, schemaPath, errors.length ? errors : [internalIssue('XML inválido (sem detalhes do validador).')]));
|
|
4007
|
+
}
|
|
4008
|
+
catch (error) {
|
|
4009
|
+
reject(makeResult(false, metodo, schemaPath, [internalIssue(String(error?.message ?? error))]));
|
|
4010
|
+
}
|
|
4011
|
+
});
|
|
4012
|
+
}
|
|
4013
|
+
function runJavaBased(xml, metodo) {
|
|
4014
|
+
return new Promise((resolve, reject) => {
|
|
4015
|
+
const { schemaPath } = getSchema(metodo);
|
|
4016
|
+
if (!schemaPath) {
|
|
4017
|
+
reject(makeResult(false, metodo, '', [internalIssue(`Schema XSD não encontrado para o método '${metodo}'. Verifique se o nome do método está correto.`)]));
|
|
4018
|
+
return;
|
|
4019
|
+
}
|
|
4020
|
+
try {
|
|
4021
|
+
xsdValidator.validateXML(xml, schemaPath, (err, validationResult) => {
|
|
4022
|
+
if (err) {
|
|
4023
|
+
reject(makeResult(false, metodo, schemaPath, [humanizeIssue(String(err.message ?? err))]));
|
|
4024
|
+
return;
|
|
4025
|
+
}
|
|
4026
|
+
if (!validationResult?.valid) {
|
|
4027
|
+
const msgs = validationResult?.messages ?? [];
|
|
4028
|
+
const errors = msgs.length
|
|
4029
|
+
? msgs.map((m) => humanizeIssue(String(m)))
|
|
4030
|
+
: [internalIssue('XML inválido (sem detalhes do validador).')];
|
|
4031
|
+
reject(makeResult(false, metodo, schemaPath, errors));
|
|
4032
|
+
return;
|
|
4033
|
+
}
|
|
4034
|
+
resolve(makeResult(true, metodo, schemaPath, []));
|
|
4035
|
+
});
|
|
4036
|
+
}
|
|
4037
|
+
catch (error) {
|
|
4038
|
+
reject(makeResult(false, metodo, schemaPath, [internalIssue(String(error?.message ?? error))]));
|
|
4039
|
+
}
|
|
4040
|
+
});
|
|
4041
|
+
}
|
|
4042
|
+
/**
|
|
4043
|
+
* Valida um XML contra o schema XSD correspondente ao método fiscal informado.
|
|
4044
|
+
*
|
|
4045
|
+
* ### Seleção do validador (ordem de prioridade)
|
|
4046
|
+
* 1. `options.validator` — escolha explícita do caller.
|
|
4047
|
+
* 2. `options.environment.getConfig().lib?.useForSchemaValidation` — valor definido
|
|
4048
|
+
* na inicialização da lib.
|
|
4049
|
+
* 3. `'validateSchemaJsBased'` — padrão (sem JDK).
|
|
4050
|
+
*
|
|
4051
|
+
* @param xml - String XML a ser validada.
|
|
4052
|
+
* @param metodo - Nome do método/operação fiscal (ex.: `'NFeAutorizacao'`).
|
|
4053
|
+
* @param options - Opções opcionais: `validator` e/ou `environment`.
|
|
4054
|
+
*
|
|
4055
|
+
* @example
|
|
4056
|
+
* // Sem opções: usa validateSchemaJsBased
|
|
4057
|
+
* await NFE_SchemaValidate(xml, 'NFeAutorizacao');
|
|
4058
|
+
*
|
|
4059
|
+
* @example
|
|
4060
|
+
* // Passando o environment inicializado pela lib (lê useForSchemaValidation do config)
|
|
4061
|
+
* await NFE_SchemaValidate(xml, 'NFeAutorizacao', { environment });
|
|
4062
|
+
*
|
|
4063
|
+
* @example
|
|
4064
|
+
* // Forçando um validador específico
|
|
4065
|
+
* await NFE_SchemaValidate(xml, 'NFeAutorizacao', { validator: 'validateSchemaJavaBased' });
|
|
4066
|
+
*/
|
|
4067
|
+
function NFE_SchemaValidate(xml, metodo, options) {
|
|
4068
|
+
let chosen = 'validateSchemaJsBased';
|
|
4069
|
+
if (options?.validator) {
|
|
4070
|
+
chosen = options.validator;
|
|
4071
|
+
}
|
|
4072
|
+
else if (options?.environment) {
|
|
4073
|
+
const fromConfig = options.environment.getConfig()?.lib?.useForSchemaValidation;
|
|
4074
|
+
if (fromConfig)
|
|
4075
|
+
chosen = fromConfig;
|
|
4076
|
+
}
|
|
4077
|
+
const xmlNormalizado = wrapEnviNFeIfNeeded(xml, metodo);
|
|
4078
|
+
return chosen === 'validateSchemaJavaBased'
|
|
4079
|
+
? runJavaBased(xmlNormalizado, metodo)
|
|
4080
|
+
: runJsBased(xmlNormalizado, metodo);
|
|
4081
|
+
}
|
|
4082
|
+
/**
|
|
4083
|
+
* Para os métodos de autorização de NF-e, o schema XSD (`enviNFe_v4.00.xsd`)
|
|
4084
|
+
* exige que o elemento raiz seja `<enviNFe>`. Quando o caller passa apenas a
|
|
4085
|
+
* árvore `<NFe>`, envelopamos automaticamente em `<enviNFe>` com `idLote=1` e
|
|
4086
|
+
* `indSinc=1` para que a validação funcione.
|
|
4087
|
+
*/
|
|
4088
|
+
function wrapEnviNFeIfNeeded(xml, metodo) {
|
|
4089
|
+
if (metodo !== 'NFEAutorizacao' && metodo !== 'NFeAutorizacao')
|
|
4090
|
+
return xml;
|
|
4091
|
+
const semDecl = xml.replace(/^\s*<\?xml[^?]*\?>\s*/, '').trimStart();
|
|
4092
|
+
if (/^<NFe[\s>]/.test(semDecl)) {
|
|
4093
|
+
return `<enviNFe versao="4.00" xmlns="http://www.portalfiscal.inf.br/nfe"><idLote>1</idLote><indSinc>1</indSinc>${semDecl}</enviNFe>`;
|
|
4094
|
+
}
|
|
4095
|
+
return xml;
|
|
4096
|
+
}
|
|
4097
|
+
|
|
3659
4098
|
/*
|
|
3660
4099
|
* This file is part of NFeWizard-io.
|
|
3661
4100
|
*
|
|
@@ -3841,12 +4280,35 @@ class LoadCertificate {
|
|
|
3841
4280
|
this.certificate = '';
|
|
3842
4281
|
this.cert_key = '';
|
|
3843
4282
|
}
|
|
3844
|
-
|
|
4283
|
+
/**
|
|
4284
|
+
* Resolve o conteúdo binário do PFX a partir do `pathCertificado` informado em
|
|
4285
|
+
* config. Aceita `string` (caminho), `Buffer` ou `NodeJS.ReadableStream`.
|
|
4286
|
+
*/
|
|
4287
|
+
async resolvePfxBuffer() {
|
|
4288
|
+
const input = this.config.dfe.pathCertificado;
|
|
4289
|
+
if (typeof input === 'string') {
|
|
4290
|
+
return fs.readFileSync(input);
|
|
4291
|
+
}
|
|
4292
|
+
if (Buffer.isBuffer(input)) {
|
|
4293
|
+
return input;
|
|
4294
|
+
}
|
|
4295
|
+
if (input && typeof input.pipe === 'function') {
|
|
4296
|
+
const stream = input;
|
|
4297
|
+
return await new Promise((resolve, reject) => {
|
|
4298
|
+
const chunks = [];
|
|
4299
|
+
stream.on('data', (chunk) => {
|
|
4300
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4301
|
+
});
|
|
4302
|
+
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
|
4303
|
+
stream.on('error', (err) => reject(err));
|
|
4304
|
+
});
|
|
4305
|
+
}
|
|
4306
|
+
throw new Error('pathCertificado deve ser uma string (path), Buffer ou Stream legível.');
|
|
4307
|
+
}
|
|
4308
|
+
loadCertificateWithPEM(pfxFile) {
|
|
3845
4309
|
return new Promise((resolve, reject) => {
|
|
3846
4310
|
try {
|
|
3847
|
-
const pfxPath = this.config.dfe.pathCertificado;
|
|
3848
4311
|
const pfxPassword = this.config.dfe.senhaCertificado;
|
|
3849
|
-
const pfxFile = fs.readFileSync(pfxPath);
|
|
3850
4312
|
const certsDir = path.resolve(baseDir, dir);
|
|
3851
4313
|
// Read CA certificates if directory exists and has files
|
|
3852
4314
|
let caCerts = [];
|
|
@@ -3905,13 +4367,10 @@ class LoadCertificate {
|
|
|
3905
4367
|
}
|
|
3906
4368
|
});
|
|
3907
4369
|
}
|
|
3908
|
-
loadCertificateWithNodeForge() {
|
|
4370
|
+
loadCertificateWithNodeForge(pfxFile) {
|
|
3909
4371
|
return new Promise((resolve, reject) => {
|
|
3910
4372
|
try {
|
|
3911
|
-
const pfxPath = this.config.dfe.pathCertificado;
|
|
3912
4373
|
const pfxPassword = this.config.dfe.senhaCertificado;
|
|
3913
|
-
// Lê o arquivo PFX
|
|
3914
|
-
const pfxFile = fs.readFileSync(pfxPath);
|
|
3915
4374
|
// Decodifica o arquivo PFX (PKCS#12)
|
|
3916
4375
|
const p12Asn1 = forge.asn1.fromDer(pfxFile.toString('binary'));
|
|
3917
4376
|
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, pfxPassword);
|
|
@@ -3983,15 +4442,16 @@ class LoadCertificate {
|
|
|
3983
4442
|
logger.info('Validando certificado', {
|
|
3984
4443
|
context: 'LoadCertificate',
|
|
3985
4444
|
});
|
|
4445
|
+
const pfxFile = await this.resolvePfxBuffer();
|
|
3986
4446
|
if (this.config.lib?.useOpenSSL || this.config.lib?.useOpenSSL === undefined) {
|
|
3987
|
-
const { agent } = await this.loadCertificateWithPEM();
|
|
4447
|
+
const { agent } = await this.loadCertificateWithPEM(pfxFile);
|
|
3988
4448
|
return {
|
|
3989
4449
|
certificate: this.certificate,
|
|
3990
4450
|
cert_key: this.cert_key,
|
|
3991
4451
|
agent
|
|
3992
4452
|
};
|
|
3993
4453
|
}
|
|
3994
|
-
const { agent } = await this.loadCertificateWithNodeForge();
|
|
4454
|
+
const { agent } = await this.loadCertificateWithNodeForge(pfxFile);
|
|
3995
4455
|
return {
|
|
3996
4456
|
certificate: this.certificate,
|
|
3997
4457
|
cert_key: this.cert_key,
|
|
@@ -4075,5 +4535,5 @@ class Environment {
|
|
|
4075
4535
|
}
|
|
4076
4536
|
}
|
|
4077
4537
|
|
|
4078
|
-
export { AxiosHttpClient, BaseNFE, BaseNFSe, Environment, GerarConsulta, HttpClientBuilder, JsonArrayTransport, LoadCertificate, SaveFiles, Utility, ValidaCPFCNPJ, ValidateEnvironment, XmlBuilder, XmlParser, getCodIBGE, getDesTipoPag, getSchema, logger, mountCOFINS, mountICMS, mountPIS };
|
|
4538
|
+
export { AxiosHttpClient, BaseNFE, BaseNFSe, Environment, GerarConsulta, HttpClientBuilder, JsonArrayTransport, LoadCertificate, NFE_SchemaValidate, SaveFiles, Utility, ValidaCPFCNPJ, ValidateEnvironment, XmlBuilder, XmlParser, getCodIBGE, getDesTipoPag, getSchema, logger, mountCOFINS, mountICMS, mountPIS };
|
|
4079
4539
|
//# sourceMappingURL=index.mjs.map
|