lucabook 0.2.25 → 0.2.26

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: c77f6025962d1697b99602db386367b08f037b4c9651c277729e3c16ae058dfa
4
- data.tar.gz: eb3fa1abeda269e967699b974d5a3f1be504e85d39ddec9e080c4ceb5c28eabe
3
+ metadata.gz: 933e3d04315c04b7f6e998b16499b458ceeeafe8bfae9ce4ef4422018eb17040
4
+ data.tar.gz: 2e243b4e85cdc50bd45e39ed311cf78a8aa12e1956be0d0273c2a634147be16c
5
5
  SHA512:
6
- metadata.gz: 960e0c3a36e2c2da145f398417813424820b924a34b7926b001ba024e0409246ed51dfc7922be8576ee9e58da8648ef7a89e38e4f0afcaa1cc85ba8c6cb164c9
7
- data.tar.gz: ec88e8230a332139b4a136d6f28745cf0f733286f5023263d29036a7b41e2a6e5577b51bf9cb2374cb49aef4d0521b094f7d242c6a40fd6de00d4e03452bd5ff
6
+ metadata.gz: b2292a4972ce1fcf883a828d17479f1580e3145e0db2d26ff6044a73495d2e151d60f0bd98fd5b2f945c3b74cdff879f9f7779073487c05c70a078306b069bbe
7
+ data.tar.gz: 27bf6b4bcea1b2eb9d5dac761d5465ce478af02a4074f0a20038b7f8a21c528b66faf399d4364702ebf22f06cc0a0112d13a4ee2e10acb22eb16b52859aaf4b6
data/exe/luca-book CHANGED
@@ -95,6 +95,12 @@ class LucaCmd
95
95
  puts YAML.dump(dat)
96
96
  end
97
97
  end
98
+
99
+ class Dict < LucaCmd
100
+ def self.update_balance(args, params)
101
+ LucaBook::Dict.generate_balance(*args)
102
+ end
103
+ end
98
104
  end
99
105
 
100
106
  def new_pj(args = nil, params = {})
@@ -120,7 +126,7 @@ when /journals?/, 'j'
120
126
  when 'list'
121
127
  OptionParser.new do |opt|
122
128
  opt.banner = 'Usage: luca-book journals list [options] [YYYY M]'
123
- opt.on('-c', '--code VAL', 'search with code') { |v| params['code'] = v }
129
+ opt.on('-c', '--code VAL', 'filter with code or label') { |v| params['code'] = v }
124
130
  opt.on('--customer', 'categorize by x-customer header') { |_v| params['headers'] = 'x-customer' }
125
131
  opt.on('-n VAL', 'report count') { |v| params[:n] = v.to_i }
126
132
  opt.on('--nu', 'show table in nushell') { |_v| params[:output] = 'nu' }
@@ -142,7 +148,7 @@ when /journals?/, 'j'
142
148
  when 'stats'
143
149
  OptionParser.new do |opt|
144
150
  opt.banner = 'Usage: luca-book journals stats [options] [YYYY M]'
145
- opt.on('-c', '--code VAL', 'search with code') { |v| params['code'] = v }
151
+ opt.on('-c', '--code VAL', 'filter with code or label') { |v| params['code'] = v }
146
152
  opt.on('-n VAL', 'report count') { |v| params[:n] = v.to_i }
147
153
  opt.on('--nu', 'show table in nushell') { |_v| params[:output] = 'nu' }
148
154
  opt.on('-o', '--output VAL', 'output serialized data') { |v| params[:output] = v }
@@ -211,6 +217,16 @@ when /reports?/, 'r'
211
217
  puts ' pl: show statement of income'
212
218
  exit 1
213
219
  end
220
+ when /balance/
221
+ subcmd = ARGV.shift
222
+ case subcmd
223
+ when 'update'
224
+ OptionParser.new do |opt|
225
+ opt.banner = 'Usage: luca-book balance update YYYY [M]'
226
+ args = opt.parse!(ARGV)
227
+ LucaCmd::Dict.update_balance(args, params)
228
+ end
229
+ end
214
230
  else
215
231
  puts 'Proper subcommand needed.'
