Skip to content
This repository has been archived by the owner on Nov 2, 2024. It is now read-only.

Código de colaboradores #25

Open
wants to merge 53 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
730944a
Novos Requisitos
claudiofsr Feb 29, 2020
1dc16f2
novos requisitos: cchardet e xlsxwriter
claudiofsr Feb 29, 2020
1585e94
add install_requires: cchardet e xlsxwriter
claudiofsr Feb 29, 2020
f3a4c33
nova versão: 1.0.3
claudiofsr Feb 29, 2020
28ddda7
testar leitura_completa e adicionar opção de codificação
claudiofsr Feb 29, 2020
bf8af84
remover a condição de registro de abertura e encerramento dos blocos
claudiofsr Feb 29, 2020
050f6ac
[new] CampoChaveEletronica e formatar saídas de CPF, CNPJ, Chave, ...
claudiofsr Feb 29, 2020
f1f8c33
[new] numero_da_linha
claudiofsr Feb 29, 2020
111fa09
[new] nivel e atualização dos registros
claudiofsr Feb 29, 2020
2692d23
[fix] registro de abertura e encerramento do Bloco 9
claudiofsr Feb 29, 2020
3db188c
[new] nivel e atualização dos registros
claudiofsr Feb 29, 2020
ad34f55
[new] novo diretório e arquivos
claudiofsr Feb 29, 2020
46a4a3b
[new] classes My_Switch e SPED_EFD_Info: imprimir info de arquivos SP…
claudiofsr Feb 29, 2020
8eb6f49
[new] encontrar arquivos SPED EFD no diretório indicado
claudiofsr Feb 29, 2020
e5a8323
[new] executável para impressão de relatórios
claudiofsr Feb 29, 2020
0de32d5
[new] tabelas adotadas
claudiofsr Feb 29, 2020
ea12f6e
[fix] corrigido dígito verificador do CPF
claudiofsr Feb 29, 2020
49d0ad0
instruções detalhadas
claudiofsr Feb 29, 2020
4163286
registrar efd_relatorios como executável
claudiofsr Feb 29, 2020
78c7513
adicionar efd_relatorios como executável
claudiofsr Mar 1, 2020
29256cf
converter arquivo de formato csv para excel xlsx
claudiofsr Mar 1, 2020
d10c9b2
imprime relatórios de SPED EFD em Excel
claudiofsr Mar 1, 2020
f957bae
substitui vários if/then/else por dicionario
claudiofsr Mar 1, 2020
b4900f8
correção do dígito verificador do cpf teste
claudiofsr Mar 1, 2020
6f866ee
renomear arquivo
claudiofsr Mar 1, 2020
e430523
renomear arquivo
claudiofsr Mar 1, 2020
d8dd94e
arquivos deletados
claudiofsr Mar 1, 2020
bd8f3ee
correção de concordância em mensagem
claudiofsr Mar 1, 2020
41666c5
importar EFD_Tabelas
claudiofsr Mar 1, 2020
be0ea07
verificar se len(data) == 8 dígitos
claudiofsr Mar 1, 2020
d493836
reintrodução de comentários
claudiofsr Mar 2, 2020
2985410
alteração do nome do método <fechamento> para <encerramento>
claudiofsr Mar 2, 2020
815c55f
alteração do nome do método <fechamento> para <encerramento>
claudiofsr Mar 2, 2020
349fe39
novos comentários
claudiofsr Mar 2, 2020
dd5dc58
possibilidade de selecionar multiplos arquivos
claudiofsr Mar 3, 2020
fb1a024
adicionada a variável numero_do_arquivo
claudiofsr Mar 3, 2020
aecf6a0
implementado o processamento de múltiplos arquivos
claudiofsr Mar 3, 2020
0c48529
[FIX] considerado o caso num_cpus <= 2
claudiofsr Mar 3, 2020
d4ffa40
remoção da variável numero_do_arquivo
claudiofsr Mar 3, 2020
fac4899
imprimir csv sem adicionar os nomes da colunas
claudiofsr Mar 3, 2020
5575c9b
unificar csv e imprimir um arquivo final xlsx
claudiofsr Mar 3, 2020
3adef0e
alteração nas variáveis ini e fim
claudiofsr Mar 3, 2020
7f80f06
open file with errors='ignore'
Mar 3, 2020
70c2a08
[ADD] make_target_name
claudiofsr Mar 4, 2020
3d6ce6c
mensagem doc alterada
claudiofsr Mar 4, 2020
1de1934
reorganizada as mensagens de execução
claudiofsr Mar 4, 2020
a1c3517
escolhido o <valor_bc> na composição de <combinação>
claudiofsr Mar 4, 2020
b2e5334
adição das colunas 'VL_PIS' e 'VL_COFINS'
claudiofsr Mar 4, 2020
3958770
EFD ICMS_IPI entradas e saídas de acordo com CFOP
claudiofsr Mar 5, 2020
1264e1e
adição de requisitos: cchardet e xlsxwriter
claudiofsr Mar 5, 2020
e4776a9
importar csv para escrever e ler os arquivos CSV
claudiofsr Mar 5, 2020
bb25788
especificações de acordo com o tipo de SPED EFD
claudiofsr Mar 5, 2020
3934ba6
Merge pull request #5 from claudiofsr/relatorios
Mar 5, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Biblioteca para geração dos arquivos do Sistema Público de Escrituração Dig

