lucabook 0.2.21 → 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.
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'date'
5
+ require 'luca_support'
6
+ require 'luca_record'
7
+ require 'luca_record/dict'
8
+ require 'luca_book'
9
+
10
+ module LucaBook #:nodoc:
11
+ # Journal List on specified term
12
+ #
13
+ class ListByHeader < LucaBook::Journal
14
+ @dirname = 'journals'
15
+
16
+ def initialize(data, start_date, code = nil, header_name = nil)
17
+ @data = data
18
+ @code = code
19
+ @header = header_name
20
+ @start = start_date
21
+ @dict = LucaRecord::Dict.load('base.tsv')
22
+ end
23
+
24
+ def self.term(from_year, from_month, to_year = from_year, to_month = from_month, code: nil, header: nil, basedir: @dirname)
25
+ data = Journal.term(from_year, from_month, to_year, to_month, code).select do |dat|
26
+ if code.nil?
27
+ true
28
+ else
29
+ [:debit, :credit].map { |key| serialize_on_key(dat[key], :code) }.flatten.include?(code)
30
+ end
31
+ end
32
+ new data, Date.new(from_year.to_i, from_month.to_i, 1), code, header
33
+ end
34
+
35
+ def list_by_code
36
+ calc_code
37
+ convert_label
38
+ @data = @data.each_with_object([]) do |(k, v), a|
39
+ journals = v.map do |dat|
40
+ date, txid = decode_id(dat[:id])
41
+ {}.tap do |res|
42
+ res['header'] = k
43
+ res['date'] = date
44
+ res['no'] = txid
45
+ res['id'] = dat[:id]
46
+ res['diff'] = dat[:diff]
47
+ res['balance'] = dat[:balance]
48
+ res['counter_code'] = dat[:counter_code].length == 1 ? dat[:counter_code].first : dat[:counter_code]
49
+ res['note'] = dat[:note]
50
+ end
51
+ end
52
+ a << { 'code' => v.last[:code], 'header' => k, 'balance' => v.last[:balance], 'count' => v.count, 'jounals' => journals }
53
+ end
54
+ readable(@data)
55
+ end
56
+
57
+ def accumulate_code
58
+ @data.each_with_object({}) do |dat, sum|
59
+ idx = dat.dig(:headers, @header) || 'others'
60
+ sum[idx] ||= BigDecimal('0')
61
+ sum[idx] += Util.diff_by_code(dat[:debit], @code) - Util.diff_by_code(dat[:credit], @code)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def set_balance
68
+ return BigDecimal('0') if @code.nil? || /^[A-H]/.match(@code)
69
+
70
+ balance_dict = Dict.latest_balance(@start)
71
+ start_balance = BigDecimal(balance_dict.dig(@code.to_s, :balance) || '0')
72
+ start = Dict.issue_date(balance_dict)&.next_month
73
+ last = @start.prev_month
74
+ if last.year >= start.year && last.month >= start.month
75
+ #TODO: start_balance to be implemented by header
76
+ self.class.term(start.year, start.month, last.year, last.month, code: @code).accumulate_code
77
+ else
78
+ #start_balance
79
+ end
80
+ end
81
+
82
+ def calc_code
83
+ raise 'no account code specified' if @code.nil?
84
+
85
+ @balance = set_balance
86
+ balance = @balance
87
+ res = {}
88
+ @data.each do |dat|
89
+ idx = dat.dig(:headers, @header) || 'others'
90
+ balance[idx] ||= BigDecimal('0')
91
+ res[idx] ||= []
92
+ {}.tap do |h|
93
+ h[:id] = dat[:id]
94
+ h[:diff] = Util.diff_by_code(dat[:debit], @code) - Util.diff_by_code(dat[:credit], @code)
95
+ balance[idx] += h[:diff]
96
+ h[:balance] = balance[idx]
97
+ h[:code] = @code
98
+ counter = h[:diff] * Util.pn_debit(@code) > 0 ? :credit : :debit
99
+ h[:counter_code] = dat[counter].map { |d| d[:code] }
100
+ h[:note] = dat[:note]
101
+ res[idx] << h
102
+ end
103
+ end
104
+ @data = res
105
+ self
106
+ end
107
+
108
+ def convert_label
109
+ @data.each do |_k, v|
110
+ v.each do |dat|
111
+ raise 'no account code specified' if @code.nil?
112
+
113
+ dat[:code] = "#{dat[:code]} #{@dict.dig(dat[:code], :label)}"
114
+ dat[:counter_code] = dat[:counter_code].map { |counter| "#{counter} #{@dict.dig(counter, :label)}" }
115
+ end
116
+ end
117
+ self
118
+ end
119
+ end
120
+ end
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  module LucaBook
7
7
  class Setup
