lucabook 0.2.25 → 0.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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