* python
* six
* cchardet
* xlsxwriter

## Como instalar

Expand Down
2 changes: 2 additions & 0 deletions requeriments.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
coveralls==0.5
six==1.9.0
pytest==2.6.4
cchardet==2.1.5
xlsxwriter=1.2.7
11 changes: 8 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from setuptools.command.test import test as test_command
from sped import __version__


class PyTest(test_command):
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]

Expand All @@ -25,7 +24,6 @@ def run_tests(self):
errno = pytest.main(self.pytest_args)
sys.exit(errno)


setup(
name='python-sped',
packages=find_packages(exclude=['contrib', 'docs', 'test*']),
Expand All @@ -50,7 +48,7 @@ def run_tests(self):
'Programming Language :: Python :: 3.6',
],
keywords='sped fiscal contábil contabilidade receita federal',
install_requires=['six'],
install_requires=['six','cchardet','xlsxwriter'],
tests_require=['pytest'],
extras_require={
'dev': ['pylint>=1.9.1'],
Expand All @@ -60,4 +58,11 @@ def run_tests(self):
]
},
cmdclass={'test': PyTest},

# https://stackoverflow.com/questions/4840182/setup-py-and-adding-file-to-bin
# scripts=['sped/relatorios/efd_relatorios'],

entry_points = {
'console_scripts': ['efd_relatorios=sped.relatorios.efd_relatorios:main']
},
)
2 changes: 1 addition & 1 deletion sped/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
from .escrituracao import Escrituracao


__version__ = '1.0.2'
__version__ = '1.0.3'
51 changes: 38 additions & 13 deletions sped/arquivos.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-


import re
from collections import OrderedDict
from io import StringIO

from .registros import RegistroIndefinido


class ArquivoDigital(object):
registros = None
blocos = None
Expand All @@ -18,30 +17,56 @@ def __init__(self):
self._registro_abertura = self.registro_abertura()
self._registro_encerramento = self.registro_encerramento()
self._blocos = OrderedDict()

def readfile(self, filename):
with open(filename) as spedfile:
for line in [line.rstrip('\r\n') for line in spedfile]:
self.read_registro(line.decode('utf8'))
self.leitura_completa = False

def readfile(self, filename, codificacao=None, verbose=None):
if codificacao is None: # 'utf-8', 'latin-1', ...
codificacao = 'utf-8'
with open(filename, 'r', encoding=codificacao, errors='ignore') as spedfile:
for line in [line.strip() for line in spedfile]:
# A simple way to remove multiple spaces in a string
line = re.sub(r'\s{2,}', ' ', line)
# Em algumas EFDs foram encontrados registros digitados incorretamente em minúsculo.
# Por exemplo, o registro 'c491' deve ser corrigido para 'C491'.
line = line[:6].upper() + line[6:] # line = '|c491|...' --> '|C491|...'
self.read_registro(line)
# Verificar se o arquivo SPED foi lido até a última linha válida que contém o registro '9999'.
if self.leitura_completa:
break
if not self.leitura_completa:
raise RuntimeError(u"\nOcorreu uma falha ao ler o arquivo: '%s'.\n" % filename)
elif verbose:
print(u"O arquivo SPED '%s' foi lido com sucesso.\n" % filename)

def read_registro(self, line):
reg_id = line.split('|')[1]