8
8
  # create project skeleton under specified directory
9
- def self.create_project(country = nil, dir = LucaSupport::Config::Pjdir)
9
+ def self.create_project(country = nil, dir = LucaSupport::PJDIR)
10
10
  FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
11
11
  Dir.chdir(dir) do
12
12
  %w[data/journals data/balance dict].each do |subdir|
@@ -18,6 +18,7 @@ module LucaBook
18
18
  'dict-en.tsv'
19
19
  end
20
20
  FileUtils.cp("#{__dir__}/templates/#{dict}", 'dict/base.tsv') unless File.exist?('dict/base.tsv')
21
+ FileUtils.cp("#{__dir__}/templates/config.yml", 'config.yml') unless File.exist?('config.yml')
21
22
  prepare_starttsv(dict) unless File.exist? 'data/balance/start.tsv'
22
23
  end
23
24
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'cgi/escape'
3
4
  require 'csv'
5
+ require 'mail'
4
6
  require 'pathname'
5
7
  require 'date'
6
8
  require 'luca_support'
@@ -12,34 +14,23 @@ require 'luca_book'
12
14
  #
13
15
  module LucaBook
14
16
  class State < LucaRecord::Base
17
+ include Accumulator
18
+
15
19
  @dirname = 'journals'
16
20
  @record_type = 'raw'
21
+ @@dict = LucaRecord::Dict.new('base.tsv')
17
22
 
18
- attr_reader :statement
23
+ attr_reader :statement, :pl_data, :bs_data, :start_balance
19
24
 
20
- def initialize(data, count = nil, date: nil)
21
- @data = data
25
+ def initialize(data, count = nil, start_d: nil, end_d: nil)
26
+ @monthly = data
22
27
  @count = count
23
- @dict = LucaRecord::Dict.load('base.tsv')
24
- @start_date = date
28
+ @start_date = start_d
29
+ @end_date = end_d
25
30
  @start_balance = set_balance
26
31
  end
27
32
 
28
- # TODO: not compatible with LucaRecord::Base.open_records
29
- def search_tag(code)
30
- count = 0
31
- Dir.children(LucaSupport::Config::Pjdir).sort.each do |dir|
32
- next if ! FileTest.directory?(LucaSupport::Config::Pjdir+dir)
33
-
34
- open_records(datadir, dir, 3) do |row, i|
35
- next if i == 2
36
- count += 1 if row.include?(code)
37
- end
38
- end
39
- puts "#{code}: #{count}"
40
- end
41
-
42
- def self.term(from_year, from_month, to_year = from_year, to_month = from_month)
33
+ def self.range(from_year, from_month, to_year = from_year, to_month = from_month)
43
34
  date = Date.new(from_year.to_i, from_month.to_i, -1)
44
35
  last_date = Date.new(to_year.to_i, to_month.to_i, -1)
45
36
  raise 'invalid term specified' if date > last_date
@@ -53,10 +44,14 @@ module LucaBook
53
44
  date = Date.new(date.next_month.year, date.next_month.month, -1)
54
45
  end
55
46
  end
56
- new(reports, counts, date: Date.new(from_year.to_i, from_month.to_i, -1))
47
+ new(reports, counts,
48
+ start_d: Date.new(from_year.to_i, from_month.to_i, 1),
49
+ end_d: Date.new(to_year.to_i, to_month.to_i, -1)
50
+ )
57
51
  end
58
52
 
59
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
60
55
  date = Date.new(from_year.to_i, from_month.to_i, -1)
