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.
- checksums.yaml +4 -4
- data/exe/luca-book +76 -7
- data/lib/luca_book.rb +2 -0
- data/lib/luca_book/accumulator.rb +152 -0
- data/lib/luca_book/console.rb +4 -4
- data/lib/luca_book/dict.rb +168 -4
- data/lib/luca_book/import.rb +36 -37
- data/lib/luca_book/import_jp.rb +83 -0
- data/lib/luca_book/journal.rb +98 -23
- data/lib/luca_book/list.rb +32 -17
- data/lib/luca_book/list_by_header.rb +120 -0
- data/lib/luca_book/setup.rb +2 -1
- data/lib/luca_book/state.rb +142 -217
- data/lib/luca_book/templates/base-jp.xbrl.erb +23 -48
- data/lib/luca_book/templates/base-jp.xsd.erb +17 -0
- data/lib/luca_book/templates/config.yml +4 -0
- data/lib/luca_book/templates/dict-en.tsv +45 -50
- data/lib/luca_book/templates/dict-jp-edinet.tsv +167 -0
- data/lib/luca_book/templates/dict-jp.tsv +192 -162
- data/lib/luca_book/templates/monthly-report.html.erb +67 -0
- data/lib/luca_book/version.rb +1 -1
- metadata +10 -3
@@ -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
|
data/lib/luca_book/setup.rb
CHANGED
@@ -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::
|
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
|
data/lib/luca_book/state.rb
CHANGED
@@ -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,
|
21
|
-
@
|
25
|
+
def initialize(data, count = nil, start_d: nil, end_d: nil)
|
26
|
+
@monthly = data
|
22
27
|
@count = count
|
23
|
-
@
|
24
|
-
@
|
28
|
+
@start_date = start_d
|
29
|
+
@end_date = end_d
|
25
30
|
@start_balance = set_balance
|
26
31
|
end
|
27
32
|
|
28
|
-
|
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,
|
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 ||= @
|
82
|
+
@statement ||= @monthly
|
101
83
|
@statement.map do |report|
|
102
84
|
{}.tap do |h|
|
103
|
-
report.each { |k, v| h[
|
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] =
|
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
|
-
|
134
|
-
|
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] ?
|
142
|
-
res['debit_balance'] = base[:debit][i] ? (@start_balance.dig(base[:debit][i].keys[0]) || 0) + base[:debit][i].values[0] : ''
|
143
|
-
res['
|
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['
|
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(
|
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
|
-
|
174
|
-
|
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
|
-
|
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
|
-
|
205
|
-
|
206
|
-
|
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
|
-
|
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
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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
|
297
|
-
|
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
|
-
|
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
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
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
|
-
|
359
|
-
|
360
|
-
def
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
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
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
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
|
306
|
+
return [] unless CONFIG['country']
|
386
307
|
|
387
|
-
case
|
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
|