Skip to content

Commit f449056

Browse files
author
Namio Evangelista Cavalcante Sousa
committed
NEW: Adicionada funcionalidade para trabalhar com Chaves de Acessos de Documentos Fiscais
1 parent 8ded37e commit f449056

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed

bradocs4py/chaveacessonfe.py

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import re
4+
5+
from datetime import datetime
6+
from itertools import chain
7+
from random import randint
8+
9+
from .cnpj import GeradorCnpj, ValidadorCnpj
10+
from .documentoidentificacao import DocumentoIdentificacao
11+
12+
class ChaveAcessoNFe(DocumentoIdentificacao):
13+
"""docstring for ChaveAcessoNFe"""
14+
15+
def __init__(self, arg): # type: (str)
16+
super().__init__(arg)
17+
18+
def __str__(self):
19+
"""
20+
Will format an adequately formatted numbers-only ChaveAcessoNFe string, adding in standard formatting visual
21+
aid symbols for display.
22+
If ChaveAcessoNFe is None, returns an empty string; otherwise, if ChaveAcessoNFe string is shorten that 11 digits or
23+
contains non-digits characters, returns the raw value that represents the instance of invalid CPF
24+
string unformatted.
25+
"""
26+
27+
if self.rawValue == None: return str()
28+
29+
x = self.rawValue
30+
31+
if not x.isdigit() or len(x) != 44 or len(set(x)) == 1:
32+
return self.rawValue
33+
34+
return '{} {} {} {} {} {} {} {} {} {} {}'.format(x[:4], x[4:8], x[8:12], x[12:16], x[16:20], x[20:24], x[24:28], x[28:32], x[32:36], x[36:40], x[40:44])
35+
36+
@property
37+
def isValid(self):
38+
"""
39+
Returns whether or not the verifying checksum digits of the given `cpf` match it's base number.
40+
Input should be a digit string of proper length.
41+
"""
42+
return ValidadorChaveAcessoNFe.validar(self)
43+
44+
45+
46+
class ValidadorChaveAcessoNFe(object):
47+
"""
48+
Valida uma instância de ChaveAcessoNFe ou uma cadeia de caracteres (string) que representa uma Chave de Acesso de uma NF-e
49+
"""
50+
51+
def __call__(self, value):
52+
return ValidadorChaveAcessoNFe.validar(value)
53+
54+
@staticmethod
55+
def validar(arg): # type: (ChaveAcessoNFe) -> bool or type: (str) -> bool
56+
57+
def __hashDigit(chave):
58+
pesos = chain(range(4,1,-1), list(range(9,1,-1))*5)
59+
60+
val = sum(int(digito) * peso for digito, peso in zip(chave, pesos)) % 11
61+
62+
return 0 if val < 2 else 11 - val
63+
64+
if type(arg) != ChaveAcessoNFe and type(arg) != str and type(arg) != int:
65+
raise TypeError('%s não corresponde a um tipo válido para uma ChaveAcessoNFe' & type(arg).__name__)
66+
67+
p = re.compile('[^0-9]')
68+
x = p.sub('', str(arg)) if type(arg) == str or type(arg) == int else p.sub('', arg.rawValue)
69+
70+
if len(x) != 44 or len(set(x)) == 1:
71+
return False
72+
73+
# 23 1811 06850713000120 55 001 001766829 1 11103011 2
74+
75+
p = re.compile(r'^1[1-7]|2[1-9]|3([1-3]|5)|4[1-3]|5[0-3]$')
76+
77+
if not p.match(str(x)[:2]):
78+
"""
79+
O primeiro par de dígitos da chave deve corresponder ao código, segundo o IBGE, de uma das Unidades
80+
Federativas do Brasil.
81+
"""
82+
return False
83+
84+
if int(x[4:6]) not in range(1,13):
85+
"""
86+
O terceiro par de dígitos deve corresponder ao valor de um dos meses do ano
87+
"""
88+
return False
89+
90+
if datetime.strptime(x[2:6], '%y%m').year > datetime.now().year:
91+
"""
92+
O ano de emissão do documento fiscal deve ser anterior ao ano em curso
93+
"""
94+
return False
95+
96+
if datetime.strptime(x[2:6], '%y%m').month > datetime.now().month:
97+
"""
98+
O mês de emissão do documento fiscal deve ser anterior ao mês em curso
99+
"""
100+
return False
101+
102+
if not ValidadorCnpj.validar(x[6:20]):
103+
"""
104+
Os 14 próximos caracteres numéricos devem corresponder a um CNPJ válido
105+
"""
106+
return False
107+
108+
if __hashDigit(x) != int(x[43]):
109+
"""
110+
O dígito verificador calculado para a chave da NF-e deve corresponder ao informado
111+
"""
112+
return False
113+
114+
return True
115+
116+
validarChaveAcessoNFe = ValidadorChaveAcessoNFe()
117+
118+
119+
class GeradorChaveAcessoNFe(object):
120+
"""
121+
Permite gerar uma Chave de Acesso de uma NF-e, considerando a sua Lei de Formação
122+
123+
A Chave de Acesso da Nota Fiscal Eletrônica é representada por uma sequência de 44 caracteres numéricos, devendo ser
124+
composta pelos seguintes campos que se encontram dispersos no Layout da NF-e:
125+
126+
* UF - Código da UF do emitente do Documento Fiscal (2 caracteres numéricos)
127+
* AAMM - Ano e mês da emissão da NF-e (4 caracteres numéricos)
128+
* CNPJ - CNPJ do emitente do Documento Fiscal (14 caracteres numéricos)
129+
* Modelo - Modelo do Documento Fiscal (2 caracteres numéricos)
130+
* Série - Série do Documento Fiscal (3 caracteres numéricos)
131+
* Número - Número do Documento Fiscal (9 caracteres numéricos)
132+
* Forma Emissão - Forma de emissão do Documento Fiscal (1 caractere numérico)
133+
* Código Numérico - Código numérico que compõe a Chave de Acesso (8 caracteres numéricos)
134+
* DV - Dígito verificados (1 caractere numérico)
135+
136+
O Dígito Verificador irá garantir a integridade da Chave de Acesso, protegendo-a principalmente contra digitações erradas.
137+
138+
O GeradorChaveAcessoNFe, considera cada conjunto de caracteres numéricos que compõe a chave de acesso de forma independente,
139+
iso é, os dois primeiros caracteres numéricos não serão gerados aleatoriamente, uma vez que nem todo par de dígitos representam
140+
uma Unidade da Federação Brasileira, como é o caso do número 30 ou 34 - Não existe, até o momento de lançamento deste
141+
código, uma UF brasileira com algum desses códigos. De igual forma, a sequência de 14 caracteres que corresponde ao
142+
CNPJ do emitente, corresponderá a um número de CNPJ válido.
143+
"""
144+
145+
def __call__(self, **kwargs):
146+
return GeradorChaveAcessoNFe.gerar(**kwargs)
147+
148+
149+
@staticmethod
150+
def gerar(**kwargs):
151+
152+
def __hashDigit(chave):
153+
pesos = chain(range(4,1,-1), list(range(9,1,-1))*5)
154+
155+
val = sum(int(digito) * peso for digito, peso in zip(chave, pesos)) % 11
156+
157+
return 0 if val < 2 else 11 - val
158+
159+
_estados_brasileiros = [11,12,13,14,15,16,17,21,22,23,24,25,26,27,28,29,31,32,33,35,41,42,43,50,51,52,53]
160+
_formas_emissao = (
161+
(1, 'Emissão normal (não em contingência)'),
162+
(2, 'Contingência FS-IA, com impressão do DANFE em formulário de segurança'),
163+
(3, 'Contingência SCAN (Sistema de Contingência do Ambiente Nacional)'),
164+
(4, 'Contingência DPEC (Declaração Prévia da Emissão em Contingência)'),
165+
(5, 'Contingência FS-DA, com impressão do DANFE em formulário de segurança'),
166+
(6, 'Contingência SVC-AN (SEFAZ Virtual de Contingência do AN)'),
167+
(7, 'Contingência SVC-RS (SEFAZ Virtual de Contingência do RS);'),
168+
(9, 'Contingência off-line da NFC-e (as demais opções de contingência são válidas também para a NFC-e)')
169+
)
170+
171+
_uf = str(kwargs.pop('UF', _estados_brasileiros[randint(0,26)]))
172+
_ano = kwargs.pop('anoEmissao', datetime.now().year)
173+
_mes = kwargs.pop('mesEmissao', datetime.now().month)
174+
_cnpj = kwargs.pop('cnpjEmitente', GeradorCnpj.gerar().rawValue)
175+
_modelo = str(kwargs.pop('modelo', 55 + 10 * randint(0,1)))
176+
_serie = str(kwargs.pop('serie', randint(0,999))).zfill(3)
177+
_numeroDF = str(kwargs.pop('numero', randint(1,999999999))).zfill(9)
178+
_formaEmissao = str(kwargs.pop('formaEmissao', _formas_emissao[randint(0,7)][0]))
179+
180+
branch = randint(0,99)
181+
branch %= 10000
182+
branch += int(branch == 0)
183+
branch = str(branch).zfill(4)
184+
185+
_codigoNumerico = str(randint(0, 9999)).zfill(4) + branch
186+
187+
while len(set(_codigoNumerico)) == 1: _codigoNumerico = str(randint(0, 9999)).zfill(4) + branch
188+
189+
base = "%(uf)s%(ano)s%(mes)s%(cnpj)s%(mod)s%(serie)s%(nNF)s%(tpEmis)s%(cNF)s"%{
190+
'uf': _uf,
191+
'ano': datetime(_ano, _mes, datetime.now().day).strftime('%y'),
192+
'mes': datetime(_ano, _mes, datetime.now().day).strftime('%m'),
193+
'cnpj': _cnpj,
194+
'mod': _modelo,
195+
'serie': _serie,
196+
'nNF': _numeroDF,
197+
'tpEmis': _formaEmissao,
198+
'cNF': _codigoNumerico
199+
}
200+
201+
return ChaveAcessoNFe(base + str(__hashDigit(base)))
202+
203+
204+
205+
gerarChaveAcessoNFe = GeradorChaveAcessoNFe.gerar()
206+
207+
208+
209+

0 commit comments

Comments
 (0)