61
56
  last_date = Date.new(to_year.to_i, to_month.to_i, -1)
62
57
  raise 'invalid term specified' if date > last_date
@@ -64,7 +59,7 @@ module LucaBook
64
59
  reports = [].tap do |r|
65
60
  while date <= last_date do
66
61
  diff = {}.tap do |h|
67
- g = gross(date.year, date.month, code)
62
+ g = gross(date.year, date.month, code: code)
68
63
  sum = g.dig(:debit).nil? ? BigDecimal('0') : Util.calc_diff(g[:debit], code)
69
64
  sum -= g.dig(:credit).nil? ? BigDecimal('0') : Util.calc_diff(g[:credit], code)
70
65
  h['code'] = code
@@ -80,27 +75,14 @@ module LucaBook
80
75
  date = Date.new(date.next_month.year, date.next_month.month, -1)
81
76
  end
82
77
  end
83
- #YAML.dump(LucaSupport::Code.readable(reports)) #.tap{ |data| puts data }
84
- #new(reports, counts, date: Date.new(from_year.to_i, from_month.to_i, -1))
85
78
  LucaSupport::Code.readable(reports)
86
79
  end
87
80
 
88
- def records_with_balance(year, month, code, balance)
89
- @book.search(year, month, nil, code).each do |h|
90
- balance += Util.calc_diff(Util.amount_by_code(h[:debit], code), code) - Util.calc_diff(Util.amount_by_code(h[:credit], code), code)
91
- h[:balance] = balance
92
- end
93
- end
94
-
95
- def to_yaml
96
- YAML.dump(readable(code2label)).tap { |data| puts data }
97
- end
98
-
99
81
  def code2label
100
- @statement ||= @data
82
+ @statement ||= @monthly
101
83
  @statement.map do |report|
102
84
  {}.tap do |h|
103
- report.each { |k, v| h[@dict.dig(k, :label) || k] = v }
85
+ report.each { |k, v| h[@@dict.dig(k, :label) || k] = v }
104
86
  end
105
87
  end
106
88
  end
@@ -125,25 +107,45 @@ module LucaBook
125
107
  data.sort.to_h
126
108
  end
127
109
  keys.map! { |k| k[0, level] }.uniq.select! { |k| k.length <= level } if level
128
- @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) }})
129
111
  @count
130
112
  end
131
113
 
114
+ # TODO: pl/bs may not be immutable
115
+ def report_mail(level = 3)
116
+ @company = CONFIG.dig('company', 'name')
117
+ {}.tap do |res|
118
+ pl(level).reverse.each do |month|
119
+ month.each do |k, v|
120
+ res[k] ||= []
121
+ res[k] << v
122
+ end
123
+ end
124
+ @months = res['_d']
125
+ @pl = res.select{ |k,v| k != '_d' }
126
+ end
127
+ @bs = bs
128
+
129
+ mail = Mail.new
130
+ mail.to = CONFIG.dig('mail', 'preview') || CONFIG.dig('mail', 'from')
131
+ mail.subject = 'Financial Report available'
132
+ mail.html_part = Mail::Part.new(body: render_erb(search_template('monthly-report.html.erb')), content_type: 'text/html; charset=UTF-8')
133
+ LucaSupport::Mail.new(mail, PJDIR).deliver
134
+ end
135
+
132
136
  def bs(level = 3, legal: false)
133
- @start_balance.keys.each { |k| @data.first[k] ||= 0 }
134
- @data.map! { |data| data.select { |k, _v| k.length <= level } }
135
- @data.map! { |data| code_sum(data).merge(data) } if legal
136
- base = accumulate_balance(@data)
137
+ set_bs(level, legal: legal)
138
+ base = accumulate_balance(@bs_data)
137
139
  rows = [base[:debit].length, base[:credit].length].max
138
140
  @statement = [].tap do |a|
139
141
  rows.times do |i|
140
142
  {}.tap do |res|
