sped2sql 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ *.gem
3
+ .rvmrc
4
+ .bundle/
5
+ .rspec
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ group :development, :test do
5
+ gem "codeclimate-test-reporter", require: nil
6
+ end
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
@@ -0,0 +1,3 @@
1
+ require 'rspec/core/rake_task'
2
+ task :default => :spec
3
+ RSpec::Core::RakeTask.new
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,10 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module SPED2SQL
3
+ module Formatters
4
+ class GeraHash
5
+ def self.get_hash
6
+ SecureRandom.uuid
7
+ end
8
+ end
9
+ end
10
+ 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,13 @@
1
+ module SPED2SQL
2
+ module Pipeline
3
+ module AddHash
4
+ include Formatters
5
+
6
+ def self.call(env)
7
+ env[:final].push(GeraHash.get_hash)
8
+ env
9
+ end
10
+
11
+ end
12
+ end
13
+ 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