lucabook 0.2.21 → 0.2.26

Sign up to get free protection for your applications and to get access to all the features.
@@ -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