abank 0.2.2 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc8d38e615af3a1ef8965fdf7e395f93079b714028308ea85318e9d92b8df5ff
4
- data.tar.gz: 40d941211ef1608af9fff0f1a9642ea69240933e1956578dcf20c0e8826f2cc0
3
+ metadata.gz: cc1db6b7872fbccb5da608191caea95a6c79e598b40dc86f64b4c64597a57a2a
4
+ data.tar.gz: c021d951d7cfb4c83413a72c097bdf7958e75ae4c886277da3848a3e88b97242
5
5
  SHA512:
6
- metadata.gz: b22caac0a7226316674b00ceecf45be10f0af39171be63df18135de736286294707c4e34d688ebd3dc150f7749f7db34619a280daaf084983a7cf910f3338290
7
- data.tar.gz: d5cf90b7b2625a59ad60ddcf36b902f926b27d54292067ae5b0e0e5a0c720ce14228339d18d471d27cb0e5eea8b80ce9138630bcd477521d483168ec8ff52e5b
6
+ metadata.gz: 859152f519b2517cfbbbe36635f92b66ff648e8ebf24fe9bb041d40e2d1f953094d75d94cca4f198ee90df68ad40abd9ceeb24d10b06ebafc2eb7a00ee00da69
7
+ data.tar.gz: 8f2fdb535d5edd44e6c3a8a959475de72bb8760685509317a2194d6e2e4f56833ecfd0891d850ad6647c1db2fd46d7b90b405f5975a4a29a7cdc7b16eb2d12bc
@@ -0,0 +1,30 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ EnabledByDefault: true
4
+
5
+ Style/Copyright:
6
+ Enabled: false
7
+
8
+ Style/DocumentationMethod:
9
+ Enabled: false
10
+
11
+ Style/MethodCallWithArgsParentheses:
12
+ Enabled: false
13
+
14
+ Style/ConstantVisibility:
15
+ Enabled: false
16
+
17
+ Layout/MultilineMethodArgumentLineBreaks:
18
+ Enabled: false
19
+
20
+ Layout/MultilineHashKeyLineBreaks:
21
+ Enabled: false
22
+
23
+ Layout/FirstMethodParameterLineBreak:
24
+ Enabled: false
25
+
26
+ Layout/FirstHashElementLineBreak:
27
+ Enabled: false
28
+
29
+ Style/MissingElse:
30
+ Enabled: false
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in abank.gemspec
4
6
  gemspec
5
7
 
6
- gem "rake", "~> 12.0"
8
+ gem 'rake', '~> 12.0'
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- abank (0.2.2)
4
+ abank (0.2.7)
5
5
  google-cloud-bigquery
6
6
  roo
7
7
  thor
@@ -17,7 +17,7 @@ GEM
17
17
  declarative-option (0.1.0)
18
18
  faraday (1.0.1)
19
19
  multipart-post (>= 1.2, < 3)
20
- google-api-client (0.39.3)
20
+ google-api-client (0.40.1)
21
21
  addressable (~> 2.5, >= 2.5.1)
22
22
  googleauth (~> 0.9)
23
23
  httpclient (>= 2.8.1, < 3.0)
@@ -25,7 +25,7 @@ GEM
25
25
  representable (~> 3.0)
26
26
  retriable (>= 2.0, < 4.0)
27
27
  signet (~> 0.12)
28
- google-cloud-bigquery (1.21.0)
28
+ google-cloud-bigquery (1.21.1)
29
29
  concurrent-ruby (~> 1.0)
30
30
  google-api-client (~> 0.33)
31
31
  google-cloud-core (~> 1.2)
@@ -34,9 +34,9 @@ GEM
34
34
  google-cloud-core (1.5.0)
35
35
  google-cloud-env (~> 1.0)
36
36
  google-cloud-errors (~> 1.0)
37
- google-cloud-env (1.3.1)
37
+ google-cloud-env (1.3.2)
38
38
  faraday (>= 0.17.3, < 2.0)
39
- google-cloud-errors (1.0.0)
39
+ google-cloud-errors (1.0.1)
40
40
  googleauth (0.12.0)
41
41
  faraday (>= 0.17.3, < 2.0)