141
- res['debit_label'] = base[:debit][i] ? @dict.dig(base[:debit][i].keys[0], :label) : ''
142
- res['debit_balance'] = base[:debit][i] ? (@start_balance.dig(base[:debit][i].keys[0]) || 0) + base[:debit][i].values[0] : ''
143
- res['debit_diff'] = base[:debit][i] ? base[:debit][i].values[0] : ''
143
+ res['debit_label'] = base[:debit][i] ? @@dict.dig(base[:debit][i].keys[0], :label) : ''
144
+ #res['debit_balance'] = base[:debit][i] ? (@start_balance.dig(base[:debit][i].keys[0]) || 0) + base[:debit][i].values[0] : ''
145
+ res['debit_balance'] = base[:debit][i] ? base[:debit][i].values[0] : ''
144
146
  res['credit_label'] = base[:credit][i] ? @dict.dig(base[:credit][i].keys[0], :label) : ''
145
- res['credit_balance'] = base[:credit][i] ? (@start_balance.dig(base[:credit][i].keys[0]) || 0) + base[:credit][i].values[0] : ''
146
- res['credit_diff'] = base[:credit][i] ? base[:credit][i].values[0] : ''
147
+ #res['credit_balance'] = base[:credit][i] ? (@start_balance.dig(base[:credit][i].keys[0]) || 0) + base[:credit][i].values[0] : ''
148
+ res['credit_balance'] = base[:credit][i] ? base[:credit][i].values[0] : ''
147
149
  a << res
148
150
  end
149
151
  end
@@ -151,12 +153,7 @@ module LucaBook
151
153
  readable(@statement)
152
154
  end
153
155
 
154
- def accumulate_balance(monthly_diffs)
155
- data = monthly_diffs.each_with_object({}) do |month, h|
156
- month.each do |k, v|
157
- h[k] = h[k].nil? ? v : h[k] + v
158
- end
159
- end
156
+ def accumulate_balance(data)
160
157
  { debit: [], credit: [] }.tap do |res|
161
158
  data.sort.to_h.each do |k, v|
162
159
  case k
@@ -170,103 +167,37 @@ module LucaBook
170
167
  end
171
168
 
172
169
  def pl(level = 2)
173
- term_keys = @data.inject([]) { |a, data| a + data.keys }
174
- .compact.select { |k| /^[A-H_].+/.match(k) }
175
- fy = @start_balance.select { |k, _v| /^[A-H].+/.match(k) }
176
- keys = (term_keys + fy.keys).uniq.sort
177
- keys.select! { |k| k.length <= level }
178
- @statement = @data.map do |data|
170
+ set_pl(level)
171
+ @statement = @monthly.map do |data|
179
172
  {}.tap do |h|
180
- keys.each { |k| h[k] = data[k] || BigDecimal('0') }
181
- end
182
- end
183
- term = @statement.each_with_object({}) do |item, h|
184
- item.each do |k, v|
185
- h[k] = h[k].nil? ? v : h[k] + v if /^[^_]/.match(k)
173
+ @pl_data.keys.each { |k| h[k] = data[k] || BigDecimal('0') }
186
174
  end
187
175
  end
188
- fy = {}.tap do |h|
189
- keys.each do |k|
190
- h[k] = BigDecimal(fy[k] || '0') + BigDecimal(term[k] || '0')
191
- end
192
- end
193
- @statement << term.tap { |h| h['_d'] = 'Period Total' }
194
- @statement << fy.tap { |h| h['_d'] = 'FY Total' }
176
+ @statement << @pl_data
195
177
  readable(code2label)
196
178
  end
197
179
 
180
+ #
181
+ def net_income
182
+ set_pl(2)
183
+ @pl_data['HA']
184
+ end
185
+
198
186
  def self.accumulate_term(start_year, start_month, end_year, end_month)
199
187
  date = Date.new(start_year, start_month, 1)
200
188
  last_date = Date.new(end_year, end_month, -1)
201
189
  return nil if date > last_date
202
190
 
203
191
  {}.tap do |res|
204
- while date <= last_date do
205
- diff, _count = net(date.year, date.month)
206
- diff.each do |k, v|
207
- next if /^[_]/.match(k)
192
+ diff, _count = net(date.year, date.month, last_date.year, last_date.month)
193
+ diff.each do |k, v|
194
+ next if /^[_]/.match(k)
208
195
 
