abank 0.1.9 → 0.2.4

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: f4fa823dd3a80abdd91cd205a521b1f00ab236cfd91150a16e1a85ac62af10b3
4
- data.tar.gz: a138ebd1ba1cf321429c5d65d219ccaf7549c8489c6ca3d4cf043f026e15a0aa
3
+ metadata.gz: f1720d3e8b1de4bc6ea635712404363e460b3550af47a665a06652fae028f917
4
+ data.tar.gz: eef494213d2f5bf45887a1f643ec2f1becf2d470e9cc5451c2412dd41d9696c2
5
5
  SHA512:
6
- metadata.gz: e8429fbe87e9fbeb10e64b55e3fd0b42b3e84d9e9305532663ff8991d0716185bce299ce23362fb2de7c35c26584ea37556c3a816945c2fff89367d48cc04b27
7
- data.tar.gz: 25279212f35408690c174205720fed6d6a9641ae8cdb78ed4d63eeab2767933dd940b95493d2bbb164a82c1c6719caf405099641566118efaf21e2501cb9f78d
6
+ metadata.gz: eb2aa5a87322b5cb27fb2fac24cd99d5288eb3be339a0976cd2d2cd12c3ee46b63c2e09e8060c16dd3f07197ba837ecf6fa612a658f86cb3c7875a5546dd16b5
7
+ data.tar.gz: 9498f7fa1b1a5cbdf4c56b39cd770073eb13642d669847fecdd5a1b3285dd3ffc15fb0e6437a8e2758d6223e4e128e7eb0664e6f6ffb42cc73d3d1ce46380ea9
@@ -0,0 +1,29 @@
1
+ AllCops:
2
+ EnabledByDefault: true
3
+
4
+ Style/ClassAndModuleChildren:
5
+ EnforcedStyle: compact
6
+
7
+ Style/Copyright:
8
+ Enabled: false
9
+
10
+ Style/DocumentationMethod:
11
+ Enabled: false
12
+
13
+ Style/MethodCallWithArgsParentheses:
14
+ Enabled: false
15
+
16
+ Style/ConstantVisibility:
17
+ Enabled: false
18
+
19
+ Layout/MultilineMethodArgumentLineBreaks:
20
+ Enabled: false
21
+
22
+ Layout/MultilineHashKeyLineBreaks:
23
+ Enabled: false
24
+
25
+ Layout/FirstMethodParameterLineBreak:
26
+ Enabled: false
27
+
28
+ Layout/FirstHashElementLineBreak:
29
+ Enabled: false
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- abank (0.1.9)
4
+ abank (0.2.4)
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.38.0)
20
+ google-api-client (0.39.5)
21
21
  addressable (~> 2.5, >= 2.5.1)
22
22
  googleauth (~> 0.9)
23
23
  httpclient (>= 2.8.1, < 3.0)
@@ -54,7 +54,7 @@ GEM
54
54
  nokogiri (1.10.9)
55
55
  mini_portile2 (~> 2.4.0)
56
56
  os (1.1.0)
57
- public_suffix (4.0.4)
57
+ public_suffix (4.0.5)
58
58
  rake (12.3.3)
59
59
  representable (3.0.4)
60
60
  declarative (< 0.1.0)
@@ -72,7 +72,7 @@ GEM
72
72
  multi_json (~> 1.10)
73
73
  thor (1.0.1)
74
74
  uber (0.1.0)
75
- yard (0.9.24)
75
+ yard (0.9.25)
76
76
 
77
77
  PLATFORMS
78
78
  ruby
@@ -1,8 +1,10 @@
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/folha'
7
+ require 'abank/rendas'
6
8
  require 'abank/version'
7
9
 
8
10
  # @author Hernani Rodrigues Vaz
@@ -13,43 +15,86 @@ module Abank
13
15
 
14
16
  # CLI para carregar folhas calculo comuns no bigquery
15
17
  class CLI < Thor