216
232
  puts
data/lib/luca_book.rb CHANGED
@@ -5,6 +5,7 @@ require 'luca_record'
5
5
  require 'luca_book/version'
6
6
 
7
7
  module LucaBook
8
+ autoload :Accumulator, 'luca_book/accumulator'
8
9
  autoload :Dict, 'luca_book/dict'
9
10
  autoload :Import, 'luca_book/import'
10
11
  autoload :Journal, 'luca_book/journal'
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'luca_book/util'
4
+ require 'luca_support/config'
5
+
6
+ module LucaBook
7
+ module Accumulator
8
+ def self.included(klass) # :nodoc:
9
+ klass.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ def accumulate_month(year, month)
14
+ monthly_record, count = net(year, month)
15
+ [total_subaccount(monthly_record), count]
16
+ end
17
+
18
+ # Accumulate Level 2, 3 account.
19
+ #
20
+ def total_subaccount(report)
21
+ {}.tap do |res|
22
+ res['A0'] = sum_matched(report, /^[A][0-9A-Z]{2,}/)
23
+ res['B0'] = sum_matched(report, /^[B][0-9A-Z]{2,}/)
24
+ res['BA'] = res['A0'] - res['B0']
25
+ res['C0'] = sum_matched(report, /^[C][0-9A-Z]{2,}/)
26
+ res['CA'] = res['BA'] - res['C0']
27
+ res['D0'] = sum_matched(report, /^[D][0-9A-Z]{2,}/)
28
+ res['E0'] = sum_matched(report, /^[E][0-9A-Z]{2,}/)
29
+ res['EA'] = res['CA'] + res['D0'] - res['E0']
30
+ res['F0'] = sum_matched(report, /^[F][0-9A-Z]{2,}/)
31
+ res['G0'] = sum_matched(report, /^[G][0-9][0-9A-Z]{1,}/)
32
+ res['GA'] = res['EA'] + res['F0'] - res['G0']
33
+ res['H0'] = sum_matched(report, /^[H][0-9][0-9A-Z]{1,}/)
34
+ res['HA'] = res['GA'] - res['H0']
35
+
36
+ report['9142'] = (report['9142'] || BigDecimal('0')) + res['HA']
37
+ res['9142'] = report['9142']
38
+ res['10'] = sum_matched(report, /^[12][0-9A-Z]{2,}/)
39
+ jp_4v = sum_matched(report, /^[4][V]{2,}/) # deferred assets for JP GAAP
40
+ res['30'] = sum_matched(report, /^[34][0-9A-Z]{2,}/) - jp_4v
41
+ res['4V'] = jp_4v if LucaSupport::CONFIG['country'] == 'jp'
42
+ res['50'] = sum_matched(report, /^[56][0-9A-Z]{2,}/)
43
+ res['70'] = sum_matched(report, /^[78][0-9A-Z]{2,}/)
44
+ res['91'] = sum_matched(report, /^91[0-9A-Z]{1,}/)
45
+ res['8ZZ'] = res['50'] + res['70']
46
+ res['9ZZ'] = sum_matched(report, /^[9][0-9A-Z]{2,}/)
47
+
48
+ res['1'] = res['10'] + res['30']
49
+ res['5'] = res['8ZZ'] + res['9ZZ']
50
+ res['_d'] = report['_d']
51
+
52
+ report.each do |k, v|
53
+ res[k] ||= sum_matched(report, /^#{k}[0-9A-Z]{1,}/) if k.length == 2
54
+ res[k] = v if k.length == 3
55
+ end
56
+
57
+ report.each do |k, v|
58
+ if k.length >= 4
59
+ if res[k[0, 3]]
60
+ res[k[0, 3]] += v
61
+ else
62
+ res[k[0, 3]] = v
63
+ end
64
+ res[k] = v
65
+ end
66
+ end
67
+ res.sort.to_h
68
+ end
69
+ end
70
+
71
+ def sum_matched(report, reg)
72
+ report.select { |k, v| reg.match(k)}.values.sum
73
+ end
74
+
75
+ # for assert purpose
76
+ #
77
+ def gross(start_year, start_month, end_year = nil, end_month = nil, code: nil, date_range: nil, rows: 4)
78
+ if ! date_range.nil?
79
+ raise if date_range.class != Range
80
+ # TODO: date based range search
81
+ end
82
+
83
+ end_year ||= start_year
84
+ end_month ||= start_month
85
+ sum = { debit: {}, credit: {}, debit_count: {}, credit_count: {} }
86
+ idx_memo = []
87
+ term(start_year, start_month, end_year, end_month, code) do |f, _path|
88
+ CSV.new(f, headers: false, col_sep: "\t", encoding: 'UTF-8')
89
+ .each_with_index do |row, i|
90
+ break if i >= rows
91
+
92
+ case i
93
+ when 0
94
+ idx_memo = row.map(&:to_s)
95
+ next if code && !idx_memo.include?(code)
96
+
97
+ idx_memo.each do |r|
98
+ sum[:debit][r] ||= BigDecimal('0')
99
+ sum[:debit_count][r] ||= 0
100
+ end
101
+ when 1
102
+ next if code && !idx_memo.include?(code)
103
+
104
+ row.each_with_index do |r, j|
105
+ sum[:debit][idx_memo[j]] += BigDecimal(r.to_s)
106
+ sum[:debit_count][idx_memo[j]] += 1
107
+ end
108
+ when 2
109
+ idx_memo = row.map(&:to_s)
110
+ break if code && !idx_memo.include?(code)
111
+
112
+ idx_memo.each do |r|
113
+ sum[:credit][r] ||= BigDecimal('0')
114
+ sum[:credit_count][r] ||= 0
115
+ end
116
+ when 3
117
+ row.each_with_index do |r, j|
118
+ sum[:credit][idx_memo[j]] += BigDecimal(r.to_s)
119
+ sum[:credit_count][idx_memo[j]] += 1
120
+ end
121
+ else
122
+ puts row # for debug
123
+ end
124
+ end
125
+ end
126
+ if code
127
+ sum[:debit] = sum[:debit][code] || BigDecimal('0')
128
+ sum[:credit] = sum[:credit][code] || BigDecimal('0')
129
+ sum[:debit_count] = sum[:debit_count][code] || 0
130
+ sum[:credit_count] = sum[:credit_count][code] || 0
131
+ end
132
+ sum
133
+ end
134
+
135
+ # netting vouchers in specified term
136
+ #
137
+ def net(start_year, start_month, end_year = nil, end_month = nil, code: nil, date_range: nil)
138
+ g = gross(start_year, start_month, end_year, end_month, code: code, date_range: date_range)
139
+ idx = (g[:debit].keys + g[:credit].keys).uniq.sort
140
+ count = {}
141
+ diff = {}.tap do |sum|
142
+ idx.each do |code|
143
+ sum[code] = g.dig(:debit, code).nil? ? BigDecimal('0') : LucaBook::Util.calc_diff(g[:debit][code], code)
144
+ sum[code] -= g.dig(:credit, code).nil? ? BigDecimal('0') : LucaBook::Util.calc_diff(g[:credit][code], code)
145
+ count[code] = (g.dig(:debit_count, code) || 0) + (g.dig(:credit_count, code) || 0)
146
+ end
147
+ end
148
+ [diff, count]
149
+ end
150
+ end
151
+ end
152
+ end
@@ -1,12 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'luca_support/code'
3
4
  require 'luca_support/config'
4
5
  require 'luca_record/dict'
6
+ require 'luca_record/io'
7
+ require 'luca_book'
5
8
  require 'date'
6
9
  require 'pathname'
7
10
 
8
11
  module LucaBook
9
12
  class Dict < LucaRecord::Dict
13
+ include Accumulator
14
+ include LucaRecord::IO
15
+
16
+ @dirname = 'journals'
17
+ @record_type = 'raw'
10
18
  # Column number settings for CSV/TSV convert
11
19
  #
12
20
  # :label
@@ -84,14 +92,93 @@ module LucaBook
84
92
  nil
85
93
  end
86
94
 
87
- def self.latest_balance
95
+ # Find balance at financial year start by given date.
96
+ # If not found 'start-yyyy-mm-*.tsv', use 'start.tsv' as default.
97
+ #
98
+ def self.latest_balance(date)
99
+ load_tsv_dict(latest_balance_path(date))
100
+ end
101
+
102
+ def self.latest_balance_path(date)
103
+ start_year = date.month >= LucaSupport::CONFIG['fy_start'] ? date.year : date.year - 1
104
+ latest = Date.new(start_year, LucaSupport::CONFIG['fy_start'], 1).prev_month
88
105
  dict_dir = Pathname(LucaSupport::PJDIR) / 'data' / 'balance'
89
- # TODO: search latest balance dictionary
90
- load_tsv_dict(dict_dir / 'start.tsv')
106
+ fileglob = %Q(start-#{latest.year}-#{format("%02d", latest.month)}-*)
107
+ path = Dir.glob(fileglob, base: dict_dir)[0] || 'start.tsv'
108
+ dict_dir / path
91
109
  end
92
110
 
93
111
  def self.issue_date(obj)
94
112
  Date.parse(obj.dig('_date', :label))
95
113
  end
114
+
115
+ def self.generate_balance(year, month = nil)
116
+ start_date = Date.new((year.to_i - 1), LucaSupport::CONFIG['fy_start'], 1)
117
+ month ||= LucaSupport::CONFIG['fy_start'] - 1
118
+ end_date = Date.new(year.to_i, month, -1)
119
+ labels = load('base.tsv')
120
+ bs = load_balance(start_date, end_date)
121
+ fy_digest = checksum(start_date, end_date)
122
+ current_ref = gitref
123
+ csv = CSV.generate(String.new, col_sep: "\t", headers: false) do |f|
124
+ f << ['code', 'label', 'balance']
125
+ f << ['_date', end_date]
126
+ f << ['_digest', fy_digest]
127
+ f << ['_gitref', current_ref] if current_ref
128
+ bs.each do |code, balance|
129
+ next if LucaSupport::Code.readable(balance) == 0
130
+
131
+ f << [code, labels.dig(code, :label), LucaSupport::Code.readable(balance)]
132
+ end
133
+ end
134
+ dict_dir = Pathname(LucaSupport::PJDIR) / 'data' / 'balance'
135
+ filepath = dict_dir / "start-#{end_date.to_s}.tsv"
136
+
137
+ File.open(filepath, 'w') { |f| f.write csv }
138
+ end
139
+
140
+ def self.load_balance(start_date, end_date)
141
+ base = latest_balance(start_date).each_with_object({}) do |(k, v), h|
142
+ h[k] = BigDecimal(v[:balance].to_s) if v[:balance]
143
+ end
144
+
145
+ search_range = term_by_month(start_date, end_date)
146
+ bs = search_range.each_with_object(base) do |date, h|
147
+ net(date.year, date.month)[0].each do |code, amount|
148
+ next if /^[^1-9]/.match(code)
149
+
150
+ h[code] ||= BigDecimal('0')
151
+ h[code] += amount
152
+ end
153
+ end
154
+ bs['9142'] ||= BigDecimal('0')
155
+ bs['9142'] += LucaBook::State
156
+ .range(start_date.year, start_date.month, end_date.year, end_date.month)
157
+ .net_income
158
+ bs.sort
159
+ end
160
+
161
+ def self.checksum(start_date, end_date)
162
+ digest = update_digest(String.new, File.read(latest_balance_path(start_date)))
163
+ term_by_month(start_date, end_date)
164
+ .map { |date| dir_digest(date.year, date.month) }
165
+ .each { |month_digest| digest = update_digest(digest, month_digest) }
166
+ digest
167
+ end
168
+
169
+ def self.gitref
170
+ digest = `git rev-parse HEAD`
171
+ $?.exitstatus == 0 ? digest.strip : nil
172
+ end
173
+
174
+ def self.term_by_month(start_date, end_date)
175
+ Enumerator.new do |yielder|
176
+ each_month = start_date
177
+ while each_month <= end_date
178
+ yielder << each_month
179
+ each_month = each_month.next_month
180
+ end
181
+ end
182
+ end
96
183
  end
97
184
  end
@@ -12,16 +12,17 @@ module LucaBook #:nodoc:
12
12
  #
13
13
  class List < LucaBook::Journal
14
14
  @dirname = 'journals'
15
+ @@dict = LucaRecord::Dict.new('base.tsv')
15
16
  attr_reader :data
16
17
 
17
18
  def initialize(data, start_date, code = nil)
18
19
  @data = data
19
20
  @code = code
20
21
  @start = start_date
21
- @dict = LucaRecord::Dict.load('base.tsv')
22
22
  end
23
23
 
24
24
  def self.term(from_year, from_month, to_year = from_year, to_month = from_month, code: nil, basedir: @dirname)
25
+ code = search_code(code) if code
25
26
  data = LucaBook::Journal.term(from_year, from_month, to_year, to_month, code).select do |dat|
26
27
  if code.nil?
27
28
  true
@@ -85,12 +86,23 @@ module LucaBook #:nodoc:
85
86
  end
86
87
  end
87
88
 
89
+ def self.search_code(code)
90
+ return code if @@dict.dig(code)
91
+
92
+ @@dict.search(code).tap do |new_code|
93
+ if new_code.nil?
94
+ puts "Search word is not matched with labels"
95
+ exit 1
96
+ end
97
+ end
98
+ end
99
+
88
100
  private
89
101
 
90
102
  def set_balance
91
103
  return BigDecimal('0') if @code.nil? || /^[A-H]/.match(@code)
92
104
 
93
- balance_dict = Dict.latest_balance
105
+ balance_dict = Dict.latest_balance(@start)
94
106
  start_balance = BigDecimal(balance_dict.dig(@code.to_s, :balance) || '0')
95
107
  start = Dict.issue_date(balance_dict)&.next_month
96
108
  last = @start.prev_month
@@ -120,20 +132,16 @@ module LucaBook #:nodoc:
120
132
  def convert_label
121
133
  @data.each do |dat|
122
134
  if @code
123
- dat[:code] = "#{dat[:code]} #{@dict.dig(dat[:code], :label)}"
124
- dat[:counter_code] = dat[:counter_code].map { |counter| "#{counter} #{@dict.dig(counter, :label)}" }
135
+ dat[:code] = "#{dat[:code]} #{@@dict.dig(dat[:code], :label)}"
136
+ dat[:counter_code] = dat[:counter_code].map { |counter| "#{counter} #{@@dict.dig(counter, :label)}" }
125
137
  else
126
- dat[:debit].each { |debit| debit[:code] = "#{debit[:code]} #{@dict.dig(debit[:code], :label)}" }
127
- dat[:credit].each { |credit| credit[:code] = "#{credit[:code]} #{@dict.dig(credit[:code], :label)}" }
138
+ dat[:debit].each { |debit| debit[:code] = "#{debit[:code]} #{@@dict.dig(debit[:code], :label)}" }
139
+ dat[:credit].each { |credit| credit[:code] = "#{credit[:code]} #{@@dict.dig(credit[:code], :label)}" }
128
140
  end
129
141
  end
130
142
  self
131
143
  end
132
144
 
133
- def dict
134
- LucaBook::Dict::Data
135
- end
136
-
137
145
  def code_header
138
146
  {}.tap do |h|
139
147
  %w[code date no id diff balance counter_code note].each do |k|
@@ -67,7 +67,7 @@ module LucaBook #:nodoc:
67
67
  def set_balance
68
68
  return BigDecimal('0') if @code.nil? || /^[A-H]/.match(@code)
69
69
 
70
- balance_dict = Dict.latest_balance
70
+ balance_dict = Dict.latest_balance(@start)
71
71
  start_balance = BigDecimal(balance_dict.dig(@code.to_s, :balance) || '0')
72
72
  start = Dict.issue_date(balance_dict)&.next_month
73
73
  last = @start.prev_month
@@ -14,15 +14,17 @@ require 'luca_book'
14
14
  #
15
15
  module LucaBook
16
16
  class State < LucaRecord::Base
17
+ include Accumulator
18
+
17
19
  @dirname = 'journals'
18
20
  @record_type = 'raw'
21
+ @@dict = LucaRecord::Dict.new('base.tsv')
19
22
 
20
23
  attr_reader :statement, :pl_data, :bs_data, :start_balance
21
24
 
22
25
  def initialize(data, count = nil, start_d: nil, end_d: nil)
23
26
  @monthly = data
24
27
  @count = count
25
- @dict = LucaRecord::Dict.load('base.tsv')
26
28
  @start_date = start_d
27
29
  @end_date = end_d
28
30
  @start_balance = set_balance
@@ -49,6 +51,7 @@ module LucaBook
49
51
  end
50
52
 
51
53
  def self.by_code(code, from_year, from_month, to_year = from_year, to_month = from_month)
54
+ code = search_code(code) if code
52
55
  date = Date.new(from_year.to_i, from_month.to_i, -1)
53
56
  last_date = Date.new(to_year.to_i, to_month.to_i, -1)
54
57
  raise 'invalid term specified' if date > last_date
@@ -79,7 +82,7 @@ module LucaBook
79
82
  @statement ||= @monthly
80
83
  @statement.map do |report|
81
84
  {}.tap do |h|
82
- report.each { |k, v| h[@dict.dig(k, :label) || k] = v }
85
+ report.each { |k, v| h[@@dict.dig(k, :label) || k] = v }
83
86
  end
84
87
  end
85
88
  end
@@ -104,7 +107,7 @@ module LucaBook
104
107
  data.sort.to_h
105
108
  end
106
109
  keys.map! { |k| k[0, level] }.uniq.select! { |k| k.length <= level } if level
107
- @count.prepend({}.tap { |header| keys.each { |k| header[k] = @dict.dig(k, :label) }})
110
+ @count.prepend({}.tap { |header| keys.each { |k| header[k] = @@dict.dig(k, :label) }})
108
111
  @count
109
112
  end
110
113
 
@@ -137,7 +140,7 @@ module LucaBook
137
140
  @statement = [].tap do |a|
138
141
  rows.times do |i|
139
142
  {}.tap do |res|
140
- res['debit_label'] = base[:debit][i] ? @dict.dig(base[:debit][i].keys[0], :label) : ''
143
+ res['debit_label'] = base[:debit][i] ? @@dict.dig(base[:debit][i].keys[0], :label) : ''
141
144
  #res['debit_balance'] = base[:debit][i] ? (@start_balance.dig(base[:debit][i].keys[0]) || 0) + base[:debit][i].values[0] : ''
142
145
  res['debit_balance'] = base[:debit][i] ? base[:debit][i].values[0] : ''
143
146
  res['credit_label'] = base[:credit][i] ? @dict.dig(base[:credit][i].keys[0], :label) : ''
@@ -174,6 +177,12 @@ module LucaBook
174
177
  readable(code2label)
175
178
  end
176
179
 
180
+ #
181
+ def net_income
182
+ set_pl(2)
183
+ @pl_data['HA']
184
+ end
185
+
177
186
  def self.accumulate_term(start_year, start_month, end_year, end_month)
178
187
  date = Date.new(start_year, start_month, 1)
179
188
  last_date = Date.new(end_year, end_month, -1)
@@ -189,64 +198,6 @@ module LucaBook
189
198
  end
190
199
  end
191
200
 
192
- def self.accumulate_month(year, month)
193
- monthly_record, count = net(year, month)
194
- [total_subaccount(monthly_record), count]
195
- end
196
-
197
- # Accumulate Level 2, 3 account.
198
- #
199
- def self.total_subaccount(report)
200
- {}.tap do |res|
201
- res['A0'] = sum_matched(report, /^[A][0-9A-Z]{2,}/)
202
- res['B0'] = sum_matched(report, /^[B][0-9A-Z]{2,}/)
203
- res['BA'] = res['A0'] - res['B0']
204
- res['C0'] = sum_matched(report, /^[C][0-9A-Z]{2,}/)
205
- res['CA'] = res['BA'] - res['C0']
206
- res['D0'] = sum_matched(report, /^[D][0-9A-Z]{2,}/)
207
- res['E0'] = sum_matched(report, /^[E][0-9A-Z]{2,}/)
208
- res['EA'] = res['CA'] + res['D0'] - res['E0']
209
- res['F0'] = sum_matched(report, /^[F][0-9A-Z]{2,}/)
210
- res['G0'] = sum_matched(report, /^[G][0-9][0-9A-Z]{1,}/)
211
- res['GA'] = res['EA'] + res['F0'] - res['G0']
212
- res['H0'] = sum_matched(report, /^[H][0-9][0-9A-Z]{1,}/)
213
- res['HA'] = res['GA'] - res['H0']
214
-
215
- report['9142'] = (report['9142'] || BigDecimal('0')) + res['HA']
216
- res['9142'] = report['9142']
217
- res['10'] = sum_matched(report, /^[12][0-9A-Z]{2,}/)
218
- jp_4v = sum_matched(report, /^[4][V]{2,}/) # deferred assets for JP GAAP
219
- res['30'] = sum_matched(report, /^[34][0-9A-Z]{2,}/) - jp_4v
220
- res['4V'] = jp_4v if CONFIG['country'] == 'jp'
221
- res['50'] = sum_matched(report, /^[56][0-9A-Z]{2,}/)
222
- res['70'] = sum_matched(report, /^[78][0-9A-Z]{2,}/)
223
- res['91'] = sum_matched(report, /^91[0-9A-Z]{1,}/)
224
- res['8ZZ'] = res['50'] + res['70']
225
- res['9ZZ'] = sum_matched(report, /^[9][0-9A-Z]{2,}/)
226
-
227
- res['1'] = res['10'] + res['30']
228
- res['5'] = res['8ZZ'] + res['9ZZ']
229
- res['_d'] = report['_d']
230
-
231
- report.each do |k, v|
232
- res[k] ||= sum_matched(report, /^#{k}[0-9A-Z]{1,}/) if k.length == 2
233
- res[k] = v if k.length == 3
234
- end
235
-
236
- report.each do |k, v|
237
- if k.length >= 4
238
- if res[k[0, 3]]
239
- res[k[0, 3]] += v
240
- else
241
- res[k[0, 3]] = v
242
- end
243
- res[k] = v
244
- end
245
- end
246
- res.sort.to_h
247
- end
248
- end
249
-
250
201
  def code_sum(report)
251
202
  legal_items.each.with_object({}) do |k, h|
252
203
  h[k] = self.class.sum_matched(report, /^#{k}.*/)
@@ -262,7 +213,7 @@ module LucaBook
262
213
  end
263
214
  pre = self.class.accumulate_term(start_year, CONFIG['fy_start'], pre_last.year, pre_last.month)
264
215
 
265
- base = Dict.latest_balance.each_with_object({}) do |(k, v), h|
216
+ base = Dict.latest_balance(@start_date).each_with_object({}) do |(k, v), h|
266
217
  h[k] = BigDecimal(v[:balance].to_s) if v[:balance]
267
218
  h[k] ||= BigDecimal('0') if k.length == 2
268
219
  end
@@ -275,86 +226,6 @@ module LucaBook
275
226
  self.class.total_subaccount(base)
276
227
  end
277
228
 
278
- def self.sum_matched(report, reg)
279
- report.select { |k, v| reg.match(k)}.values.sum
280
- end
281
-
282
- # for assert purpose
283
- #
284
- def self.gross(start_year, start_month, end_year = nil, end_month = nil, code: nil, date_range: nil, rows: 4)
285
- if ! date_range.nil?
286
- raise if date_range.class != Range
287
- # TODO: date based range search
288
- end
289
-
290
- end_year ||= start_year
291
- end_month ||= start_month
292
- sum = { debit: {}, credit: {}, debit_count: {}, credit_count: {} }
293
- idx_memo = []
294
- term(start_year, start_month, end_year, end_month, code) do |f, _path|
295
- CSV.new(f, headers: false, col_sep: "\t", encoding: 'UTF-8')
296
- .each_with_index do |row, i|
297
- break if i >= rows
298
-
299
- case i
300
- when 0
301
- idx_memo = row.map(&:to_s)
302
- next if code && !idx_memo.include?(code)
303
-
304
- idx_memo.each do |r|
305
- sum[:debit][r] ||= BigDecimal('0')
306
- sum[:debit_count][r] ||= 0
307
- end
308
- when 1
309
- next if code && !idx_memo.include?(code)
310
-
311
- row.each_with_index do |r, j|
312
- sum[:debit][idx_memo[j]] += BigDecimal(r.to_s)
313
- sum[:debit_count][idx_memo[j]] += 1
314
- end
315
- when 2
316
- idx_memo = row.map(&:to_s)
317
- break if code && !idx_memo.include?(code)
318
-
319
- idx_memo.each do |r|
320
- sum[:credit][r] ||= BigDecimal('0')
321
- sum[:credit_count][r] ||= 0
322
- end
323
- when 3
324
- row.each_with_index do |r, j|
325
- sum[:credit][idx_memo[j]] += BigDecimal(r.to_s)
326
- sum[:credit_count][idx_memo[j]] += 1
327
- end
328
- else
329
- puts row # for debug
330
- end
331
- end
332
- end
333
- if code
334
- sum[:debit] = sum[:debit][code] || BigDecimal('0')
335
- sum[:credit] = sum[:credit][code] || BigDecimal('0')
336
- sum[:debit_count] = sum[:debit_count][code] || 0
337
- sum[:credit_count] = sum[:credit_count][code] || 0
338
- end
339
- sum
340
- end
341
-
342
- # netting vouchers in specified term
343
- #
344
- def self.net(start_year, start_month, end_year = nil, end_month = nil, code: nil, date_range: nil)
345
- g = gross(start_year, start_month, end_year, end_month, code: code, date_range: date_range)
346
- idx = (g[:debit].keys + g[:credit].keys).uniq.sort
347
- count = {}
348
- diff = {}.tap do |sum|
349
- idx.each do |code|
350
- sum[code] = g.dig(:debit, code).nil? ? BigDecimal('0') : Util.calc_diff(g[:debit][code], code)
351
- sum[code] -= g.dig(:credit, code).nil? ? BigDecimal('0') : Util.calc_diff(g[:credit][code], code)
352
- count[code] = (g.dig(:debit_count, code) || 0) + (g.dig(:credit_count, code) || 0)
353
- end
354
- end
355
- [diff, count]
356
- end
357
-
358
229
  def render_xbrl(filename = nil)
359
230
  set_bs(3, legal: true)
360
231
  set_pl(3)
@@ -378,7 +249,7 @@ module LucaBook
378
249
  return nil if /^_/.match(code)
379
250
 
380
251
  context = /^[0-9]/.match(code) ? 'CurrentYearNonConsolidatedInstant' : 'CurrentYearNonConsolidatedDuration'
381
- tag = @dict.dig(code, :xbrl_id)
252
+ tag = @@dict.dig(code, :xbrl_id)
382
253
  #raise "xbrl_id not found: #{code}" if tag.nil?
383
254
  return nil if tag.nil?
384
255
  return nil if readable(amount).zero? && prior_amount.nil?
@@ -389,6 +260,17 @@ module LucaBook
389
260
  prior + current
390
261
  end
391
262
 
263
+ def self.search_code(code)
264
+ return code if @@dict.dig(code)
265
+
266
+ @@dict.search(code).tap do |new_code|
267
+ if new_code.nil?
268
+ puts "Search word is not matched with labels"
269
+ exit 1
270
+ end
271
+ end
272
+ end
273
+
392
274
  private
393
275
 
394
276
  def set_bs(level = 3, legal: false)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucaBook
4
- VERSION = '0.2.25'
4
+ VERSION = '0.2.26'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lucabook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.25
4
+ version: 0.2.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuma Takahiro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-12 00:00:00.000000000 Z
11
+ date: 2021-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lucarecord
@@ -79,6 +79,7 @@ files:
79
79
  - LICENSE
80
80
  - exe/luca-book
81
81
  - lib/luca_book.rb
82
+ - lib/luca_book/accumulator.rb
82
83
  - lib/luca_book/console.rb
83
84
  - lib/luca_book/dict.rb
84
85
  - lib/luca_book/import.rb