209
- res[k] = res[k].nil? ? v : res[k] + v
210
- end
211
- date = date.next_month
196
+ res[k] = res[k].nil? ? v : res[k] + v
212
197
  end
213
198
  end
214
199
  end
215
200
 
216
- def self.accumulate_month(year, month)
217
- monthly_record, count = net(year, month)
218
- [total_subaccount(monthly_record), count]
219
- end
220
-
221
- # Accumulate Level 2, 3 account.
222
- #
223
- def self.total_subaccount(report)
224
- {}.tap do |res|
225
- res['A0'] = sum_matched(report, /^[A][0-9A-Z]{2,}/)
226
- res['B0'] = sum_matched(report, /^[B][0-9A-Z]{2,}/)
227
- res['BA'] = res['A0'] - res['B0']
228
- res['C0'] = sum_matched(report, /^[C][0-9A-Z]{2,}/)
229
- res['CA'] = res['BA'] - res['C0']
230
- res['D0'] = sum_matched(report, /^[D][0-9A-Z]{2,}/)
231
- res['E0'] = sum_matched(report, /^[E][0-9A-Z]{2,}/)
232
- res['EA'] = res['CA'] + res['D0'] - res['E0']
233
- res['F0'] = sum_matched(report, /^[F][0-9A-Z]{2,}/)
234
- res['G0'] = sum_matched(report, /^[G][0-9][0-9A-Z]{1,}/)
235
- res['GA'] = res['EA'] + res['F0'] - res['G0']
236
- res['H0'] = sum_matched(report, /^[H][0-9][0-9A-Z]{1,}/)
237
- res['HA'] = res['GA'] - res['H0']
238
-
239
- report['9142'] = (report['9142'] || BigDecimal('0')) + res['HA']
240
- res['9142'] = report['9142']
241
- res['10'] = sum_matched(report, /^[123][0-9A-Z]{2,}/)
242
- res['40'] = sum_matched(report, /^[4][0-9A-Z]{2,}/)
243
- res['50'] = sum_matched(report, /^[56][0-9A-Z]{2,}/)
244
- res['70'] = sum_matched(report, /^[78][0-9A-Z]{2,}/)
245
- res['91'] = sum_matched(report, /^91[0-9A-Z]{1,}/)
246
- res['8ZZ'] = res['50'] + res['70']
247
- res['9ZZ'] = sum_matched(report, /^[9][0-9A-Z]{2,}/)
248
-
249
- res['1'] = res['10'] + res['40']
250
- res['5'] = res['8ZZ'] + res['9ZZ']
251
- res['_d'] = report['_d']
252
-
253
- report.each do |k, v|
254
- res[k] = v if k.length == 3
255
- end
256
-
257
- report.each do |k, v|
258
- if k.length >= 4
259
- if res[k[0, 3]]
260
- res[k[0, 3]] += v
261
- else
262
- res[k[0, 3]] = v
263
- end
264
- end
265
- end
266
- res.sort.to_h
267
- end
268
- end
269
-
270
201
  def code_sum(report)
271
202
  legal_items.each.with_object({}) do |k, h|