try:
registro_class = getattr(self.__class__.registros,
'Registro' + reg_id)
# https://stackoverflow.com/questions/25577578/access-class-variable-from-instance
# Devo substituir 'self.__class__.registros' por 'type(self).registros' ?
registro_class = getattr(self.__class__.registros, 'Registro' + reg_id)
except AttributeError:
raise RuntimeError(u"Arquivo inválido para EFD - PIS/COFINS")
raise RuntimeError(u"Arquivo inválido para EFD - PIS/COFINS. Registro: %s" % reg_id)

registro = registro_class(line)
bloco_id = reg_id[0]
bloco = self._blocos[bloco_id]

if registro.__class__ == self.__class__.registro_abertura:
# Atualizar o registro de abertura 0000 do SPED
self._registro_abertura = registro
elif registro.__class__ == self.__class__.registro_encerramento:
# Atualizar o registro de encerramento 9999 do SPED
self._registro_encerramento = registro
self.leitura_completa = True
elif registro.__class__ == bloco.registro_abertura.__class__:
# Atualizar os registros de abertura dos blocos: 0001, A001, C001, ...
bloco.registro_abertura = registro
elif registro.__class__ == bloco.registro_encerramento.__class__:
# Atualizar os registros de encerramento dos blocos: 0990, A990, C990, ...
bloco.registro_encerramento = registro
else:
bloco_id = reg_id[0]
bloco = self._blocos[bloco_id]
# Adicionar informações dos registros a cada linha obtida de filename
bloco.add(registro)

def write_to(self, buff):
Expand Down
7 changes: 3 additions & 4 deletions sped/blocos.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

from .registros import Registro


class Bloco(object):
def __init__(self, nome=''):
self._nome = nome
self._registros = []
#self.registro_abertura = Registro()
#self.registro_encerramento = Registro()

def __repr__(self):
return '<%s.%s(%s)>' % (self.__class__.__module__,
Expand All @@ -28,6 +29,4 @@ def registros(self):

def add(self, registro):
# Não adiciona o registro de abertura e fechamento
if not registro.__class__ == self.registro_abertura.__class__ and \
not registro.__class__ == self.registro_encerramento.__class__:
self._registros.append(registro)
self._registros.append(registro)
115 changes: 112 additions & 3 deletions sped/campos.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-


import re

from datetime import date
Expand Down Expand Up @@ -188,13 +187,23 @@ def get(self, registro):
return datetime.strptime(valor, '%d%m%Y').date()

def set(self, registro, valor):
# https://stackoverflow.com/questions/19887353/attributeerror-str-object-has-no-attribute-strftime
valor = datetime.strptime(valor, '%d%m%Y')
if isinstance(valor, date):
super().set(registro, valor.strftime('%d%m%Y'))
elif not valor:
super().set(registro, None)
else:
raise FormatoInvalidoError(registro, self.nome)

@staticmethod
def formatar(data_in):
dt = datetime.strptime(data_in, "%d%m%Y") # ddmmaaaa
#data_out = dt.isoformat('T')
#data_out = dt.strftime('%x %X') # excel date format
data_out = dt.strftime("%d/%m/%Y")
return data_out


class CampoRegex(Campo):
def __init__(self, indice, nome, obrigatorio=False, regex=None):
Expand All @@ -216,7 +225,11 @@ def set(self, registro, valor):
class CampoCNPJ(Campo):
@staticmethod
def validar(valor):
if len(valor) != 14:
# valor = '53.939.351/0001-29'
# remover os caracteres não dígitos (\D)
valor = re.sub(r'\D', '', valor)

if not re.search(r'^\d{14}$', str(valor)):
return False

multiplicadores = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
Expand All @@ -239,11 +252,26 @@ def validar(valor):

return True

@staticmethod
def formatar(cnpj):
cnpj = re.sub(r'\D', '', cnpj)
mensagem_de_validacao = ''
if len(cnpj) >= 1:
if not CampoCNPJ.validar(cnpj):
mensagem_de_validacao = ' : dígito verificador do cnpj inválido!'
if len(cnpj) == 14:
cnpj = "%s.%s.%s/%s-%s" % (cnpj[0:2],cnpj[2:5],cnpj[5:8],cnpj[8:12],cnpj[12:14])
return cnpj + mensagem_de_validacao


class CampoCPF(Campo):
@staticmethod
def validar(valor):
if len(valor) != 11:
# valor = '333.333.333-33'
# remover os caracteres não dígitos (\D)
valor = re.sub(r'\D', '', valor)

if not re.search(r'^\d{11}$', str(valor)):
return False

multiplicadores = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2]
Expand All @@ -265,13 +293,94 @@ def validar(valor):
return False

return True

@staticmethod
def formatar(cpf):
cpf = re.sub(r'\D', '', cpf)
mensagem_de_validacao = ''
if len(cpf) >= 1:
if not CampoCPF.validar(cpf):
mensagem_de_validacao = ' : dígito verificador do cpf inválido!'
if len(cpf) == 11:
cpf = "%s.%s.%s-%s" % (cpf[0:3],cpf[3:6],cpf[6:9],cpf[9:11])
return cpf + mensagem_de_validacao


class CampoCPFouCNPJ(Campo):
@staticmethod
def validar(valor):
# remover os caracteres não dígitos (\D)
valor = re.sub(r'\D', '', valor)

if len(valor) == 14:
return CampoCNPJ.validar(valor)
if len(valor) == 11:
return CampoCPF.validar(valor)
return False

@staticmethod
def formatar(digt):
digt = re.sub(r'\D', '', digt)
mensagem_de_validacao = ''
if len(digt) >= 1:
if len(digt) == 11 and not CampoCPF.validar(digt):
mensagem_de_validacao = ' : dígito verificador do cpf inválido!'
elif len(digt) == 14 and not CampoCNPJ.validar(digt):
mensagem_de_validacao = ' : dígito verificador do cnpj inválido!'

if len(digt) == 11:
digt = "CPF %s.%s.%s-%s" % (digt[0:3],digt[3:6],digt[6:9],digt[9:11])
elif len(digt) == 14:
digt = "CNPJ %s.%s.%s/%s-%s" % (digt[0:2],digt[2:5],digt[5:8],digt[8:12],digt[12:14])
return digt + mensagem_de_validacao


# Fonte: 'NFe Manual_de_Orientacao_Contribuinte_v_6.00.pdf', pg 144.
# 5.4 Cálculo do Dígito Verificador da Chave de Acesso da NF-e
class CampoChaveEletronica(Campo):
@staticmethod
def validar(valor):
# remover os caracteres não dígitos (\D)
valor = re.sub(r'\D', '', valor)

if not re.search(r'^\d{44}$', str(valor)):
return False

chave = [int(digito) for digito in valor]
multiplicadores = [4, 3, 2] + [9, 8, 7, 6, 5, 4, 3, 2] * 5 + [0]

soma = sum([chave[i] * multiplicadores[i] for i in range(44)])

resto_da_divisao = soma % 11
digito_verificador = 11 - resto_da_divisao

if digito_verificador >= 10:
digito_verificador = 0

if chave[-1] != digito_verificador:
return False

# dentro da chave eletrônica há o CNPJ do emitente
# que também será verificado
cnpj = str(valor)[6:20]

return CampoCNPJ.validar(cnpj)

@staticmethod
def formatar(chave):
chave = re.sub(r'\D', '', chave)
mensagem_de_validacao = ''
if len(chave) >= 1:
if not CampoChaveEletronica.validar(chave):
mensagem_de_validacao = ' : dígito verificador da chave inválido!'
if len(chave) == 44:
chave = "%s.%s.%s.%s.%s.%s.%s.%s-%s" % (chave[0:2],chave[2:6],chave[6:20],chave[20:22],chave[22:25],chave[25:34],chave[34:35],chave[35:43],chave[43:44])
return chave + mensagem_de_validacao


class CampoNCM(Campo):
@staticmethod
def formatar(ncm):
if len(ncm) == 8:
ncm = "%s.%s.%s" % (ncm[0:4],ncm[4:6],ncm[6:8])
return ncm
8 changes: 4 additions & 4 deletions sped/efd/icms_ipi/blocos.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class Bloco0(Bloco):
registro_encerramento = Registro0990()

@property
def fechamento(self):
registro = self.__class__.registro_encerramento()
def encerramento(self):
registro = self.__class__.registro_encerramento
# Define a quantidade de registros
registro[2] = len(self._registros) + 3
return registro
Expand Down Expand Up @@ -110,8 +110,8 @@ class Bloco9(Bloco):
registro_encerramento = Registro9990()

@property
def fechamento(self):
registro = self.__class__.registro_encerramento()
def encerramento(self):
registro = self.__class__.registro_encerramento
# Define a quantidade de registros
registro[2] = len(self._registros) + 3
return registro
Loading