18
+ desc 'tag', 'classifica movimentos no bigquery'
19
+ # classifica movimentos no bigquery
20
+ def tag
21
+ Big.new(i: true).mv_classifica
22
+ end
23
+
24
+ desc 'rendas', 'atualiza rendas no bigquery'
25
+ # atualiza rendas no bigquery
26
+ def rendas
27
+ Big.new.re_atualiza
28
+ end
29
+
30
+ desc 'apaga', 'apaga movimentos no bigquery'
31
+ option :k, banner: 'k1[,k2,...]', required: true,
32
+ desc: 'Keys movimentos a apagar'
33
+ # apaga movimentos no bigquery
34
+ def apaga
35
+ Big.new(k: options[:k]).mv_apaga
36
+ end
37
+
16
38
  desc 'load', 'carrega dados da folha calculo no bigquery'
17
39
  option :d, banner: 'DIR', default: "/home/#{ID}/Downloads",
18
40
  desc: 'Onde procurar folhas calculo'
19
- option :x, banner: 'EXT', default: '.xlsx',
20
- desc: 'Extensao das folhas calculo'
41
+ option :v, banner: 'DATA', default: '',
42
+ desc: 'data valor para movimentos a carregar'
43
+ option :g, banner: 'TAG', default: '',
44
+ desc: 'classificacao para movimentos a carregar'
21
45
  option :s, type: :boolean, default: false,
22
46
  desc: 'apaga linha similar no bigquery'
23
47
  option :e, type: :boolean, default: false,
24
48
  desc: 'apaga linha igual no bigquery'
25
49
  option :m, type: :boolean, default: false,
26
50
  desc: 'apaga linhas existencia multipla no bigquery'
27
- # processa folha calculo
51
+ # carrega folha calculo
28
52
  def load
29
- Dir.glob("#{options[:d]}/*#{options[:x]}").sort.each do |f|
30
- Bigquery.new(f, { s: options[:s], e: options[:e],
31
- m: options[:m], i: true }).processa
53
+ Dir.glob("#{options[:d]}/*.xlsx").sort.each do |f|
54
+ Big::Folha.new(f, load_ops).processa_folha
32
55
  end
33
56
  end
34
57
 
35
- desc 'mostra', 'mostra dados da folha calculo'
58
+ desc 'show', 'mostra dados da folha calculo'
36
59
  option :d, banner: 'DIR', default: "/home/#{ID}/Downloads",
37
60
  desc: 'Onde procurar folhas calculo'
38
- option :x, banner: 'EXT', default: '.xlsx',
39
- desc: 'Extensao das folhas calculo'
40
61
  # mostra folha calculo
41
- def mostra
42
- Dir.glob("#{options[:d]}/*#{options[:x]}").sort.each do |f|
43
- Bigquery.new(f).processa
62
+ def show
63
+ Dir.glob("#{options[:d]}/*.xlsx").sort.each do |f|
64
+ Big::Folha.new(f).processa_folha
44
65
  end
45
66
  end
46
67
 
47
- desc 'classifica', 'classifica arquivo no bigquery'
48
- # classifica arquivo no bigquery
49
- def classifica
50
- Bigquery.new.classifica
68
+ desc 'criare', 'cria contrato arrendamento/rendas no bigquery'
69
+ option :c, banner: 'CONTRATO', required: true,
70
+ desc: 'Identificador contrato arrendamento a criar'
71
+ option :t, type: :boolean, default: true,
72
+ desc: 'cria todas as rendas?'
73
+ option :v, banner: 'DATA', default: '',
74
+ desc: 'data contrato arrendamento a criar'
75
+ # cria contrato arrendamento/rendas no bigquery
76
+ def criare
77
+ Big::Contrato.new(options[:c], { t: options[:t], v: options[:v] }).re_cria
78
+ end
79
+
80
+ desc 'apagare', 'apaga contrato arrendamento/rendas no bigquery'
81
+ option :c, banner: 'CONTRATO', required: true,
82
+ desc: 'Identificador contrato arrendamento a apagar'
83
+ option :t, type: :boolean, default: false,
84
+ desc: 'apaga todas as rendas?'
85
+ # apaga contrato arrendamento/rendas no bigquery
86
+ def apagare
87
+ Big::Contrato.new(options[:c], { t: options[:t], v: '' }).re_apaga
88
+ end
89
+
90
+ no_commands do
91
+ # @return [Hash] opcoes trabalho com linhas para load
92
+ def load_ops
93
+ { s: options[:s], e: options[:e], m: options[:m], i: true,
94
+ v: options[:v], g: options[:g] }
95
+ end
51
96
  end