272
203
  h[k] = self.class.sum_matched(report, /^#{k}.*/)
@@ -275,14 +206,16 @@ module LucaBook
275
206
 
276
207
  def set_balance
277
208
  pre_last = @start_date.prev_month
278
- pre = if @start_date.month > LucaSupport::CONFIG['fy_start'].to_i
279
- self.class.accumulate_term(pre_last.year, LucaSupport::CONFIG['fy_start'], pre_last.year, pre_last.month)
280
- elsif @start_date.month < LucaSupport::CONFIG['fy_start'].to_i
281
- self.class.accumulate_term(pre_last.year - 1, LucaSupport::CONFIG['fy_start'], pre_last.year, pre_last.month)
282
- end
283
-
284
- base = Dict.latest_balance.each_with_object({}) do |(k, v), h|
209
+ start_year = if @start_date.month > CONFIG['fy_start'].to_i
210
+ pre_last.year
211
+ else
212
+ pre_last.year - 1
213
+ end
214
+ pre = self.class.accumulate_term(start_year, CONFIG['fy_start'], pre_last.year, pre_last.month)
215
+
216
+ base = Dict.latest_balance(@start_date).each_with_object({}) do |(k, v), h|
285
217
  h[k] = BigDecimal(v[:balance].to_s) if v[:balance]
218
+ h[k] ||= BigDecimal('0') if k.length == 2
286
219
  end
287
220
  if pre
288
221
  idx = (pre.keys + base.keys).uniq
@@ -293,101 +226,93 @@ module LucaBook
293
226
  self.class.total_subaccount(base)
294
227
  end
295
228
 
296
- def self.sum_matched(report, reg)
297
- report.select { |k, v| reg.match(k)}.values.sum
229
+ def render_xbrl(filename = nil)
230
+ set_bs(3, legal: true)
231
+ set_pl(3)
232
+ country_suffix = CONFIG['country'] || 'en'
233
+ @company = CGI.escapeHTML(CONFIG.dig('company', 'name'))
234
+ @balance_sheet_selected = 'true'
235
+ @pl_selected = 'true'
236
+ @capital_change_selected = 'true'
237
+ @issue_date = Date.today
238
+
239
+ prior_bs = @start_balance.filter { |k, _v| /^[9]/.match(k) }
240
+ @xbrl_entries = @bs_data.map{ |k, v| xbrl_line(k, v, prior_bs[k]) }.compact.join("\n")
241
+ @xbrl_entries += @pl_data.map{ |k, v| xbrl_line(k, v) }.compact.join("\n")
242
+ @filename = filename || @issue_date.to_s
243
+
244
+ File.open("#{@filename}.xbrl", 'w') { |f| f.write render_erb(search_template("base-#{country_suffix}.xbrl.erb")) }
245
+ File.open("#{@filename}.xsd", 'w') { |f| f.write render_erb(search_template("base-#{country_suffix}.xsd.erb")) }
298
246
  end
299
247
 
300
- # for assert purpose
301
- #
302
- def self.gross(year, month = nil, code = nil, date_range = nil, rows = 4)
303
- if ! date_range.nil?
304
- raise if date_range.class != Range
305
- # TODO: date based range search
306
- end
248
+ def xbrl_line(code, amount, prior_amount = nil)
249
+ return nil if /^_/.match(code)
307
250
 
308
- sum = { debit: {}, credit: {}, debit_count: {}, credit_count: {} }
309
- idx_memo = []
310
- search(year, month, nil, code) do |f, _path|
311
- CSV.new(f, headers: false, col_sep: "\t", encoding: 'UTF-8')
312
- .each_with_index do |row, i|
313
- break if i >= rows
314
-
315
- case i
316
- when 0
317
- idx_memo = row.map(&:to_s)
318
- next if code && !idx_memo.include?(code)
319
-
320
- idx_memo.each do |r|
321
- sum[:debit][r] ||= BigDecimal('0')
322
- sum[:debit_count][r] ||= 0
323
- end
324
- when 1
325
- next if code && !idx_memo.include?(code)
326
-
327
- row.each_with_index do |r, j|
328
- sum[:debit][idx_memo[j]] += BigDecimal(r.to_s)
329
- sum[:debit_count][idx_memo[j]] += 1
330
- end
331
- when 2
332
- idx_memo = row.map(&:to_s)
333
- break if code && !idx_memo.include?(code)
334
-
335
- idx_memo.each do |r|
336
- sum[:credit][r] ||= BigDecimal('0')
337
- sum[:credit_count][r] ||= 0
338
- end
339
- when 3
340
- row.each_with_index do |r, j|
341
- sum[:credit][idx_memo[j]] += BigDecimal(r.to_s)
342
- sum[:credit_count][idx_memo[j]] += 1
343
- end
344
- else
345
- puts row # for debug
346
- end
251
+ context = /^[0-9]/.match(code) ? 'CurrentYearNonConsolidatedInstant' : 'CurrentYearNonConsolidatedDuration'
252
+ tag = @@dict.dig(code, :xbrl_id)
253
+ #raise "xbrl_id not found: #{code}" if tag.nil?
254
+ return nil if tag.nil?
255
+ return nil if readable(amount).zero? && prior_amount.nil?
256
+
257
+ prior = prior_amount.nil? ? '' : "<#{tag} decimals=\"0\" unitRef=\"JPY\" contextRef=\"Prior1YearNonConsolidatedInstant\">#{readable(prior_amount)}</#{tag}>\n"
258
+ current = "<#{tag} decimals=\"0\" unitRef=\"JPY\" contextRef=\"#{context}\">#{readable(amount)}</#{tag}>"
259
+
260
+ prior + current
261
+ end
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
347
270
  end
348
271
  end
349
- if code
350
- sum[:debit] = sum[:debit][code] || BigDecimal('0')
351
- sum[:credit] = sum[:credit][code] || BigDecimal('0')
352
- sum[:debit_count] = sum[:debit_count][code] || 0
353
- sum[:credit_count] = sum[:credit_count][code] || 0
354
- end
355
- sum
356
272
  end
357
273
 
358
- # netting vouchers in specified term
359
- #
360
- def self.net(year, month = nil, code = nil, date_range = nil)
361
- g = gross(year, month, code, date_range)
362
- idx = (g[:debit].keys + g[:credit].keys).uniq.sort
363
- count = {}
364
- diff = {}.tap do |sum|
365
- idx.each do |code|
366
- sum[code] = g.dig(:debit, code).nil? ? BigDecimal('0') : Util.calc_diff(g[:debit][code], code)
367
- sum[code] -= g.dig(:credit, code).nil? ? BigDecimal('0') : Util.calc_diff(g[:credit][code], code)
368
- count[code] = (g.dig(:debit_count, code) || 0) + (g.dig(:credit_count, code) || 0)
274
+ private
275
+
276
+ def set_bs(level = 3, legal: false)
277
+ @start_balance.each do |k, v|
278
+ next if /^_/.match(k)
279
+ @monthly.first[k] = (v || 0) + (@monthly.first[k] || 0)
280
+ end
281
+ list = @monthly.map { |data| data.select { |k, _v| k.length <= level } }
282
+ list.map! { |data| code_sum(data).merge(data) } if legal
283
+ @bs_data = list.each_with_object({}) do |month, h|
284
+ month.each do |k, v|
285
+ next if /^_/.match(k)
286
+
287
+ h[k] = (h[k] || BigDecimal('0')) + v
369
288
  end
370
289
  end
371
- [diff, count]
372
290
  end
373
291
 
374
- # TODO: obsolete in favor of Dict.latest_balance()
375
- def load_start
376
- file = Pathname(LucaSupport::Config::Pjdir) / 'data' / 'balance' / 'start.tsv'
377
- {}.tap do |dict|
378
- LucaRecord::Dict.load_tsv_dict(file).each { |k, v| h[k] = v[:balance] if !v[:balance].nil? }
292
+ def set_pl(level = 2)
293
+ keys = @monthly.inject([]) { |a, data| a + data.keys }
294
+ .compact.select { |k| /^[A-H_].+/.match(k) }
295
+ .uniq.sort
296
+ keys.select! { |k| k.length <= level }
297
+ @pl_data = @monthly.each_with_object({}) do |item, h|
298
+ keys.each do |k|
299
+ h[k] = (h[k] || BigDecimal('0')) + (item[k] || BigDecimal('0')) if /^[^_]/.match(k)
300
+ end
301
+ h['_d'] = 'Period Total'
379
302
  end
380
303
  end
381
304
 
382
- private
383
-
384
305
  def legal_items
385
- return [] unless LucaSupport::Config::COUNTRY
306
+ return [] unless CONFIG['country']
386
307
 
387
- case LucaSupport::Config::COUNTRY
308
+ case CONFIG['country']
388
309
  when 'jp'
389
- ['91', '911', '912', '913', '9131', '9132', '914', '9141', '9142', '915', '916', '92', '93']
310
+ ['31', '32', '33', '91', '911', '912', '913', '9131', '9132', '914', '9141', '9142', '915', '916', '92', '93']
390
311
  end
391
312
  end
313
+
314
+ def lib_path
315
+ __dir__
316
+ end
392
317
  end
393
318
  end