• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wmixvideo / nfe / #9462

23 Jul 2026 08:39PM UTC coverage: 50.977% (-0.8%) from 51.794%
#9462

push

web-flow
Refatoração de chaves para suportar CNPJ alfanumérico (NT 2026.004) (#1181)

* Javadoc Warnings invalid input: '<' e '&' literais

* Javadoc Warnings invalid input: '<' e '&' literais

* Refatoração de chaves para suportar CNPJ alfanumérico (NT 2026.004)

* Normaliza char para maiusculo na chave

O cálculo do DV usa valores[i - 1] - '0'. Se a chave vier com letras minúsculas (o que é aceito por alguns validadores de CNPJ na base), o DV calculado muda (ex.: 'a' - '0' != 'A' - '0'), gerando falsos negativos/positivos. Normalize o caractere para maiúsculo antes de converter para o valor numérico.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Diego Fincatto <58352+fincatto@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

12 of 14 new or added lines in 9 files covered. (85.71%)

757 existing lines in 44 files now uncovered.

15132 of 29684 relevant lines covered (50.98%)

0.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.0
/src/main/java/com/fincatto/documentofiscal/mdfe3/classes/parsers/MDFChaveParser.java
1
package com.fincatto.documentofiscal.mdfe3.classes.parsers;
2

3
import com.fincatto.documentofiscal.DFModelo;
4
import com.fincatto.documentofiscal.DFUnidadeFederativa;
5
import com.fincatto.documentofiscal.mdfe3.classes.def.MDFTipoEmissao;
6
import com.fincatto.documentofiscal.utils.DFUtils;
7
import org.apache.commons.lang3.StringUtils;
8

9
import java.time.LocalDate;
10

11
/**
12
 * Classe responsavel por fazer o parse da chave do MDFe.<br>
13
 * Aceita chave com CNPJ alfanumerico do emitente nas posicoes 6-19 (NT 2026.004).
14
 */
15
public class MDFChaveParser {
16

17
    private final String chave;
18

19
    /**
20
     * Normaliza a chave para maiusculas e remove qualquer caractere que nao seja alfanumerico.<br>
21
     * A partir do CNPJ alfanumerico (NT 2026.004), a chave de 44 posicoes pode conter letras
22
     * nas posicoes 6-19 (CNPJ do emitente), entao nao pode mais ser tratada como somente digitos.
23
     */
24
    public MDFChaveParser(final String chave) {
1✔
25
        this.chave = StringUtils.stripToEmpty(chave).replaceAll("[^A-Za-z0-9]", "").toUpperCase();
1✔
26
        if (this.chave.length() != 44) {
1✔
27
            throw new IllegalArgumentException(String.format("A chave do MDFe deve ter exatos 44 caracteres alfanumericos: %s", chave));
1✔
28
        }
29
    }
1✔
30

31
    public String getChave() {
32
        return chave;
1✔
33
    }
34

35
    public DFUnidadeFederativa getNFUnidadeFederativa() {
36
        return DFUnidadeFederativa.valueOfCodigo(this.chave.substring(0, 2));
1✔
37
    }
38

39
    public LocalDate getDataEmissao() {
40
        return LocalDate.of(this.getDataEmissaoAno(), this.getDataEmissaoMes(), 1);
1✔
41
    }
42

43
    private int getDataEmissaoMes() {
44
        return Integer.parseInt(this.chave.substring(4, 6));
1✔
45
    }
46

47
    private int getDataEmissaoAno() {
48
        return 2000 + Integer.parseInt(this.chave.substring(2, 4));
1✔
49
    }
50

51
    /**
52
     * Returna o CNPJ do emitente da chave.<br>
53
     * Se nao for um CNPJ valido, retorna nulo.
54
     *
55
     * @return CNPJ do emitente ou nulo.
56
     */
57
    public String getCnpjEmitente() {
58
        return isEmitentePessoaJuridica() ? this.chave.substring(6, 20) : null;
1✔
59
    }
60

61
    /**
62
     * Returna o CPF do emitente da chave.<br>
63
     * Se nao for um CPF valido, retorna nulo.
64
     *
65
     * @return CPF do emitente ou nulo.
66
     */
67
    public String getCpfEmitente() {
68
        return isEmitentePessoaFisica() ? this.chave.substring(9, 20) : null;
1✔
69
    }
70

71
    public DFModelo getModelo() {
72
        return DFModelo.valueOfCodigo(this.chave.substring(20, 22));
1✔
73
    }
74

75
    public String getSerie() {
76
        return this.chave.substring(22, 25);
1✔
77
    }
78

79
    public String getNumero() {
UNCOV
80
        return this.chave.substring(25, 34);
×
81
    }
82

83
    public MDFTipoEmissao getFormaEmissao() {
84
        return MDFTipoEmissao.valueOfCodigo(this.chave.substring(34, 35));
1✔
85
    }
86

87
    public String getCodigoNumerico() {
88
        return this.chave.substring(35, 43);
1✔
89
    }
90

91
    public String getDV() {
92
        return this.chave.substring(43, 44);
1✔
93
    }
94

95
    public String getFormatado() {
96
        return this.chave.replaceFirst("(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})", "$1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11");
1✔
97
    }
98

99
    /**
100
     * Indica se o emitente da chave eh pessoa fisica.
101
     *
102
     * @return Se chave foi emitida por pessoa fisica.
103
     */
104
    public boolean isEmitentePessoaFisica() {
105
        return DFUtils.isCpfValido(this.chave.substring(9, 20)) && this.isSerieReservadaPessoaFisica();
1✔
106
    }
107

108
    /**
109
     * Indica se o emitente da chave eh pessoa juridica.
110
     *
111
     * @return Se chave foi emitida por pessoa juridica.
112
     */
113
    public boolean isEmitentePessoaJuridica() {
114
        return DFUtils.isCnpjValido(this.chave.substring(6, 20)) && !this.isSerieReservadaPessoaFisica();
1✔
115
    }
116

117
    /**
118
     * Indica se a chave é de uma série destinada a pessoa física.<br>
119
     * De acordo com a documentação: "Será reservada uma faixa do campo Série do MDF-e (920-969), como forma de identificação da Emitente pessoa física (CPF);"<br>
120
     * <a href="http://sped.rfb.gov.br/estatico/8A/16CC5375F0AC1A0B7666221FC2116325133C01/MOC_MDFe_VisaoGeral_v3.00a.pdf">...</a>
121
     *
122
     * @return se a serie é destinada a pessoa física.
123
     */
124
    public boolean isSerieReservadaPessoaFisica() {
125
        if (DFUtils.isNumerico(this.getSerie())) {
1✔
126
            final int serie = Integer.parseInt(this.getSerie());
1✔
127
            return serie >= 920 && serie <= 969;
1✔
128
        }
UNCOV
129
        return false;
×
130
    }
131
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc