sped2sql 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.md +24 -0
- data/README.md +106 -0
- data/Rakefile +3 -0
- data/lib/sped2sql.rb +11 -0
- data/lib/sped2sql/conversor.rb +68 -0
- data/lib/sped2sql/formatters/gera_hash.rb +10 -0
- data/lib/sped2sql/formatters/string_converter.rb +36 -0
- data/lib/sped2sql/layout/mapa.rb +49 -0
- data/lib/sped2sql/pipeline/add_hash.rb +13 -0
- data/lib/sped2sql/pipeline/base.rb +29 -0
- data/lib/sped2sql/pipeline/normaliza_sql.rb +16 -0
- data/lib/sped2sql/sql/parser.rb +68 -0
- data/lib/sped2sql/version.rb +5 -0
- data/spec/resources/csv.txt +1 -0
- data/spec/resources/mapa.txt +3 -0
- data/spec/resources/sped.txt +3 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/sped2sql/conversor_spec.rb +106 -0
- data/spec/sped2sql/formatters/gera_hash_spec.rb +25 -0
- data/spec/sped2sql/formatters/string_converter_spec.rb +47 -0
- data/spec/sped2sql/layout/mapa_spec.rb +40 -0
- data/spec/sped2sql/pipeline/add_hash_spec.rb +33 -0
- data/spec/sped2sql/pipeline/base_spec.rb +54 -0
- data/spec/sped2sql/pipeline/normaliza_sql_spec.rb +28 -0
- data/spec/sped2sql/sql/parser_spec.rb +116 -0
- data/sped2sql.gemspec +26 -0
- data/templates/efd_icms_ipi.txt +186 -0
- data/travis.yml +12 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 95c686634750ae95c0c53e640e3f828546dc3853
|
4
|
+
data.tar.gz: 9ca4fd15d9da0dcba6bb94b4105dc852dedb7bc2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e13b2d57d99fc60afefe148de62c42085a51455c4999bfcc7a4979c981476999d9785e843c688a7337a7c87b391af8659599e6ce14c4dd034640fdd48b0083f9
|
7
|
+
data.tar.gz: d2fa4d85a9036ebf94d6f79e975611c245717cc2c2dcc234002408ebddc32dfd0c610bc908d480e2a3493db83cec7919dff006d8d8d934279b56598f388fc8a9
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
sped2sql (1.0.4)
|
5
|
+
rake (~> 10.3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
codeclimate-test-reporter (0.4.0)
|
11
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
12
|
+
diff-lcs (1.2.5)
|
13
|
+
docile (1.1.5)
|
14
|
+
multi_json (1.10.1)
|
15
|
+
rake (10.3.2)
|
16
|
+
rspec (3.0.0)
|
17
|
+
rspec-core (~> 3.0.0)
|
18
|
+
rspec-expectations (~> 3.0.0)
|
19
|
+
rspec-mocks (~> 3.0.0)
|
20
|
+
rspec-core (3.0.4)
|
21
|
+
rspec-support (~> 3.0.0)
|
22
|
+
rspec-expectations (3.0.4)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.0.0)
|
25
|
+
rspec-mocks (3.0.4)
|
26
|
+
rspec-support (~> 3.0.0)
|
27
|
+
rspec-support (3.0.4)
|
28
|
+
simplecov (0.9.0)
|
29
|
+
docile (~> 1.1.0)
|
30
|
+
multi_json
|
31
|
+
simplecov-html (~> 0.8.0)
|
32
|
+
simplecov-html (0.8.0)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
codeclimate-test-reporter
|
39
|
+
rspec (~> 3.0)
|
40
|
+
sped2sql!
|
data/LICENSE.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2014 Josué Lima ([@josuelima](https://github.com/josuelima))
|
3
|
+
==========================================================================
|
4
|
+
|
5
|
+
The "SPED2SQL" RubyGem is released under the **MIT LICENSE**
|
6
|
+
------------------------------------------------------------
|
7
|
+
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
10
|
+
in the Software without restriction, including without limitation the rights
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
13
|
+
furnished to do so, subject to the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be included in
|
16
|
+
all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
THE SOFTWARE.s
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# SPED2SQL
|
2
|
+
|
3
|
+
[![Code Climate](https://codeclimate.com/github/josuelima/sped2sql.png)](https://codeclimate.com/github/josuelima/sped2sql)
|
4
|
+
[![Build Status](https://travis-ci.org/josuelima/sped2sql.svg?branch=master)](https://travis-ci.org/josuelima/sped2sql)
|
5
|
+
[![Test Coverage](https://codeclimate.com/github/josuelima/sped2sql/badges/coverage.svg)](https://codeclimate.com/github/josuelima/sped2sql)
|
6
|
+
|
7
|
+
** SPED2SQL is a RubyGem to convert SPED tax data files into SQL. SPED2SQL reads the SPED file and matches it against a template in order to convert the data into its corresponding SQL format. You can also make it extensible by creating as many custom parsers as you want and attach it to the reading process **
|
8
|
+
|
9
|
+
SPED2SQL é uma RubyGem que converte arquivos SPED para arquivos SQL prontos para serem inseridos no banco de dados (caso precise criar as tabelas do SPED, de uma olhada nesse outro repositório: [SPED Schema](https://github.com/josuelima/sped_schema)).
|
10
|
+
|
11
|
+
Atualmente apenas o template para a EFD ICMS IPI está disponibilizado, porém você pode implementar e customizar o seu próprio template (e se possível enviar de volta como contribuição).
|
12
|
+
|
13
|
+
## Instalação
|
14
|
+
```ruby
|
15
|
+
gem install sped2sql
|
16
|
+
```
|
17
|
+
|
18
|
+
## Uso
|
19
|
+
|
20
|
+
### Convertendo um arquivo EFD ICMS IPI para SQL
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'sped2sql'
|
24
|
+
|
25
|
+
conversor = SPED2SQL::Conversor.new(caminho_arquivo_sped, :efd_icms_ipi)
|
26
|
+
conversor.converter!
|
27
|
+
|
28
|
+
# Salva o SQL em um arquivo texto
|
29
|
+
IO.write('caminho_destino_sql.sql', conversor.to_sql)
|
30
|
+
```
|
31
|
+
Verifique os SPECS para outros exemplos e o retorno SQL
|
32
|
+
|
33
|
+
|
34
|
+
### Convertendo um arquivo SPED utilizando um template próprio
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require 'sped2sql'
|
38
|
+
|
39
|
+
conversor = SPED2SQL::Conversor.new(caminho_arquivo_sped, caminho_template_txt)
|
40
|
+
conversor.converter!
|
41
|
+
|
42
|
+
# Salva o SQL em um arquivo texto
|
43
|
+
IO.write('caminho_destino_sql.sql', conversor.to_sql)
|
44
|
+
```
|
45
|
+
Veja um exemplo de [template](https://github.com/josuelima/sped2sql/blob/master/templates/efd_icms_ipi.txt)
|
46
|
+
|
47
|
+
|
48
|
+
### Parsers
|
49
|
+
|
50
|
+
Por padrão duas operações (tasks) são executas durante a leitura de cada linha do SPED. São elas:
|
51
|
+
* [NormalizaSQL](https://github.com/josuelima/sped2sql/blob/master/lib/sped2sql/pipeline/normaliza_sql.rb): Converte cada campo para o respectivo tipo de dado informado no template
|
52
|
+
* [AddHash](https://github.com/josuelima/sped2sql/blob/master/lib/sped2sql/pipeline/add_hash.rb): Adiciona um identificador único para cada registro
|
53
|
+
|
54
|
+
Para não utiliza-las, basta apenas instanciar o conversor explicitando as tasks vazias:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
conversor = SPED2SQL::Conversor.new(arquivo_sped, arquivo_mapa, {tasks: :vazio})
|
58
|
+
```
|
59
|
+
|
60
|
+
|
61
|
+
### Parsers customizados
|
62
|
+
|
63
|
+
É possível utilizar seus próprios parsers para serem executados como tasks a cada leitura de linha.
|
64
|
+
Basta apenas que ele responda ao metodo call recebendo como argumento um hash contendo:
|
65
|
+
|
66
|
+
* :original => linha original que está sendo lida no momento
|
67
|
+
* :final => linha com as modificações feitas por outros parsers até o momento
|
68
|
+
* :mapa => template mapeando o tipo de dado para cada registro do sped
|
69
|
+
* :memoria => último registro lido de cada tabela
|
70
|
+
* :saida => acumulador das leituras feitas até momento
|
71
|
+
* :options => Opções enviadas na instaciação do conversor
|
72
|
+
|
73
|
+
E retorne o mesmo hash como a key final modificada com suas alterações (apenas modificações nessa key serão levadas em conta).
|
74
|
+
|
75
|
+
Neste exemplo, um simples parser que adiciona a hora atual na última coluna da linha lida:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
module MyParser
|
79
|
+
def self.call(env)
|
80
|
+
env[:final].push(Time.now)
|
81
|
+
env
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
Para utilizar apenas este parser na leitura do seu arquivo:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
conversor = SPED2SQL::Conversor.new(arquivo_sped, arquivo_mapa, {tasks: [MyParser]})
|
90
|
+
```
|
91
|
+
|
92
|
+
Para utilizar os parsers default e acrescentar o seu ao final:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
conversor = SPED2SQL::Conversor.new(arquivo_sped, arquivo_mapa)
|
96
|
+
conversor << MyParser
|
97
|
+
```
|
98
|
+
|
99
|
+
## Contribuindo
|
100
|
+
|
101
|
+
Contribuições são bem vindas. Você pode contribuir de diversas maneiras:
|
102
|
+
|
103
|
+
* Procurando e reportando erros ([faça isso aqui](https://github.com/josuelima/sped2sql/issues))
|
104
|
+
* Corrigindo erros e enviando as correções (Fork o projeto e envie um Pull Request)
|
105
|
+
* Criando novos templates
|
106
|
+
* Criando parsers úteis de propósito geral
|
data/Rakefile
ADDED
data/lib/sped2sql.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'csv'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'sped2sql/formatters/string_converter'
|
5
|
+
require 'sped2sql/formatters/gera_hash'
|
6
|
+
require 'sped2sql/pipeline/base'
|
7
|
+
require 'sped2sql/pipeline/normaliza_sql'
|
8
|
+
require 'sped2sql/pipeline/add_hash'
|
9
|
+
require 'sped2sql/sql/parser'
|
10
|
+
require 'sped2sql/layout/mapa'
|
11
|
+
require 'sped2sql/conversor'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module SPED2SQL
|
3
|
+
class Conversor < Pipeline::Base
|
4
|
+
include Layout
|
5
|
+
|
6
|
+
attr_reader :fonte, :template, :saida, :memoria, :options
|
7
|
+
|
8
|
+
def initialize(fonte, template, options = {})
|
9
|
+
@fonte = fonte
|
10
|
+
@template = template.is_a?(Symbol) ? Mapa.arquivo_template(template) : template
|
11
|
+
@saida = []
|
12
|
+
@memoria = Hash.new { |k, v| k[v] = [] }
|
13
|
+
@options = options
|
14
|
+
|
15
|
+
valida_arquivo(@fonte)
|
16
|
+
valida_arquivo(@template)
|
17
|
+
|
18
|
+
tasks = if options[:tasks].is_a?(Array)
|
19
|
+
options[:tasks]
|
20
|
+
elsif options[:tasks] == :vazio
|
21
|
+
[]
|
22
|
+
else
|
23
|
+
[Pipeline::NormalizaSQL, Pipeline::AddHash]
|
24
|
+
end
|
25
|
+
|
26
|
+
super(tasks)
|
27
|
+
end
|
28
|
+
|
29
|
+
def converter!
|
30
|
+
mapa = Mapa.carrega!(@template)
|
31
|
+
dados = IO.read(fonte, encoding: 'ISO-8859-1').gsub("'", '"')
|
32
|
+
|
33
|
+
CSV.parse(dados, col_sep: '|', quote_char: "'") do |row|
|
34
|
+
# pula linha se o registro nao existe no mapa
|
35
|
+
next unless mapa.has_key?(row[1])
|
36
|
+
|
37
|
+
# O primeiro e o ultimo item de uma linha no SPED sempre eh nulo
|
38
|
+
linha = row.clone[1..-2]
|
39
|
+
|
40
|
+
# Executa o pipe
|
41
|
+
pipe = execute({ original: linha,
|
42
|
+
final: linha,
|
43
|
+
mapa: mapa,
|
44
|
+
memoria: @memoria,
|
45
|
+
saida: @saida,
|
46
|
+
options: @options })
|
47
|
+
|
48
|
+
@saida << pipe[:final]
|
49
|
+
@memoria[linha.first] << pipe[:final]
|
50
|
+
|
51
|
+
# Para um arquivo completo do SPED, 9999 eh o ultimo registro.
|
52
|
+
# termina a leitura do arquivo no registro 9999 evitando ler
|
53
|
+
# linhas em branco ou assinatura digital
|
54
|
+
break if linha[0].to_i == 9999
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_sql
|
59
|
+
SQL::Parser.to_sql(@saida, @options[:db] || {})
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def valida_arquivo(file)
|
65
|
+
fail(ArgumentError, "Arquivo inexistente: #{file}") unless File.exist?(file)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module SPED2SQL
|
3
|
+
module Formatters
|
4
|
+
class StringConverter
|
5
|
+
class << self
|
6
|
+
def converter(subject, tipo)
|
7
|
+
return '' unless valid_subject?(subject)
|
8
|
+
send(tipo, subject)
|
9
|
+
end
|
10
|
+
|
11
|
+
def string(subject)
|
12
|
+
subject.gsub(/['"\\\x0]/, '\\\\\0')
|
13
|
+
end
|
14
|
+
|
15
|
+
def date(subject)
|
16
|
+
"#{subject[4..7]}-#{subject[2..3]}-#{subject[0..1]}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def decimal(subject)
|
20
|
+
# O formato para decimal no SPED eh sempre #.###,##
|
21
|
+
subject.gsub(/\./, '').gsub(/,/, '.')
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def method_missing(_, *dados)
|
27
|
+
dados[0]
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid_subject?(subject)
|
31
|
+
!(subject.nil? || subject == '')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module SPED2SQL
|
3
|
+
module Layout
|
4
|
+
TEMPLATE_PATH = File.expand_path('../../../../templates', __FILE__)
|
5
|
+
|
6
|
+
class Mapa
|
7
|
+
##
|
8
|
+
# Carrega um mapa (arquivo csv) para um hash
|
9
|
+
#
|
10
|
+
# Esse mapa define o tipo de dado em cada campo de
|
11
|
+
# um registro do SPED e eh utilizado para fazer a
|
12
|
+
# normalizacao do dado vindo do arquivo SPED na hora
|
13
|
+
# de inserir no banco de dados
|
14
|
+
#
|
15
|
+
# Exemplo de um mapa:
|
16
|
+
#
|
17
|
+
# - Para um arquivo SPED:
|
18
|
+
# |0001|Dados aleatorios|Dados aleatorios|
|
19
|
+
# |0001|Dados do Fornecedor|01012014|1150,00|
|
20
|
+
#
|
21
|
+
# - O mapa deveria ser:
|
22
|
+
# |0001|string|string|string|
|
23
|
+
# |0175|string|string|date|decimal|
|
24
|
+
#
|
25
|
+
# O hash final sera:
|
26
|
+
# { "0001" => [:string, :string], "0175" => [:string, :date, :decimal] }
|
27
|
+
|
28
|
+
def self.carrega!(fonte)
|
29
|
+
mapa = {}
|
30
|
+
|
31
|
+
CSV.foreach(fonte, col_sep: '|') do |row|
|
32
|
+
linha = row.clone.compact
|
33
|
+
mapa[linha[0]] = linha[1..-1].map(&:to_sym)
|
34
|
+
end
|
35
|
+
|
36
|
+
mapa
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.arquivo_template(tipo)
|
40
|
+
case tipo
|
41
|
+
when :efd_icms_ipi
|
42
|
+
File.join(TEMPLATE_PATH, 'efd_icms_ipi.txt')
|
43
|
+
else
|
44
|
+
fail(ArgumentError, "Template inexistente: #{tipo}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Simples class para adicionar tarefas a serem executadas
|
4
|
+
# na leitura de cada linha do arquivo SPED
|
5
|
+
#
|
6
|
+
# Adicione qualquer modulo ou classe que responda a call
|
7
|
+
# recebendo como argumento um hash com a linha atual e a
|
8
|
+
# memoria e que devolva os mesmo no retorno.
|
9
|
+
module SPED2SQL
|
10
|
+
module Pipeline
|
11
|
+
class Base
|
12
|
+
attr_accessor :tasks
|
13
|
+
|
14
|
+
def initialize(tasks = [])
|
15
|
+
@tasks = tasks
|
16
|
+
end
|
17
|
+
|
18
|
+
def <<(task)
|
19
|
+
@tasks << task
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute(env)
|
24
|
+
@tasks.each { |t| env = t.call(env) }
|
25
|
+
env
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module SPED2SQL
|
3
|
+
module Pipeline
|
4
|
+
module NormalizaSQL
|
5
|
+
include Formatters
|
6
|
+
|
7
|
+
def self.call(env)
|
8
|
+
mapa = env[:mapa][env[:original].first]
|
9
|
+
env[:final] = env[:original].zip(mapa).map do |dado, tipo|
|
10
|
+
StringConverter.converter(dado, tipo)
|
11
|
+
end
|
12
|
+
env
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|