42
42
  jwt (>= 1.4, < 3.0)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Abank [![Build Status](https://travis-ci.com/hernanirvaz/abank.svg?branch=master)](https://travis-ci.com/hernanirvaz/abank)
2
2
 
3
- Arquiva movimentos conta-corrente, conta-cartao do activobank no bigquery. Permite apagar movimentos similares/existentes ja no bigquery. Permite ainda classificar movimentos ja no bigquery.
3
+ Arquiva movimentos conta-corrente, conta-cartao do activobank no bigquery. Permite apagar/recriar movimentos/rendas ja no bigquery. Permite ainda classificar movimentos no bigquery.
4
4
 
5
5
  ## Installation
6
6
 
@@ -20,10 +20,14 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- $ abank help [COMANDO] # ajuda aos comandos
24
- $ abank mostra # mostra dados da folha calculo
25
- $ abank load # carrega dados da folha calculo no bigquery
26
- $ abank classifica # classifica arquivo no bigquery
23
+ $ abank apagamv -k=KEY[,KEY...] # apaga movimentos
24
+ $ abank apagact -c=CONTRATO # apaga contrato arrendamento
25
+ $ abank criact -c=CONTRATO # cria contrato arrendamento
26
+ $ abank recriact -c=CONTRATO # atualiza rendas de contrato arrendamento
27
+ $ abank recriare # atualiza rendas dos contratos ativos
28
+ $ abank load # carrega dados da folha calculo
29
+ $ abank show # mostra dados da folha calculo
30
+ $ abank tag # classifica movimentos
27
31
 
28
32
  ## Development
29
33
 
data/Rakefile CHANGED
@@ -1,2 +1,4 @@
1
- require "bundler/gem_tasks"
2
- task :default => :build
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ task default: :build
@@ -10,12 +10,9 @@ Gem::Specification.new do |spec|
10
10
  spec.homepage = 'https://github.com/hernanirvaz/abank'
11
11
  spec.license = 'MIT'
12
12
 
13
- spec.summary = 'Arquiva <conta-corrente>.xlsx,' \
14
- ' <conta-cartao>.xlsx no bigquery.'
15
- spec.description = spec.summary +
16
- ' Pode apagar movimentos similares/existentes' \
17
- ' ja no bigquery.' \
18
- ' Pode ainda classificar movimentos ja no bigquery.'
13
+ spec.summary = 'Arquiva movimentos conta-corrente, conta-cartao do activobank no bigquery.'
14
+ spec.description = spec.summary + ' Permite apagar/recriar movimentos/rendas ja no bigquery. ' \
15
+ ' Permite ainda classificar movimentos no bigquery.'
19
16
 
20
17
  spec.metadata['homepage_uri'] = spec.homepage
21
18
  spec.metadata['yard.run'] = 'yard'
@@ -24,10 +21,10 @@ Gem::Specification.new do |spec|
24
21
 
25
22
  # Specify which files should be added to the gem when it is released.
26
23
  # The `git ls-files -z` loads files in RubyGem that have been added into git.
27
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
- `git ls-files -z`.split("\x0")
29
- .reject { |f| f.match(%r{^(test|spec|features)/}) }
30
- end
24
+ spec.files =
25
+ Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
31
28
  spec.bindir = 'exe'
32
29
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
30
  spec.require_paths = ['lib']
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "abank"
4
+ require 'bundler/setup'
5
+ require 'abank'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "abank"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
@@ -1,64 +1,94 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
- require 'abank/bigquery'
5
- require 'abank/folhacalculo'
4
+ require 'abank/big'
5
+ require 'abank/contrato'
6
+ require 'abank/rendas'
7
+ require 'abank/folha'
6
8
  require 'abank/version'
7
9
 
8
10
  # @author Hernani Rodrigues Vaz
9
11
  module Abank
10
- ID = `whoami`.chomp
12
+ DR = "/home/#{`whoami`.chomp}/Downloads"
11
13
 
12
14
  class Error < StandardError; end
13
15
 
14
16
  # CLI para carregar folhas calculo comuns no bigquery
15
17
  class CLI < Thor
16
- desc 'load', 'carrega dados da folha calculo no bigquery'
17
- option :d, banner: 'DIR', default: "/home/#{ID}/Downloads",
18
- desc: 'Onde procurar folhas calculo'
19
- option :x, banner: 'EXT', default: '.xlsx',
20
- desc: 'Extensao das folhas calculo'
21
- option :n, banner: 'NUM', type: :numeric, default: 0,
22
- desc: 'Correcao dias para data valor'
23
- option :s, type: :boolean, default: false,
24
- desc: 'apaga linha similar no bigquery'
25
- option :e, type: :boolean, default: false,
26
- desc: 'apaga linha igual no bigquery'
27
- option :m, type: :boolean, default: false,
28
- desc: 'apaga linhas existencia multipla no bigquery'
29
- # processa folha calculo
30
- def load
31
- Dir.glob("#{options[:d]}/*#{options[:x]}").sort.each do |f|
32
- Bigquery.new(f, load_ops).processa
33
- end
18
+ desc 'tag', 'classifica movimentos'
19
+ # classifica movimentos
20
+ def tag
21
+ Big.new.mv_classifica
34
22
  end
35
23
 
36
- desc 'mostra', 'mostra dados da folha calculo'
37
- option :d, banner: 'DIR', default: "/home/#{ID}/Downloads",
38
- desc: 'Onde procurar folhas calculo'
39
- option :x, banner: 'EXT', default: '.xlsx',
40
- desc: 'Extensao das folhas calculo'
41
- # mostra folha calculo
42
- def mostra
43
- Dir.glob("#{options[:d]}/*#{options[:x]}").sort.each do |f|
44
- Bigquery.new(f).processa
24
+ desc 'apagamv', 'apaga movimentos'
25
+ option :k, banner: 'KEY[,KEY...]', required: true, desc: 'keys movimentos a apagar'
26
+ # apaga movimentos
27
+ def apagamv
28
+ Big.new(k: options[:k]).mv_delete.mv_insert.re_work
29
+ end
30
+
31
+ desc 'criact', 'cria contrato arrendamento'
32
+ option :c, banner: 'CONTRATO', required: true, desc: 'Identificador contrato arrendamento'
33
+ option :t, type: :boolean, default: true, desc: 'cria todas as rendas?'
34
+ option :d, banner: 'DATA', default: '', desc: 'data contrato arrendamento'
35
+ # cria contrato arrendamento
36
+ def criact
37
+ Big.new(c: options[:c], t: options[:t], d: options[:d]).ct_cria
38
+ end
39
+
40
+ desc 'apagact', 'apaga contrato arrendamento'
41
+ option :c, banner: 'CONTRATO', required: true, desc: 'Identificador contrato arrendamento'
42
+ option :t, type: :boolean, default: false, desc: 'apaga todas as rendas?'
43
+ # apaga contrato arrendamento
44
+ def apagact
45
+ Big.new(c: options[:c], t: options[:t]).ct_apaga
46
+ end
47
+
48
+ desc 'recriact', 'atualiza rendas de contrato arrendamento'
49
+ option :c, banner: 'CONTRATO', required: true, desc: 'Identificador contrato arrendamento'
50
+ option :t, type: :boolean, default: false, desc: 'apaga todas as rendas?'
51
+ option :d, banner: 'DATA', default: '', desc: 'data contrato arrendamento'
52
+ # atualiza rendas de contrato arrendamento
53
+ def recriact
54
+ Big.new(c: options[:c], t: options[:t]).ct_apaga
55
+ Big.new(c: options[:c], t: true, d: options[:d]).ct_cria
56
+ end
57
+
58
+ desc 'recriare', 'atualiza rendas dos contratos ativos'
59
+ option :t, type: :boolean, default: false, desc: 'atualiza todas as rendas?'
60
+ # atualiza rendas dos contratos ativos
61
+ def recriare
62
+ Big.new(t: options[:t]).re_atualiza
63
+ end
64
+
65
+ desc 'work', 'carrega/apaga dados da folha calculo'
66
+ option :s, type: :boolean, default: false, desc: 'apaga movimento similar'
67
+ option :e, type: :boolean, default: false, desc: 'apaga movimento igual'
68
+ option :v, banner: 'DATA', default: '', desc: 'data valor para movimentos a carregar'
69
+ option :g, banner: 'TAG', default: '', desc: 'classificacao para movimentos a carregar'
70
+ # carrega/apaga dados da folha calculo
71
+ def work
72
+ Dir.glob("#{DR}/*.xlsx").sort.each do |f|
73
+ Folha.new(work_opc.merge(f: f)).processa_xls
45
74
  end
46
75
  end
47
76
 
48
- desc 'classifica', 'classifica arquivo no bigquery'
49
- # classifica arquivo no bigquery
50
- def classifica
51
- Bigquery.new.classifica
77
+ desc 'show', 'mostra dados da folha calculo'
78
+ # mostra folha calculo
79
+ def show
80
+ Dir.glob("#{DR}/*.xlsx").sort.each do |f|
81
+ Folha.new(f: f).processa_xls
82
+ end
52
83
  end
53
84
 
54
85
  no_commands do
55
- # @return [Hash] ops opcoes trabalho com linhas para load
56
- def load_ops
57
- { s: options[:s], e: options[:e],
58
- m: options[:m], i: true, n: options[:n] }
86
+ # @return [Hash] opcoes trabalho com movimentos para work
87
+ def work_opc
88
+ { s: options[:s], e: options[:e], i: true, v: options[:v], g: options[:g] }
59
89
  end
60
90
  end
61
91
 
62
- default_task :mostra
92
+ default_task :show
63
93
  end
64
94
  end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'google/cloud/bigquery'
4
+
5
+ module Abank
6
+ # @see Big
7
+ class Big
8
+ DF = '%Y-%m-%d'
9
+
10
+ # @return [Hash] opcoes trabalho
11
+ attr_reader :opcao
12
+
13
+ # @return [Google::Cloud::Bigquery] API bigquery
14
+ attr_reader :bqapi
15
+
16
+ # @return [Google::Cloud::Bigquery::QueryJob] job bigquery
17
+ attr_reader :bqjob
18
+
19
+ # @return [Google::Cloud::Bigquery::Data] resultado do select
20
+ attr_reader :bqres
21
+
22
+ # @return [Integer] numero linhas afetadas pela Data Manipulation Language (DML)
23
+ attr_reader :bqnrs
24
+
25
+ # @return [String] movimentos a inserir (values.mv)
26
+ attr_reader :mvvls
27
+
28
+ # @return [String] movimentos a apagar (keysin.mv)
29
+ attr_reader :mvkys
30
+
31
+ # acesso a base dados abank no bigquery
32
+ #
33
+ # @param [Hash] opc opcoes trabalho
34
+ # @option opc [String] :k ('') movimentos a apagar (keysin.mv)
35
+ # @option opc [String] :c ('') id contrato arrendamento (re)
36
+ # @option opc [String] :d ('') data inicio contrato arrendamento (re)
37
+ # @option opc [Boolean] :t (false) trabalha todas as rendas? (re)
38
+ # @return [Hash] opcoes trabalho
39
+ def initialize(opc = {})
40
+ @opcao = opc
41
+ @bqapi = Google::Cloud::Bigquery.new
42
+ @mvvls = ''
43
+ @mvkys = opc.fetch(:k, '')
44
+ @ctide = opc.fetch(:c, '')
45
+ # p ['B', opcao]
46
+ opcao
47
+ end
48
+
49
+ # (see CLI#tag)
50
+ def mv_classifica
51
+ dml('update hernanilr.ab.mv set mv.ct=tt.nct ' \
52
+ 'from (select * from hernanilr.ab.cl) as tt ' \
53
+ "where #{ky_mv}=tt.ky")
54
+ puts 'MOVIMENTOS CLASSIFICADOS ' + bqnrs.to_s
55
+ end
56
+
57
+ # apaga movimentos & suas rendas associadas no bigquery
58
+ #
59
+ # @return [Big] acesso a base dados abank no bigquery
60
+ def mv_delete
61
+ vars_mv_work
62
+ if mvkys.size.positive?
63
+ # obtem lista contratos arrendamento associados aos movimentos a apagar
64
+ @ctlct = sel("select ct from hernanilr.ab.mv where #{ky_mv} in(#{mvkys}) and substr(ct,1,1)='r' group by 1")
65
+
66
+ # apaga rendas associadas e depois movimentos
67
+ @opcao[:t] = true
68
+ lr_apaga.mv_delete_dml
69
+
70
+ # para obrigar re_work a trabalhar com lista contratos (ctlct)
71
+ @bqnrs = 0
72
+ end
73
+ self
74
+ end
75
+
76
+ # insere & classifica movimentos no bigquery
77
+ #
78
+ # @return [Big] acesso a base dados abank no bigquery
79
+ def mv_insert
80
+ if mvvls.size.positive?
81
+ dml('insert hernanilr.ab.mv VALUES' + mvvls)
82
+ puts 'MOVIMENTOS INSERIDOS ' + bqnrs.to_s
83
+ mv_classifica if bqnrs.positive?
84
+ end
85
+ self
86
+ end
87
+
88
+ # inicializa variaveis para delete/insert movimentos
89
+ def vars_mv_work
90
+ @bqnrs = 0
91
+ @ctlct = []
92
+ @mvkys = mvkys[1..] if mvkys[0] == ','
93
+ @mvvls = mvvls[1..] if mvvls[0] == ','
94
+ end
95
+
96
+ # apaga movimentos no bigquery
97
+ def mv_delete_dml
98
+ dml("delete from hernanilr.ab.mv where #{ky_mv} in(#{mvkys})")
99
+ puts 'MOVIMENTOS APAGADOS ' + bqnrs.to_s
100
+ end
101
+
102
+ # @return [String] expressao sql da chave de movimentos
103
+ def ky_mv
104
+ 'FARM_FINGERPRINT(CONCAT(CAST(mv.nc as STRING),mv.ds,CAST(mv.dl as STRING),CAST(mv.vl as STRING)))'
105
+ end
106
+
107
+ # cria job bigquery & verifica execucao
108
+ #
109
+ # @param [String] sql comando a executar
110
+ # @return [Boolean] job ok?
111
+ def job?(sql)
112
+ # p sql
113
+ @bqjob = bqapi.query_job(sql)
114
+ @bqjob.wait_until_done!
115
+ puts @bqjob.error['message'] if @bqjob.failed?
116
+ @bqjob.failed?
117
+ end
118
+
119
+ # executa sql & devolve resultado do bigquery
120
+ #
121
+ # @param (see job?)
122
+ # @param [Array] erro quando da erro no bigquery
123
+ # @return [Google::Cloud::Bigquery::Data] resultado do sql
124
+ def sel(sql, erro = [])
125
+ @bqres = job?(sql) ? erro : bqjob.data
126
+ end
127
+
128
+ # executa Data Manipulation Language (DML) no bigquery
129
+ #
130
+ # @param (see job?)
131
+ # @return [Integer] numero rows afetadas pelo dml
132
+ def dml(sql)
133
+ @bqnrs = job?(sql) ? 0 : bqjob.num_dml_affected_rows
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abank
4
+ # acesso a base dados abank no bigquery
5
+ class Big
6
+ # @return [String] id contrato arrendamento
7
+ attr_reader :ctide
8
+
9
+ # @return [Array<Hash>] lista ids contratos arrendamento
10
+ # @example
11
+ # [{ ct: 'r03000' }, ...]
12
+ attr_reader :ctlct
13
+
14
+ # @return [Array<Hash>] lista dados contrato arrendamento (inclui lista movimentos novos)
15
+ # @example
16
+ # [{ct: 'r03000', dc: '2020-03-01', ano: 2020, cnt: 0, dl: '2020-03-01', mv: [{dl: '2020-03-02', vl: 30}, ...] }]
17
+ attr_reader :ctlcm
18
+
19
+ # (see CLI#criact)
20
+ def ct_cria
21
+ if existe_contrato?
22
+ @bqnrs = 1
23
+ puts 'CONTRATO JA EXISTE'
24
+ else
25
+ dml('insert into hernanilr.ab.re ' + sql_contrato_mv)
26
+ puts "CONTRATO #{ctide} " + (bqnrs.zero? ? 'NAO EXISTE' : 'INSERIDO')
27
+ end
28
+ return unless existem_rendas?
29
+
30
+ # processa rendas associadas ao contrato arrendamento
31
+ cm_cria.vr_cria.re_insert
32
+ end
33
+
34
+ # (see CLI#apagact)
35
+ def ct_apaga
36
+ @ctlct = [{ ct: ctide }]
37
+ lc_apaga
38
+ end
39
+
40
+ # apaga rendas da lista de contrato arrendamento
41
+ #
42
+ # @return [Big] acesso a base dados abank no bigquery
43
+ def lr_apaga
44
+ return self unless opcao[:t] && ctlct.count.positive?
45
+
46
+ # para nao apagar contrato arrendamento - somente as rendas
47
+ @opcao[:t] = false
48
+
49
+ lc_apaga
50
+ self
51
+ end
52
+
53
+ # apaga rendas da lista de contratos arrendamento
54
+ def lc_apaga
55
+ dml("delete from hernanilr.ab.re where ct in(#{str_lc})#{opcao[:t] ? '' : ' and cnt>0'}")
56
+ puts "RENDAS #{str_lc('')} APAGADAS " + bqnrs.to_s
57
+ end
58
+
59
+ # @return [String] texto formatado que representa lista de contratos arrendamento
60
+ def str_lc(sep = "'")
61
+ ctlct.map { |c| sep + c[:ct] + sep }.join(',')
62
+ end
63
+
64
+ # optem lista dados contrato arrendamento (inclui lista movimentos novos)
65
+ #
66
+ # @return [Big] acesso a base dados abank no bigquery
67
+ def cm_cria
68
+ @ctlcm = []
69
+ ctlct.each do |c|
70
+ @ctide = c[:ct]
71
+ sel(sql_last_re)
72
+ @ctlcm << bqres[0].merge({ mv: sel(sql_novo_mv(bqres[0][:dl])) })
73
+ end
74
+ self
75
+ end
76
+
77
+ # @return [Boolean] existem rendas para processar sim/nao?
78
+ def existem_rendas?
79
+ @ctlct = [{ ct: ctide }]
80
+ bqnrs.positive? && opcao[:t]
81
+ end
82
+
83
+ # @return [Boolean] contrato arrendamento ja existe sim/nao?
84
+ def existe_contrato?
85
+ sel("select ct from hernanilr.ab.re where ct='#{ctide}' and cnt=0").count.positive?
86
+ end
87
+
88
+ # @return [String] sql para obter ultima renda do contrato arrendamento
89
+ def sql_last_re
90
+ 'select ct,DATE_SUB(DATE_SUB(dl,INTERVAL dias DAY),INTERVAL IF(cnt=0,0,cnt-1) MONTH) as dc,ano,cnt,dl ' \
91
+ "from hernanilr.ab.re where ct='#{ctide}' order by ano desc,cnt desc limit 1"
92
+ end
93
+
94
+ # @return [String] sql para obter movimentos novos (depois da ultima renda do contrato arrendamento)
95
+ def sql_novo_mv(mdl)
96
+ "select dl,vl from hernanilr.ab.mv where ct='#{ctide}' and dl>='#{(mdl + 1).strftime(DF)}' order by dl,dv"
97
+ end
98
+
99
+ # @return [String] sql para obter dados do inicio contrato arrendamento
100
+ def sql_contrato_mv
101
+ if opcao[:d].size.zero?
102
+ 'select ct,EXTRACT(YEAR FROM DATE_TRUNC(dl,MONTH)) as ano,0 as cnt,DATE_TRUNC(dl,MONTH) as dl,0 dias ' \
103
+ "from hernanilr.ab.mv where ct='#{ctide}' order by dl limit 1"
104
+ else
105
+ "select '#{ctide}' as ct,EXTRACT(YEAR FROM DATE '#{opcao[:d]}') as ano" \
106
+ ",0 as cnt,DATE '#{opcao[:d]}' as dl,0 dias"
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'roo'
4
+
5
+ module Abank
6
+ # acesso a folha calculo & base dados abank no bigquery
7
+ class Folha < Big
8
+ # @return [Roo::Excelx] folha calculo a processar
9
+ attr_reader :folha
10
+
11
+ # @return [Integer] numero conta associado a folha calculo
12
+ # @example
13
+ # mov*.xlsx --> 1 --> conta-corrente
14
+ # movCard*.xlsx --> 2 --> conta-cartao
15
+ attr_reader :conta
16
+
17
+ # @return [Array] row folha calculo em processamento
18
+ attr_reader :rowfc
19
+
20
+ # acesso a folha calculo & base dados abank no bigquery
21
+ #
22
+ # @param [Hash] opc opcoes trabalho
23
+ # @option opc [String] :f ('') folha calculo a processar
24
+ # @option opc [Boolean] :s (false) apaga movimento similar? (mv)
25
+ # @option opc [Boolean] :e (false) apaga movimento igual? (mv)
26
+ # @option opc [Boolean] :i (false) insere movimento novo? (mv)
27
+ # @option opc [String] :v ('') data valor movimentos (mv)
28
+ # @option opc [String] :g ('') classificacao movimentos (mv)
29
+ def initialize(opc = {})
30
+ @opcao = super
31
+ @folha = Roo::Spreadsheet.open(opc.fetch(:f))
32
+ @conta = opc.fetch(:f).match?(/card/i) ? 2 : 1
33
+ @opcao[:s] = opc.fetch(:s, false)
34
+ @opcao[:e] = opc.fetch(:e, false)
35
+ @opcao[:i] = opc.fetch(:i, false)
36
+ @opcao[:v] = opc.fetch(:v, '')
37
+ @opcao[:g] = opc.fetch(:g, '')
38
+ end
39
+
40
+ # carrega/mostra folha calculo
41
+ def processa_xls
42
+ n = 0
43
+ folha.sheet(0).parse(header_search: ['Data Lanc.', 'Data Valor', 'Descrição', 'Valor']) do |r|
44
+ n += 1
45
+ puts n == 1 ? "\n" + folha.info : processa_linha(r)
46
+ end
47
+ return unless opcao[:i]
48
+
49
+ # processa movimentos & atualiza rendas
50
+ mv_delete.mv_insert.re_work
51
+ end
52
+
53
+ # processa linha folha calculo
54
+ #
55
+ # @param [Hash] linha da folha calculo em processamento
56
+ # @return [String] texto informativo formatado da linha em processamento
57
+ def processa_linha(linha)
58
+ vars_xls(linha)
59
+ # pesquisa existencia linha folha calculo no bigquery
60
+ # array.count = 0 ==> pode carregar esta linha
61
+ # array.count = 1 ==> mais testes necessarios
62
+ # array.count > 1 ==> nao pode carregar esta linha
63
+ sel(sql_existe_mv, [{}, {}])
64
+ if linha_naoexiste? then linha_base + values_mv
65
+ elsif linha_existe? then linha_existe
66
+ elsif linha_simila? then linha_similar
67
+ else linha_multiplas
68
+ end
69
+ end
70
+
71
+ # inicializa variavel para processar linha folha calculo
72
+ #
73
+ # @param (see processa_linha)
74
+ def vars_xls(linha)
75
+ @rowfc = linha.values
76
+ @rowfc[2] = rowfc[2].strip
77
+ @rowfc[3] = -1 * rowfc[3] if conta > 1
78
+ end
79
+
80
+ # @return [String] texto base formatado para display
81
+ def linha_base
82
+ "#{rowfc[0].strftime(DF)} #{format('%<v3>-34.34s %<v4>8.2f', v3: rowfc[2], v4: rowfc[3])}"
83
+ end
84
+
85
+ # @return [String] texto linha existente formatada para display
86
+ def linha_existe
87
+ add_kys if opcao[:e]
88
+ linha_base + ' EXIS ' + format('%<v1>20d', v1: bqres.first[:ky])
89
+ end
90
+
91
+ # @return [String] texto linha similar formatada para display
92
+ def linha_similar
93
+ add_kys if opcao[:s]
94
+ linha_base + ' SIMI ' + format('%<v1>-20.20s', v1: bqres.first[:ds].strip)
95
+ end
96
+
97
+ # @return [String] texto linha existencia multipla formatada para display
98
+ def linha_multiplas
99
+ linha_base + ' MULT(' + bqres.count.to_s + ')'
100
+ end
101
+
102
+ # obtem chaves movimento (keysin.mv) para apagar
103
+ def add_kys
104
+ bqres.each { |r| @mvkys += ",#{r[:ky]}" }
105
+ end
106
+
107
+ # @return [Boolean] linha folha calculo nao existe no bigquery?
108
+ def linha_naoexiste?
109
+ bqres.count.zero?
110
+ end
111
+
112
+ # @return [Boolean] linha folha calculo existe no bigquery?
113
+ def linha_existe?
114
+ bqres.count == 1 && bqres.first[:ds].strip == rowfc[2]
115
+ end
116
+
117
+ # @return [Boolean] linha folha calculo existe parecida no bigquery?
118
+ def linha_simila?
119
+ bqres.count == 1 && bqres.first[:ds].strip != rowfc[2]
120
+ end
121
+
122
+ # @return [String] sql para movimentos no bigquery
123
+ def sql_existe_mv
124
+ "select *,#{ky_mv} as ky from hernanilr.ab.mv " \
125
+ "where nc=#{conta} and dl='#{rowfc[0].strftime(DF)}' and vl=#{rowfc[3]}"
126
+ end
127
+
128
+ # obtem movimento (values.mv) para inserir
129
+ #
130
+ # @return [String] ' NOVO'
131
+ def values_mv
132
+ @mvvls += ",('#{rowfc[0].strftime(DF)}','#{dvc.strftime(DF)}','#{rowfc[2]}',#{rowfc[3]}" + values_mv_extra
133
+ ' NOVO'
134
+ end
135
+
136
+ # @return [String] campos extra do movimento (values.mv) para inserir
137
+ def values_mv_extra
138
+ ",#{conta},#{dvc.year},#{dvc.month},'#{tpc}',#{ctc})"
139
+ end
140
+
141
+ # @return [Date] data valor corrigida
142
+ def dvc
143
+ opcao[:v].size.zero? ? rowfc[1] : Date.parse(opcao[:v])
144
+ end
145
+
146
+ # @return [String] classificacao do movimento (null --> classificacao automatica)
147
+ def ctc
148
+ opcao[:g].size.zero? ? 'null' : ("'" + opcao[:g] + "'")
149
+ end
150
+
151
+ # @return [String] tipo movimento c[redito] ou d[ebito]
152
+ def tpc
153
+ rowfc[3].positive? ? 'c' : 'd'
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abank
4
+ # @see Big
5
+ class Big
6
+ # @return [Integer] ano renda em tratamento
7
+ attr_reader :reano
8
+
9
+ # @return [Integer] mes renda em tratamento
10
+ attr_reader :repos
11
+
12
+ # @return [Float] valor renda mensal
13
+ attr_reader :revre
14
+
15
+ # @return [String] rendas a inserir (values.re)
16
+ attr_reader :revls
17
+
18
+ # @return [Integer] movimento em tratamento
19
+ attr_reader :mvpos
20
+
21
+ # @return [Date] data lancamento movimento em tratamento
22
+ attr_reader :mvdlm
23
+
24
+ # @return [Float] valor movimento em tratamento
25
+ attr_reader :mvvlm
26
+
27
+ # (see CLI#recriare)
28
+ def re_atualiza
29
+ # obtem contratos ativos
30
+ @ctlct = sel('SELECT ct from hernanilr.ab.re group by 1')
31
+
32
+ # [re]cria rendas [novas|todas]
33
+ lr_apaga.cm_cria.vr_cria.re_insert
34
+ end
35
+
36
+ # cria rendas associadas a lista ids contratos arrendamento
37
+ def re_work
38
+ bqnrs.zero? || ctlct.count.positive? ? cm_cria.vr_cria.re_insert : re_atualiza
39
+ end
40
+
41
+ # obtem rendas a inserir (values.re)
42
+ #
43
+ # @return [Big] acesso a base dados abank no bigquery
44
+ def vr_cria
45
+ @revls = ctlcm.map { |c| rendas_novas(c) }.flatten(1).join(',')
46
+ self
47
+ end
48
+
49
+ # insere rendas no bigquery
50
+ def re_insert
51
+ if revls.size.zero?
52
+ puts 'NAO EXISTEM RENDAS NOVAS'
53
+ else
54
+ dml('insert hernanilr.ab.re VALUES' + revls)
55
+ puts "RENDAS #{str_lc('')} CRIADAS " + bqnrs.to_s
56
+ end
57
+ end
58
+
59
+ # @param [Hash] cmv dados contrato arrendamento (inclui lista movimentos novos)
60
+ # @return [Array<String>] lista rendas novas dum contrato arrendamento (values.re)
61
+ def rendas_novas(cmv)
62
+ return [] unless cmv[:mv].count.positive?
63
+
64
+ vars_re(cmv)
65
+ r = []
66
+ while mvvlm >= revre && mvpos < cmv[:mv].count
67
+ r << nova_re(cmv)
68
+ proximo_mv(cmv)
69
+ end
70
+ r
71
+ end
72
+
73
+ # inicializa variaveis para processar rendas do contrato arrendamento
74
+ # @param (see rendas_novas)
75
+ def vars_re(cmv)
76
+ @reano = cmv[:ano]
77
+ @repos = cmv[:cnt]
78
+ @revre = Float(cmv[:ct][/\d+/]) / 100
79
+ @mvpos = 0
80
+ vars_re_mv(cmv)
81
+ end
82
+
83
+ # inicializa variaveis para processar movimentos associados ao contrato arrendamento
84
+ # @param (see rendas_novas)
85
+ def vars_re_mv(cmv)
86
+ @mvdlm = cmv[:mv][mvpos][:dl]
87
+ @mvvlm = cmv[:mv][mvpos][:vl]
88
+ end
89
+
90
+ # @param (see rendas_novas)
91
+ # @return [String] renda formatada (values.re)
92
+ def nova_re(cmv)
93
+ # inicializa proxima renda
94
+ if repos == 12
95
+ @repos = 1
96
+ @reano += 1
97
+ else
98
+ @repos += 1
99
+ end
100
+ "('#{cmv[:ct]}',#{reano},#{repos},'#{mvdlm.strftime(DF)}',#{dias(cmv)})"
101
+ end
102
+
103
+ # @param (see rendas_novas)
104
+ # @return [Integer] dias atraso no pagamento da renda
105
+ def dias(cmv)
106
+ mvdlm.mjd - (Date.new(reano, repos, 1) >> (cmv[:dc].month - 1)).mjd
107
+ end
108
+
109
+ # inicializa variaveis para processar proximo movimento
110
+ # @param (see rendas_novas)
111
+ def proximo_mv(cmv)
112
+ # valor renda paga retirado do valor do movimento
113
+ @mvvlm -= revre
114
+ return unless mvvlm < revre
115
+
116
+ # avanca na lista de movimentos
117
+ @mvpos += 1
118
+ return unless mvpos < cmv[:mv].count
119
+
120
+ vars_re_mv(cmv)
121
+ end
122
+ end
123
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Abank
4
- VERSION = '0.2.2'
4
+ VERSION = '0.2.7'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abank
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hernâni Rodrigues Vaz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-14 00:00:00.000000000 Z
11
+ date: 2020-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,9 +94,9 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- description: Arquiva <conta-corrente>.xlsx, <conta-cartao>.xlsx no bigquery. Pode
98
- apagar movimentos similares/existentes ja no bigquery. Pode ainda classificar movimentos
99
- ja no bigquery.
97
+ description: Arquiva movimentos conta-corrente, conta-cartao do activobank no bigquery.
98
+ Permite apagar/recriar movimentos/rendas ja no bigquery. Permite ainda classificar
99
+ movimentos no bigquery.
100
100
  email:
101
101
  - hernanirvaz@gmail.com
102
102
  executables:
@@ -105,6 +105,7 @@ extensions: []
105
105
  extra_rdoc_files: []
106
106
  files:
107
107
  - ".gitignore"
108
+ - ".rubocop.yml"
108
109
  - ".travis.yml"
109
110
  - Gemfile
110
111
  - Gemfile.lock
@@ -116,8 +117,10 @@ files:
116
117
  - bin/setup
117
118
  - exe/abank
118
119
  - lib/abank.rb
119
- - lib/abank/bigquery.rb
120
- - lib/abank/folhacalculo.rb
120
+ - lib/abank/big.rb
121
+ - lib/abank/contrato.rb
122
+ - lib/abank/folha.rb
123
+ - lib/abank/rendas.rb
121
124
  - lib/abank/version.rb
122
125
  homepage: https://github.com/hernanirvaz/abank
123
126
  licenses:
@@ -140,8 +143,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
143
  - !ruby/object:Gem::Version
141
144
  version: '0'
142
145
  requirements: []
143
- rubygems_version: 3.0.3
146
+ rubygems_version: 3.0.8
144
147
  signing_key:
145
148
  specification_version: 4
146
- summary: Arquiva <conta-corrente>.xlsx, <conta-cartao>.xlsx no bigquery.
149
+ summary: Arquiva movimentos conta-corrente, conta-cartao do activobank no bigquery.
147
150
  test_files: []
@@ -1,137 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'roo'
4
- require 'google/cloud/bigquery'
5
-
6
- module Abank
7
- DF = '%Y-%m-%d'
8
-
9
- # (see Bigquery)
10
- class Bigquery
11
- # @return [Google::Cloud::Bigquery] API bigquery
12
- attr_reader :apibq
13
- # @return [Roo::Excelx] folha calculo a processar
14
- attr_reader :folha
15
- # @return [Hash] opcoes trabalho com linhas
16
- attr_reader :linha
17
- # @return [Integer] numero conta
18
- attr_reader :conta
19
-
20
- # @return [Array] row folha calculo em processamento
21
- attr_reader :row
22
- # @return [Google::Cloud::Bigquery::QueryJob] job bigquery
23
- attr_reader :job
24
- # @return (see sql_select)
25
- attr_reader :sql
26
-
27
- # @param [String] xls folha calculo para processar
28
- # @param [Hash] ops opcoes trabalho com linhas
29
- # @option ops [Boolean] :s (false) apaga linha similar?
30
- # @option ops [Boolean] :e (false) apaga linha igual?
31
- # @option ops [Boolean] :m (false) apaga linhas existencia multipla?
32
- # @option ops [Boolean] :i (false) insere linha nova?
33
- # @option ops [Numeric] :n (0) numero dias correcao para data valor
34
- # @return [Bigquery] acesso folhas calculo activobank
35
- # & correspondente bigquery dataset
36
- def initialize(xls = '', ops = { s: false, e: false, m: false,
37
- i: false, n: 0 })
38
- # usa env GOOGLE_APPLICATION_CREDENTIALS para obter credentials
39
- # @see https://cloud.google.com/bigquery/docs/authentication/getting-started
40
- @apibq = Google::Cloud::Bigquery.new
41
- @folha = Roo::Spreadsheet.open(xls) if xls.size.positive?
42
- @linha = ops
43
- @conta = xls.match?(/card/i) ? 2 : 1
44
- end
45
-
46
- # cria job bigquery & verifica execucao
47
- #
48
- # @param [String] sql a executar
49
- # @return [Boolean] job ok?
50
- def job_bigquery?(sql)
51
- @job = apibq.query_job(sql)
52
- @job.wait_until_done!
53
- puts @job.error['message'] if @job.failed?
54
- @job.failed?
55
- end
56
-
57
- # executa Stored procedure (SP) job bigquery
58
- #
59
- # @param (see job_bigquery?)
60
- # @return (see num_insert)
61
- def esp(sql)
62
- job_bigquery?(sql) ? 0 : num_insert(apibq.jobs(parent_job: job))
63
- end
64
-
65
- # @return [Integer] numero linhas inseridas
66
- def num_insert(sps)
67
- sps.find { |j| j.statement_type == 'INSERT' }.num_dml_affected_rows
68
- end
69
-
70
- # cria Data Manipulation Language (DML) job bigquery
71
- #
72
- # @param (see job_bigquery?)
73
- # @return [Integer] numero linhas afetadas
74
- def dml(sql)
75
- job_bigquery?(sql) ? 0 : job.num_dml_affected_rows
76
- end
77
-
78
- # pesquisa existencia linha folha calculo no bigquery
79
- #
80
- # @return [Google::Cloud::Bigquery::Data] resultado do sql num array<hash>
81
- def sql_select
82
- # array.count = 0 ==> pode carregar esta linha
83
- # array.count = 1 ==> mais testes necessarios
84
- # array.count > 1 ==> nao carregar esta linha
85
- @sql = job_bigquery?('select * ' + sql_where) ? [{}, {}] : job.data
86
- end
87
-
88
- # @return [String] parte sql para processamento linhas similares
89
- def sql_where
90
- "from hernanilr.ab.mv where nc=#{conta}" \
91
- " and dl='#{row[0].strftime(DF)}'" \
92
- " and vl=#{row[3]}"
93
- end
94
-
95
- # (see CLI#classifica)
96
- def classifica
97
- return unless linha[:i]
98
-
99
- puts 'LINHAS CLASSIFICADAS ' +
100
- dml('update hernanilr.ab.mv set mv.ct=tt.nct' \
101
- ' from (select * from hernanilr.ab.cl) as tt' \
102
- ' where mv.dl=tt.dl and mv.dv=tt.dv' \
103
- ' and mv.ds=tt.ds and mv.vl=tt.vl').to_s
104
- puts 'RENDAS INSERIDAS ' + esp('call hernanilr.ab.rendas()').to_s
105
- end
106
-
107
- # @return [Integer] numero linhas inseridas
108
- def sql_insert
109
- return 1 unless linha[:i]
110
-
111
- dml('insert hernanilr.ab.mv(dl,dv,ds,vl,nc,ano,mes,ct,tp) VALUES(' \
112
- "'#{row[0].strftime(DF)}','#{data_valor.strftime(DF)}','#{row[2]}'" +
113
- str_insert1)
114
- end
115
-
116
- # @return [Date] data valor corrigida
117
- def data_valor
118
- row[1] + linha[:n].to_i
119
- end
120
-
121
- # @return [String] campos extra da linha bigquery
122
- def str_insert1
123
- ",#{row[3]},#{conta}" + str_insert2
124
- end
125
-
126
- # @return [String] campos calculados da linha bigquery
127
- def str_insert2
128
- ",#{data_valor.year},#{data_valor.month},null," \
129
- "'#{row[3].positive? ? 'c' : 'd'}')"
130
- end
131
-
132
- # @return [Integer] numero linhas apagadas
133
- def sql_delete
134
- dml('delete ' + sql_where + " and ds='#{sql.first[:ds].strip}'")
135
- end
136
- end
137
- end
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abank
4
- HT = ['Data Lanc.', 'Data Valor', 'Descrição', 'Valor'].freeze
5
- RF = '%<v3>-35.35s %<v4>8.2f'
6
-
7
- # classifica & arquiva dados das folhas calculo activobank no bigquery
8
- class Bigquery
9
- # corrige linha folha calculo para processamento
10
- #
11
- # @param [Hash] has da linha em processamento
12
- def corrige_hash(has)
13
- @row = has.values
14
- @row[2] = row[2].strip
15
- @row[3] = -1 * row[3] if conta > 1
16
- end
17
-
18
- # processa linhas folha calculo & classifica bigquery
19
- def processa
20
- n = 0
21
- folha.sheet(0).parse(header_search: HT) do |r|
22
- n += 1
23
- puts n == 1 ? "\n" + folha.info : processa_row(r)
24
- end
25
- classifica
26
- end
27
-
28
- # processa linha folha calculo para arquivo
29
- #
30
- # @param (see corrige_hash)
31
- # @return [String] texto informativo do processamento
32
- def processa_row(has)
33
- corrige_hash(has)
34
- sql_select
35
- if row_naoexiste? then row_str + (sql_insert == 1 ? ' NOVA' : ' ERRO')
36
- elsif row_simila? then row_similar
37
- elsif row_existe? then row_existente
38
- else row_multiplas
39
- end
40
- end
41
-
42
- # @return [String] linha folha calculo formatada
43
- def row_str
44
- "#{row[0].strftime(DF)} #{row[1].strftime(DF)} " \
45
- "#{format(RF, v3: row[2], v4: row[3])}"
46
- end
47
-
48
- # @return [String] linha folha calculo similar
49
- def row_similar
50
- d = linha[:s] ? sql_delete : 0
51
- row_str + ' SIMILAR' + str_apagadas(d) + sql.first[:ds].strip
52
- end
53
-
54
- # @return [String] linha folha calculo existente
55
- def row_existente
56
- d = linha[:e] ? sql_delete : 0
57
- row_str + ' EXISTENTE' + str_apagadas(d)
58
- end
59
-
60
- # @return [String] linha folha calculo existencia multipla
61
- def row_multiplas
62
- d = linha[:m] ? sql_delete : 0
63
- row_str + ' MULTIPLAS ' + sql.count.to_s + str_apagadas(d)
64
- end
65
-
66
- # @param [Integer] numero linhas apagadas
67
- # @return [String] texto formatado linhas apagadas
68
- def str_apagadas(num)
69
- num.positive? ? ' & ' + num.to_s + ' APAGADA(S) ' : ' '
70
- end
71
-
72
- # @return [Boolean] linha folha calculo nao existe no bigquery?
73
- def row_naoexiste?
74
- sql.count.zero?
75
- end
76
-
77
- # @return [Boolean] linha folha calculo existe no bigquery?
78
- def row_existe?
79
- sql.count == 1 && sql.first[:ds].strip == row[2]
80
- end
81
-
82
- # @return [Boolean] linha folha calculo existe parecida no bigquery?
83
- def row_simila?
84
- sql.count == 1 && sql.first[:ds].strip != row[2]
85
- end
86
- end
87
- end