52
97
 
53
- default_task :mostra
98
+ default_task :rendas
54
99
  end
55
100
  end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'google/cloud/bigquery'
4
+
5
+ # class Contrato
6
+ class Abank::Big
7
+ DF = '%Y-%m-%d'
8
+
9
+ # @return [Google::Cloud::Bigquery] API bigquery
10
+ attr_reader :api
11
+ # @return [Hash] opcoes trabalho com linhas
12
+ attr_reader :opl
13
+
14
+ # @return [Google::Cloud::Bigquery::QueryJob] job bigquery
15
+ attr_reader :job
16
+ # @return [Google::Cloud::Bigquery::Data] lista devolvida pelo select
17
+ attr_reader :resultados
18
+
19
+ # @param [Hash] ops opcoes trabalho
20
+ # @option ops [Boolean] :s (false) apaga linha similar? (mv)
21
+ # @option ops [Boolean] :e (false) apaga linha igual? (mv)
22
+ # @option ops [Boolean] :m (false) apaga linhas existencia multipla? (mv)
23
+ # @option ops [Boolean] :i (false) insere linha nova? (mv)
24
+ # @option ops [String] :v ('') data valor (mv)/data contrato (re)
25
+ # @option ops [String] :g ('') classificacao movimentos (mv)
26
+ # @option ops [Boolean] :t (false) trabalha todoas as rendas? (re)
27
+ # @option ops [String] :k ('') keys movimentos a apagar (mv)
28
+ # @return [Big] acesso bigquery dataset
29
+ def initialize(ops = {})
30
+ @opl = ops
31
+ @api ||= Google::Cloud::Bigquery.new
32
+ p ['Big', ops, api]
33
+ end
34
+
35
+ # (see CLI#classifica)
36
+ def mv_classifica
37
+ return unless opl[:i]
38
+
39
+ i = dml('update hernanilr.ab.mv set mv.ct=tt.nct ' \
40
+ 'from (select * from hernanilr.ab.cl) as tt ' \
41
+ 'where mv.dl=tt.dl and mv.dv=tt.dv ' \
42
+ 'and mv.ds=tt.ds and mv.vl=tt.vl')
43
+ puts 'LINHAS CLASSIFICADAS ' + i.to_s
44
+ return unless i.positive?
45
+
46
+ re_atualiza
47
+ end
48
+
49
+ # (see CLI#atualiza)
50
+ def re_atualiza
51
+ r = re_join(lista_ativos)
52
+ if r.size.zero?
53
+ puts 'NAO EXISTEM RENDAS NOVAS'
54
+ else
55
+ puts 'RENDAS CRIADAS ' + dml('insert hernanilr.ab.re VALUES' + r).to_s
56
+ end
57
+ end
58
+
59
+ # (see CLI#apagamv)
60
+ def mv_apaga
61
+ e = ct_envolvidos
62
+ i = dml(sql_apaga_mv)
63
+ puts 'MOVIMENTOS APAGADOS ' + i.to_s
64
+ return unless i.positive? && e.count.positive?
65
+
66
+ e.map { |c| Contrato.new(c).re_apaga }
67
+
68
+ re_atualiza
69
+ end
70
+
71
+ def ct_envolvidos
72
+ sel(sql_sel_mv).group_by { |r| r[:ct] }
73
+ .delete_if { |k, _| !k || k[0] != 'r' }.keys
74
+ end
75
+
76
+ # @return [Array<Hash>] lista contratos com lista movimentos novos
77
+ def lista_ativos
78
+ sel(sql_ativos_re).map { |c| Contrato.new(c[:ct]).dados_contrato }.compact
79
+ end
80
+
81
+ # @param [Array<Hash>] lct lista contratos com lista movimentos novos
82
+ # @return [String] row formatada das novas rendas para inserir bigquery
83
+ def re_join(lct)
84
+ lct.map { |c| Contrato::Rendas.new(c).rendas }.flatten(1).join(',')
85
+ end
86
+
87
+ def sql_ativos_re
88
+ 'SELECT ct from hernanilr.ab.re group by 1 order by 1'
89
+ end
90
+
91
+ def sql_sel_mv
92
+ 'select * ' + sql_where_mv
93
+ end
94
+
95
+ # @return [String] sql apaga movimentos
96
+ def sql_apaga_mv
97
+ 'delete ' + sql_where_mv
98
+ end
99
+
100
+ # @return [String] parte sql para processamento movimentos
101
+ def sql_where_mv
102
+ "from hernanilr.ab.mv where #{sql_digest_mv} in(#{opl[:k]})"
103
+ end
104
+
105
+ def sql_digest_mv
106
+ 'FARM_FINGERPRINT(CONCAT(CAST(nc as STRING),' \
107
+ 'ds,CAST(dl as STRING),CAST(vl as STRING)))'
108
+ end
109
+
110
+ # cria job bigquery & verifica execucao
111
+ #
112
+ # @param [String] sql comando sql a executar
113
+ # @return [Boolean] job ok?
114
+ def job_bigquery?(sql)
115
+ p sql
116
+ @job = api.query_job(sql)
117
+ @job.wait_until_done!
118
+ puts @job.error['message'] if @job.failed?
119
+ @job.failed?
120
+ end
121
+
122
+ # executa Data Manipulation Language (DML) job no bigquery
123
+ #
124
+ # @param (see job_bigquery?)
125
+ # @return [Integer] numero linhas afetadas
126
+ def dml(sql)
127
+ job_bigquery?(sql) ? 0 : job.num_dml_affected_rows
128
+ end
129
+
130
+ # executa sql & devolve resultados do bigquery
131
+ #
132
+ # @param sql (see job_bigquery?)
133
+ # @param [Array] arr resultado quando da erro no bigquery
134
+ # @return [Google::Cloud::Bigquery::Data] resultado do sql num array<hash>
135
+ def sel(sql, arr = [])
136
+ @resultados = job_bigquery?(sql) ? arr : job.data
137
+ end
138
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # class Big::Contrato
4
+ class Abank::Big::Contrato < Abank::Big
5
+ # @return [String] identificador contrato arrendamento
6
+ attr_reader :rct
7
+
8
+ # {:ct=>"r03000"}
9
+ def initialize(con, ops = { t: false, v: '' })
10
+ p ['Contrato', con, ops]
11
+ @rct = con
12
+ super(ops)
13
+ end
14
+
15
+ # (see CLI#cria)
16
+ def re_cria
17
+ if existe_contrato?
18
+ i = 0
19
+ puts 'JA EXISTE CONTRATO'
20
+ else
21
+ i = dml('insert into hernanilr.ab.re ' + sql_contrato_mv)
22
+ puts i.zero? ? 'NAO EXISTE CONTRATO' : "CONTRATO #{rct} INSERIDO"
23
+ end
24
+ return unless i.positive? && opl[:t]
25
+
26
+ re_atualiza
27
+ end
28
+
29
+ # (see CLI#apagare)
30
+ def re_apaga
31
+ puts "RENDAS #{rct} APAGADAS " + dml(sql_apaga_re).to_s
32
+ end
33
+
34
+ # @return [Hash] dados contrato & movimentos novos
35
+ def dados_contrato
36
+ c = sel(sql_last_re).first
37
+ sel(sql_novo_mv(c[:dl]))
38
+ return unless resultados.count.positive?
39
+
40
+ { mv: resultados }.merge(c)
41
+ end
42
+
43
+ def sql_last_re
44
+ 'select ct,DATE_SUB(DATE_SUB(dl,INTERVAL dias DAY)' \
45
+ ',INTERVAL IF(cnt=0,0,cnt-1) MONTH) as dc,ano,cnt,dl ' \
46
+ "from hernanilr.ab.re where ct='#{rct}' " \
47
+ 'order by ano desc,cnt desc limit 1'
48
+ end
49
+
50
+ def sql_novo_mv(mdl)
51
+ "select dl,vl from hernanilr.ab.mv where ct='#{rct}' " \
52
+ "and dl>='#{(mdl + 1).strftime(DF)}' order by dl,dv"
53
+ end
54
+
55
+ def existe_contrato?
56
+ sel(sql_contrato_re).count.positive?
57
+ end
58
+
59
+ def sql_contrato_re
60
+ "select * from hernanilr.ab.re where ct='#{rct}' and cnt=0"
61
+ end
62
+
63
+ # @return [String] sql obtem dados inicio contrato arrendamento
64
+ def sql_contrato_mv
65
+ if opl[:v].size.zero?
66
+ 'select ct,EXTRACT(YEAR FROM DATE_TRUNC(dl,MONTH)) as ano,0 as cnt' \
67
+ ',DATE_TRUNC(dl,MONTH) as dl,0 dias ' \
68
+ "from hernanilr.ab.mv where ct='#{rct}' order by dl limit 1"
69
+ else
70
+ "select '#{rct}' as ct" \
71
+ ",EXTRACT(YEAR FROM DATE '#{opl[:v]}') as ano,0 as cnt" \
72
+ ",DATE '#{opl[:v]}' as dl,0 dias "
73
+ end
74
+ end
75
+
76
+ def sql_apaga_re
77
+ "delete from hernanilr.ab.re where ct='#{rct}'" +
78
+ (opl[:t] ? '' : ' and cnt>0')
79
+ end
80
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'roo'
4
+
5
+ # class Big::Contrato
6
+ class Abank::Big::Folha < Abank::Big
7
+ HT = ['Data Lanc.', 'Data Valor', 'Descrição', 'Valor'].freeze
8
+ RF = '%<v3>-34.34s %<v4>8.2f'
9
+ EN = ' %<v1>20d'
10
+ ES = ' %<v1>-20.20s'
11
+
12
+ # @return [Roo::Excelx] folha calculo a processar
13
+ attr_reader :folha
14
+ # @return [Integer] numero conta
15
+ attr_reader :conta
16
+
17
+ # @return [Array] row folha calculo em processamento
18
+ attr_reader :row
19
+
20
+ def initialize(xls, ops = { s: false, e: false, m: false, i: false,
21
+ v: '', g: '' })
22
+ p ['Folha', xls, ops]
23
+ @folha = Roo::Spreadsheet.open(xls) if xls.size.positive?
24
+ @conta = xls.match?(/card/i) ? 2 : 1
25
+ super(ops)
26
+ end
27
+
28
+ # processa linhas folha calculo & classifica bigquery
29
+ def processa_folha
30
+ n = 0
31
+ folha.sheet(0).parse(header_search: HT) do |r|
32
+ n += 1
33
+ puts n == 1 ? "\n" + folha.info : processa_row(r)
34
+ end
35
+ mv_classifica
36
+ end
37
+
38
+ # processa linha folha calculo para arquivo
39
+ #
40
+ # @param (see corrige_hash)
41
+ # @return [String] texto informativo do processamento
42
+ def processa_row(has)
43
+ corrige_hash(has)
44
+ # pesquisa existencia linha folha calculo no bigquery
45
+ # array.count = 0 ==> pode carregar esta linha
46
+ # array.count = 1 ==> mais testes necessarios
47
+ # array.count > 1 ==> nao pode carregar esta linha
48
+ sel(sql_sel_mv, [{}, {}])
49
+ if row_naoexiste? then row_str + (insert_mv == 1 ? ' NOVA' : ' ERRO')
50
+ elsif row_simila? then row_similar
51
+ elsif row_existe? then row_existente
52
+ else row_multiplas
53
+ end
54
+ end
55
+
56
+ # corrige linha folha calculo para processamento
57
+ #
58
+ # @param [Hash] has da linha em processamento
59
+ def corrige_hash(has)
60
+ @row = has.values
61
+ @row[2] = row[2].strip
62
+ @row[3] = -1 * row[3] if conta > 1
63
+ end
64
+
65
+ # @return [String] linha folha calculo formatada
66
+ def row_str
67
+ "#{row[0].strftime(DF)} #{format(RF, v3: row[2], v4: row[3])}"
68
+ end
69
+
70
+ # @return [String] linha folha calculo similar
71
+ def row_similar
72
+ d = opl[:s] ? delete_mv : 0
73
+ row_str + ' SIMI' + str_apagadas(d) + str_extra_s(resultados.first[:ds])
74
+ end
75
+
76
+ # @return [String] linha folha calculo existente
77
+ def row_existente
78
+ d = opl[:e] ? delete_mv : 0
79
+ row_str + ' EXIS' + str_apagadas(d) + str_extra_n(resultados.first[:ky])
80
+ end
81
+
82
+ def str_extra_s(ext)
83
+ format(ES, v1: ext.strip)
84
+ end
85
+
86
+ def str_extra_n(ext)
87
+ format(EN, v1: ext)
88
+ end
89
+
90
+ # @return [String] linha folha calculo existencia multipla
91
+ def row_multiplas
92
+ d = opl[:m] ? delete_mv : 0
93
+ row_str + ' M(' + resultados.count.to_s + ')' + str_apagadas(d)
94
+ end
95
+
96
+ # @param [Integer] numero linhas apagadas
97
+ # @return [String] texto formatado linhas apagadas
98
+ def str_apagadas(num)
99
+ num.positive? ? ' A(' + num.to_s + ')' : ''
100
+ end
101
+
102
+ # @return [Boolean] linha folha calculo nao existe no bigquery?
103
+ def row_naoexiste?
104
+ resultados.count.zero?
105
+ end
106
+
107
+ # @return [Boolean] linha folha calculo existe no bigquery?
108
+ def row_existe?
109
+ resultados.count == 1 && resultados.first[:ds].strip == row[2]
110
+ end
111
+
112
+ # @return [Boolean] linha folha calculo existe parecida no bigquery?
113
+ def row_simila?
114
+ resultados.count == 1 && resultados.first[:ds].strip != row[2]
115
+ end
116
+
117
+ def sql_sel_mv
118
+ "select *,#{sql_digest_mv} as ky " + sql_where_mv
119
+ end
120
+
121
+ # @return [String] parte sql para processamento movimentos
122
+ def sql_where_mv
123
+ "from hernanilr.ab.mv where nc=#{conta} " \
124
+ "and dl='#{row[0].strftime(DF)}' " \
125
+ "and vl=#{row[3]}"
126
+ end
127
+
128
+ # @return [Integer] numero linhas inseridas
129
+ def insert_mv
130
+ return 1 unless opl[:i]
131
+
132
+ dml('insert hernanilr.ab.mv(dl,dv,ds,vl,nc,ano,mes,ct,tp) VALUES(' \
133
+ "'#{row[0].strftime(DF)}','#{dvc.strftime(DF)}','#{row[2]}',#{row[3]}" +
134
+ str_ins_pc)
135
+ end
136
+
137
+ # @return [Date] data valor corrigida
138
+ def dvc
139
+ opl[:v].size.zero? ? row[1] : Date.parse(opl[:v])
140
+ end
141
+
142
+ # @return [String] campos extra da linha bigquery
143
+ def str_ins_pc
144
+ ",#{conta},#{dvc.year},#{dvc.month},#{ctc},'#{tpc}')"
145
+ end
146
+
147
+ def ctc
148
+ opl[:g].size.zero? ? 'null' : ("'" + opl[:g] + "'")
149
+ end
150
+
151
+ def tpc
152
+ row[3].positive? ? 'c' : 'd'
153
+ end
154
+
155
+ # @return [Integer] numero linhas apagadas
156
+ def delete_mv
157
+ dml('delete ' + sql_where_mv + " and ds='#{resultados.first[:ds]}'")
158
+ end
159
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ # class Big::Renda
4
+ class Abank::Big::Contrato::Rendas < Abank::Big::Contrato
5
+ attr_reader :ren
6
+
7
+ # @return [Integer] ano renda
8
+ attr_reader :ran
9
+ # @return [Integer] mes renda
10
+ attr_reader :rcn
11
+ # @return [Float] valor renda mensal
12
+ attr_reader :rru
13
+
14
+ # @return [Integer] total de movimentos novos
15
+ attr_reader :mcn
16
+ # @return [Integer] movimento em tratamento
17
+ attr_reader :mpn
18
+ # @return [Date] data lancamento movimento em tratamento
19
+ attr_reader :mdl
20
+ # @return [Float] valor movimento em tratamento
21
+ attr_reader :mvl
22
+
23
+ # @return [Date] data contrato arrendamento
24
+ # {:mv=>[{:dl=>#<Date: 2020-03-02>, :vl=>0.3e2},...]
25
+ # ,:ct=>"r03000"
26
+ # ,:dc=>#<Date: 2020-03-01>
27
+ # ,:ano=>2020
28
+ # ,:cnt=>0
29
+ # ,:dl=>#<Date: 2020-03-01>}
30
+ def initialize(ren)
31
+ p ['Rendas', ren]
32
+ @ren = ren
33
+ super(ren[:ct])
34
+ end
35
+
36
+ # @param [Hash] con dados contrato & lista movimentos novos
37
+ # @return [Array<Hash>] lista rendas novas para criar
38
+ def rendas
39
+ vars_re
40
+ vars_mv
41
+ r = []
42
+ while mvl >= rru && mpn < mcn
43
+ r << nova_re
44
+ proximo_mv
45
+ end
46
+ r
47
+ end
48
+
49
+ # @param (see rendas)
50
+ def vars_mv
51
+ @mpn = 0
52
+ @mcn = ren[:mv].count
53
+ vars_dados_mv
54
+ end
55
+
56
+ # @param (see rendas)
57
+ def vars_dados_mv
58
+ @mdl = ren[:mv][mpn][:dl]
59
+ @mvl = ren[:mv][mpn][:vl]
60
+ end
61
+
62
+ # @param (see rendas)
63
+ def vars_re
64
+ @ran = ren[:ano]
65
+ @rcn = ren[:cnt]
66
+ @rru = Float(ren[:ct][/\d+/]) / 100
67
+ end
68
+
69
+ # @return [Array] lista dados da renda nova
70
+ def nova_re
71
+ if rcn == 12
72
+ @rcn = 1
73
+ @ran += 1
74
+ else
75
+ @rcn += 1
76
+ end
77
+ # [rct, ran, rcn, mdl, mdl.mjd - vencimento.mjd]
78
+ "('#{rct}',#{ran},#{rcn},'#{mdl.strftime(DF)}',#{dias})"
79
+ end
80
+
81
+ def dias
82
+ mdl.mjd - (Date.new(ran, rcn, 1) >> (ren[:dc].month - 1)).mjd
83
+ end
84
+
85
+ # @param (see rendas)
86
+ def proximo_mv
87
+ @mvl -= rru
88
+ return unless mvl < rru
89
+
90
+ @mpn += 1
91
+ return unless mpn < mcn
92
+
93
+ vars_dados_mv
94
+ end
95
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Abank
4
- VERSION = '0.1.9'
4
+ VERSION = '0.2.4'
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.1.9
4
+ version: 0.2.4
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-04-22 00:00:00.000000000 Z
11
+ date: 2020-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -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:
@@ -1,115 +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<Symbol, Boolean>] 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<Symbol, Boolean>] 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
- # @return [Bigquery] acesso folhas calculo activobank
34
- # & correspondente bigquery dataset
35
- def initialize(xls = '', ops = { s: false, e: false, m: false, i: false })
36
- # usa env GOOGLE_APPLICATION_CREDENTIALS para obter credentials
37
- # @see https://cloud.google.com/bigquery/docs/authentication/getting-started
38
- @apibq = Google::Cloud::Bigquery.new
39
- @folha = Roo::Spreadsheet.open(xls) if xls.size.positive?
40
- @linha = ops
41
- @conta = xls.match?(/card/i) ? 2 : 1
42
- end
43
-
44
- # cria job bigquery & verifica execucao
45
- #
46
- # @param [String] sql a executar
47
- # @return [Boolean] job ok?
48
- def job_bigquery?(sql)
49
- @job = apibq.query_job(sql)
50
- @job.wait_until_done!
51
- puts @job.error['message'] if @job.failed?
52
- @job.failed?
53
- end
54
-
55
- # cria Data Manipulation Language (DML) job bigquery
56
- #
57
- # @param (see job_bigquery?)
58
- # @return [Integer] numero linhas afetadas
59
- def dml(sql)
60
- job_bigquery?(sql) ? 0 : job.num_dml_affected_rows
61
- end
62
-
63
- # pesquisa existencia linha folha calculo no bigquery
64
- #
65
- # @return [Google::Cloud::Bigquery::Data] resultado do sql num array<hash>
66
- def sql_select
67
- # array.count = 0 ==> pode carregar esta linha
68
- # array.count = 1 ==> mais testes necessarios
69
- # array.count > 1 ==> nao carregar esta linha
70
- @sql = job_bigquery?('select * ' + sql_where) ? [{}, {}] : job.data
71
- end
72
-
73
- # @return [String] parte sql para processamento linhas similares
74
- def sql_where
75
- "from hernanilr.ab.mv where nc=#{conta}" \
76
- " and dl='#{row[0].strftime(DF)}'" \
77
- " and vl=#{row[3]}"
78
- end
79
-
80
- # (see CLI#classifica)
81
- def classifica
82
- return unless linha[:i]
83
-
84
- puts 'LINHAS CLASSIFICADAS ' +
85
- dml('update hernanilr.ab.mv set mv.ct=tt.nct' \
86
- ' from (select * from hernanilr.ab.cl) as tt' \
87
- ' where mv.dl=tt.dl and mv.dv=tt.dv' \
88
- ' and mv.ds=tt.ds and mv.vl=tt.vl').to_s
89
- end
90
-
91
- # @return [Integer] numero linhas inseridas
92
- def sql_insert
93
- return 1 unless linha[:i]
94
-
95
- dml('insert hernanilr.ab.mv(dl,dv,ds,vl,nc,ano,mes,ct,tp) VALUES(' \
96
- "'#{row[0].strftime(DF)}','#{row[1].strftime(DF)}','#{row[2]}'" +
97
- str_insert1)
98
- end
99
-
100
- # @return [String] campos extra da linha bigquery
101
- def str_insert1
102
- ",#{row[3]},#{conta}" + str_insert2
103
- end
104
-
105
- # @return [String] campos calculados da linha bigquery
106
- def str_insert2
107
- ",#{row[1].year},#{row[1].month},null,'#{row[3].positive? ? 'c' : 'd'}')"
108
- end
109
-
110
- # @return [Integer] numero linhas apagadas
111
- def sql_delete
112
- dml('delete ' + sql_where + " and ds='#{sql.first[:ds].strip}'")
113
- end
114
- end
115
- 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>-50.50s %<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