lucabook 0.2.10 → 0.2.18
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 +121 -50
- data/lib/luca_book.rb +4 -0
- data/lib/luca_book/console.rb +7 -15
- data/lib/luca_book/dict.rb +10 -3
- data/lib/luca_book/import.rb +51 -43
- data/lib/luca_book/journal.rb +26 -22
- data/lib/luca_book/list.rb +138 -0
- data/lib/luca_book/setup.rb +23 -3
- data/lib/luca_book/state.rb +161 -100
- data/lib/luca_book/templates/base-jp.xbrl.erb +78 -0
- data/lib/luca_book/templates/dict-en.tsv +35 -31
- data/lib/luca_book/templates/dict-jp.tsv +163 -0
- data/lib/luca_book/util.rb +46 -0
- data/lib/luca_book/version.rb +1 -1
- metadata +6 -4
- data/lib/luca_book/report.rb +0 -3
- data/lib/luca_book/templates/dict-ja.tsv +0 -145
data/lib/luca_book/journal.rb
CHANGED
@@ -10,77 +10,81 @@ module LucaBook
|
|
10
10
|
class Journal < LucaRecord::Base
|
11
11
|
@dirname = 'journals'
|
12
12
|
|
13
|
-
#
|
14
13
|
# create journal from hash
|
15
14
|
#
|
16
|
-
def self.create
|
15
|
+
def self.create(d)
|
17
16
|
date = Date.parse(d['date'])
|
18
17
|
|
19
|
-
debit_amount = serialize_on_key(d['debit'], 'value')
|
20
|
-
credit_amount = serialize_on_key(d['credit'], 'value')
|
18
|
+
debit_amount = LucaSupport::Code.decimalize(serialize_on_key(d['debit'], 'value'))
|
19
|
+
credit_amount = LucaSupport::Code.decimalize(serialize_on_key(d['credit'], 'value'))
|
21
20
|
raise 'BalanceUnmatch' if debit_amount.inject(:+) != credit_amount.inject(:+)
|
22
21
|
|
23
22
|
debit_code = serialize_on_key(d['debit'], 'code')
|
24
23
|
credit_code = serialize_on_key(d['credit'], 'code')
|
25
24
|
|
26
|
-
# TODO:
|
27
|
-
codes = (debit_code + credit_code).uniq
|
25
|
+
# TODO: need to sync filename & content. Limit code length for filename
|
26
|
+
# codes = (debit_code + credit_code).uniq
|
27
|
+
codes = nil
|
28
28
|
create_record!(date, codes) do |f|
|
29
29
|
f << debit_code
|
30
|
-
f << debit_amount
|
30
|
+
f << LucaSupport::Code.readable(debit_amount)
|
31
31
|
f << credit_code
|
32
|
-
f << credit_amount
|
32
|
+
f << LucaSupport::Code.readable(credit_amount)
|
33
|
+
['x-customer', 'x-editor'].each do |x_header|
|
34
|
+
f << [x_header, d[x_header]] if d.dig(x_header)
|
35
|
+
end
|
33
36
|
f << []
|
34
37
|
f << [d.dig('note')]
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
41
|
+
def self.update_codes(obj)
|
42
|
+
debit_code = serialize_on_key(obj[:debit], :code)
|
43
|
+
credit_code = serialize_on_key(obj[:credit], :code)
|
44
|
+
codes = (debit_code + credit_code).uniq.sort.compact
|
45
|
+
change_codes(obj[:id], codes)
|
46
|
+
end
|
47
|
+
|
38
48
|
# define new transaction ID & write data at once
|
39
49
|
def self.create_record!(date_obj, codes = nil)
|
40
|
-
|
50
|
+
create_record(nil, date_obj, codes) do |f|
|
41
51
|
f.write CSV.generate('', col_sep: "\t", headers: false) { |c| yield(c) }
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
45
|
-
#
|
46
55
|
# collect values on specified key
|
47
56
|
#
|
48
57
|
def self.serialize_on_key(array_of_hash, key)
|
49
58
|
array_of_hash.map { |h| h[key] }
|
50
59
|
end
|
51
60
|
|
52
|
-
#
|
53
61
|
# override de-serializing journal format
|
54
62
|
#
|
55
63
|
def self.load_data(io, path)
|
56
64
|
{}.tap do |record|
|
57
65
|
body = false
|
58
|
-
record[:id] = path[0]
|
66
|
+
record[:id] = "#{path[0]}/#{path[1]}"
|
59
67
|
CSV.new(io, headers: false, col_sep: "\t", encoding: 'UTF-8')
|
60
68
|
.each.with_index(0) do |line, i|
|
61
69
|
case i
|
62
70
|
when 0
|
63
71
|
record[:debit] = line.map { |row| { code: row } }
|
64
72
|
when 1
|
65
|
-
line.each_with_index
|
66
|
-
record[:debit][i][:amount] = amount.to_i # TODO: bigdecimal support
|
67
|
-
end
|
73
|
+
line.each_with_index { |amount, j| record[:debit][j][:amount] = BigDecimal(amount.to_s) }
|
68
74
|
when 2
|
69
75
|
record[:credit] = line.map { |row| { code: row } }
|
70
76
|
when 3
|
71
|
-
line.each_with_index
|
72
|
-
record[:credit][i][:amount] = amount.to_i # TODO: bigdecimal support
|
73
|
-
end
|
77
|
+
line.each_with_index { |amount, j| record[:credit][j][:amount] = BigDecimal(amount.to_s) }
|
74
78
|
else
|
75
|
-
if line.empty?
|
79
|
+
if body == false && line.empty?
|
76
80
|
record[:note] ||= []
|
77
81
|
body = true
|
78
|
-
|
82
|
+
else
|
83
|
+
record[:note] << line.join(' ') if body
|
79
84
|
end
|
80
|
-
record[:note] << line.join(' ') if body
|
81
85
|
end
|
82
|
-
record[:note]&.join('\n')
|
83
86
|
end
|
87
|
+
record[:note] = record[:note]&.join('\n')
|
84
88
|
end
|
85
89
|
end
|
86
90
|
end
|
@@ -0,0 +1,138 @@
|
|
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
|
+
# Journal List on specified term
|
11
|
+
#
|
12
|
+
module LucaBook
|
13
|
+
class List < LucaBook::Journal
|
14
|
+
@dirname = 'journals'
|
15
|
+
|
16
|
+
def initialize(data, start_date, code = nil)
|
17
|
+
@data = data
|
18
|
+
@code = code
|
19
|
+
@start = start_date
|
20
|
+
@dict = LucaRecord::Dict.load('base.tsv')
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.term(from_year, from_month, to_year = from_year, to_month = from_month, code: nil, basedir: @dirname)
|
24
|
+
data = LucaBook::Journal.term(from_year, from_month, to_year, to_month, code).select do |dat|
|
25
|
+
if code.nil?
|
26
|
+
true
|
27
|
+
else
|
28
|
+
[:debit, :credit].map { |key| serialize_on_key(dat[key], :code) }.flatten.include?(code)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
new data, Date.new(from_year.to_i, from_month.to_i, 1), code
|
32
|
+
end
|
33
|
+
|
34
|
+
def list_on_code
|
35
|
+
calc_code
|
36
|
+
convert_label
|
37
|
+
@data = [code_header] + @data.map do |dat|
|
38
|
+
date, txid = LucaSupport::Code.decode_id(dat[:id])
|
39
|
+
{}.tap do |res|
|
40
|
+
res['code'] = dat[:code]
|
41
|
+
res['date'] = date
|
42
|
+
res['no'] = txid
|
43
|
+
res['id'] = dat[:id]
|
44
|
+
res['diff'] = dat[:diff]
|
45
|
+
res['balance'] = dat[:balance]
|
46
|
+
res['counter_code'] = dat[:counter_code].length == 1 ? dat[:counter_code].first : dat[:counter_code]
|
47
|
+
res['note'] = dat[:note]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def list_journals
|
54
|
+
convert_label
|
55
|
+
@data = @data.map do |dat|
|
56
|
+
date, txid = LucaSupport::Code.decode_id(dat[:id])
|
57
|
+
{}.tap do |res|
|
58
|
+
res['date'] = date
|
59
|
+
res['no'] = txid
|
60
|
+
res['id'] = dat[:id]
|
61
|
+
res['debit_code'] = dat[:debit].length == 1 ? dat[:debit][0][:code] : dat[:debit].map { |d| d[:code] }
|
62
|
+
res['debit_amount'] = dat[:debit].inject(0) { |sum, d| sum + d[:amount] }
|
63
|
+
res['credit_code'] = dat[:credit].length == 1 ? dat[:credit][0][:code] : dat[:credit].map { |d| d[:code] }
|
64
|
+
res['credit_amount'] = dat[:credit].inject(0) { |sum, d| sum + d[:amount] }
|
65
|
+
res['note'] = dat[:note]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def accumulate_code
|
72
|
+
@data.inject(BigDecimal('0')) do |sum, dat|
|
73
|
+
sum + Util.diff_by_code(dat[:debit], @code) - Util.diff_by_code(dat[:credit], @code)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_yaml
|
78
|
+
YAML.dump(LucaSupport::Code.readable(@data)).tap { |data| puts data }
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def set_balance
|
84
|
+
return BigDecimal('0') if @code.nil? || /^[A-H]/.match(@code)
|
85
|
+
|
86
|
+
balance_dict = Dict.latest_balance
|
87
|
+
start_balance = BigDecimal(balance_dict.dig(@code.to_s, :balance) || '0')
|
88
|
+
start = Dict.issue_date(balance_dict)&.next_month
|
89
|
+
last = @start.prev_month
|
90
|
+
if last.year >= start.year && last.month >= start.month
|
91
|
+
start_balance + self.class.term(start.year, start.month, last.year, last.month, code: @code).accumulate_code
|
92
|
+
else
|
93
|
+
start_balance
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def calc_code
|
98
|
+
@balance = set_balance
|
99
|
+
if @code
|
100
|
+
balance = @balance
|
101
|
+
@data.each do |dat|
|
102
|
+
dat[:diff] = Util.diff_by_code(dat[:debit], @code) - Util.diff_by_code(dat[:credit], @code)
|
103
|
+
balance += dat[:diff]
|
104
|
+
dat[:balance] = balance
|
105
|
+
dat[:code] = @code
|
106
|
+
counter = dat[:diff] * Util.pn_debit(@code) > 0 ? :credit : :debit
|
107
|
+
dat[:counter_code] = dat[counter].map { |d| d[:code] }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def convert_label
|
114
|
+
@data.each do |dat|
|
115
|
+
if @code
|
116
|
+
dat[:code] = "#{dat[:code]} #{@dict.dig(dat[:code], :label)}"
|
117
|
+
dat[:counter_code] = dat[:counter_code].map { |counter| "#{counter} #{@dict.dig(counter, :label)}" }
|
118
|
+
else
|
119
|
+
dat[:debit].each { |debit| debit[:code] = "#{debit[:code]} #{@dict.dig(debit[:code], :label)}" }
|
120
|
+
dat[:credit].each { |credit| credit[:code] = "#{credit[:code]} #{@dict.dig(credit[:code], :label)}" }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def dict
|
127
|
+
LucaBook::Dict::Data
|
128
|
+
end
|
129
|
+
|
130
|
+
def code_header
|
131
|
+
{}.tap do |h|
|
132
|
+
%w[code date no id diff balance counter_code note].each do |k|
|
133
|
+
h[k] = k == 'balance' ? @balance : ''
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/luca_book/setup.rb
CHANGED
@@ -6,13 +6,33 @@ require 'fileutils'
|
|
6
6
|
module LucaBook
|
7
7
|
class Setup
|
8
8
|
# create project skeleton under specified directory
|
9
|
-
def self.create_project(dir = LucaSupport::Config::Pjdir)
|
9
|
+
def self.create_project(country = nil, dir = LucaSupport::Config::Pjdir)
|
10
10
|
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
11
11
|
Dir.chdir(dir) do
|
12
|
-
%w[data/journals dict].each do |subdir|
|
12
|
+
%w[data/journals data/balance dict].each do |subdir|
|
13
13
|
FileUtils.mkdir_p(subdir) unless Dir.exist?(subdir)
|
14
14
|
end
|
15
|
-
|
15
|
+
dict = if File.exist?("#{__dir__}/templates/dict-#{country}.tsv")
|
16
|
+
"dict-#{country}.tsv"
|
17
|
+
else
|
18
|
+
'dict-en.tsv'
|
19
|
+
end
|
20
|
+
FileUtils.cp("#{__dir__}/templates/#{dict}", 'dict/base.tsv') unless File.exist?('dict/base.tsv')
|
21
|
+
prepare_starttsv(dict) unless File.exist? 'data/balance/start.tsv'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generate initial balance template.
|
26
|
+
# Codes are same as base dictionary.
|
27
|
+
# The previous month of start date is better for _date.
|
28
|
+
#
|
29
|
+
def self.prepare_starttsv(dict)
|
30
|
+
CSV.open('data/balance/start.tsv', 'w', col_sep: "\t", encoding: 'UTF-8') do |csv|
|
31
|
+
csv << ['code', 'label', 'balance']
|
32
|
+
csv << ['_date', '2020-1-1']
|
33
|
+
CSV.open("#{__dir__}/templates/#{dict}", 'r', col_sep: "\t", encoding: 'UTF-8').each do |row|
|
34
|
+
csv << row if /^[1-9]/.match(row[0])
|
35
|
+
end
|
16
36
|
end
|
17
37
|
end
|
18
38
|
end
|
data/lib/luca_book/state.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
require 'csv'
|
3
4
|
require 'pathname'
|
@@ -7,7 +8,6 @@ require 'luca_record'
|
|
7
8
|
require 'luca_record/dict'
|
8
9
|
require 'luca_book'
|
9
10
|
|
10
|
-
#
|
11
11
|
# Statement on specified term
|
12
12
|
#
|
13
13
|
module LucaBook
|
@@ -17,9 +17,11 @@ module LucaBook
|
|
17
17
|
|
18
18
|
attr_reader :statement
|
19
19
|
|
20
|
-
def initialize(data)
|
20
|
+
def initialize(data, count = nil)
|
21
21
|
@data = data
|
22
|
+
@count = count
|
22
23
|
@dict = LucaRecord::Dict.load('base.tsv')
|
24
|
+
@start_balance = set_balance
|
23
25
|
end
|
24
26
|
|
25
27
|
# TODO: not compatible with LucaRecord::Base.open_records
|
@@ -41,66 +43,42 @@ module LucaBook
|
|
41
43
|
last_date = Date.new(to_year.to_i, to_month.to_i, -1)
|
42
44
|
raise 'invalid term specified' if date > last_date
|
43
45
|
|
46
|
+
counts = []
|
44
47
|
reports = [].tap do |r|
|
45
48
|
while date <= last_date do
|
46
|
-
|
47
|
-
|
49
|
+
diff, count = accumulate_month(date.year, date.month)
|
50
|
+
r << diff.tap { |c| c['_d'] = date.to_s }
|
51
|
+
counts << count.tap { |c| c['_d'] = date.to_s }
|
52
|
+
date = Date.new(date.next_month.year, date.next_month.month, -1)
|
48
53
|
end
|
49
54
|
end
|
50
|
-
new reports
|
55
|
+
new reports, counts
|
51
56
|
end
|
52
57
|
|
53
58
|
def by_code(code, year=nil, month=nil)
|
54
|
-
raise
|
59
|
+
raise 'not supported year range yet' if ! year.nil? && month.nil?
|
55
60
|
|
56
|
-
|
57
|
-
full_term = scan_terms
|
61
|
+
balance = @book.load_start.dig(code) || 0
|
62
|
+
full_term = self.class.scan_terms
|
58
63
|
if ! month.nil?
|
59
|
-
pre_term = full_term.select{|y,m| y <= year.to_i && m < month.to_i }
|
60
|
-
|
61
|
-
[{ code: code, balance:
|
64
|
+
pre_term = full_term.select { |y, m| y <= year.to_i && m < month.to_i }
|
65
|
+
balance += pre_term.map { |y, m| self.class.net(y, m)}.inject(0){|sum, h| sum + h[code] }
|
66
|
+
[{ code: code, balance: balance, note: "#{code} #{@dict.dig(code, :label)}" }] + records_with_balance(year, month, code, balance)
|
62
67
|
else
|
63
|
-
start = { code: code, balance:
|
64
|
-
full_term.map {|y, m| y }.uniq.map {|y|
|
65
|
-
records_with_balance(y, nil, code,
|
68
|
+
start = { code: code, balance: balance, note: "#{code} #{@dict.dig(code, :label)}" }
|
69
|
+
full_term.map { |y, m| y }.uniq.map { |y|
|
70
|
+
records_with_balance(y, nil, code, balance)
|
66
71
|
}.flatten.prepend(start)
|
67
72
|
end
|
68
73
|
end
|
69
74
|
|
70
75
|
def records_with_balance(year, month, code, balance)
|
71
76
|
@book.search(year, month, nil, code).each do |h|
|
72
|
-
balance +=
|
77
|
+
balance += Util.calc_diff(Util.amount_by_code(h[:debit], code), code) - Util.calc_diff(Util.amount_by_code(h[:credit], code), code)
|
73
78
|
h[:balance] = balance
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
77
|
-
#
|
78
|
-
# TODO: useless method. consider to remove
|
79
|
-
#
|
80
|
-
def accumulate_all
|
81
|
-
current = @book.load_start
|
82
|
-
target = []
|
83
|
-
Dir.chdir(@book.pjdir) do
|
84
|
-
net_records = scan_terms(@book.pjdir).map do |year, month|
|
85
|
-
target << [year, month]
|
86
|
-
accumulate_month(year, month)
|
87
|
-
end
|
88
|
-
all_keys = net_records.map{|h| h.keys}.flatten.uniq
|
89
|
-
net_records.each.with_index(0) do |diff, i|
|
90
|
-
all_keys.each {|key| diff[key] = 0 unless diff.has_key?(key)}
|
91
|
-
diff.each do |k,v|
|
92
|
-
if current[k]
|
93
|
-
current[k] += v
|
94
|
-
else
|
95
|
-
current[k] = v
|
96
|
-
end
|
97
|
-
end
|
98
|
-
f = { target: "#{target[i][0]}-#{target[i][1]}", diff: diff.sort, current: current.sort }
|
99
|
-
yield f
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
82
|
def to_yaml
|
105
83
|
YAML.dump(code2label).tap { |data| puts data }
|
106
84
|
end
|
@@ -109,38 +87,118 @@ module LucaBook
|
|
109
87
|
@statement ||= @data
|
110
88
|
@statement.map do |report|
|
111
89
|
{}.tap do |h|
|
112
|
-
report.each { |k, v| h[@dict.dig(k, :label)] = v }
|
90
|
+
report.each { |k, v| h[@dict.dig(k, :label) || k] = v }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def stats(level = nil)
|
96
|
+
keys = @count.map(&:keys).flatten.push('_t').uniq.sort
|
97
|
+
@count.map! do |data|
|
98
|
+
sum = 0
|
99
|
+
keys.each do |k|
|
100
|
+
data[k] ||= 0
|
101
|
+
sum += data[k] if /^[^_]/.match(k)
|
102
|
+
next if level.nil? || k.length <= level
|
103
|
+
|
104
|
+
if data[k[0, level]]
|
105
|
+
data[k[0, level]] += data[k]
|
106
|
+
else
|
107
|
+
data[k[0, level]] = data[k]
|
108
|
+
end
|
113
109
|
end
|
110
|
+
data.select! { |k, _v| k.length <= level } if level
|
111
|
+
data['_t'] = sum
|
112
|
+
data.sort.to_h
|
114
113
|
end
|
114
|
+
keys.map! { |k| k[0, level] }.uniq.select! { |k| k.length <= level } if level
|
115
|
+
@count.prepend({}.tap { |header| keys.each { |k| header[k] = @dict.dig(k, :label) }})
|
116
|
+
puts YAML.dump(@count)
|
117
|
+
@count
|
115
118
|
end
|
116
119
|
|
117
|
-
def bs
|
118
|
-
@
|
119
|
-
|
120
|
+
def bs(level = 3, legal: false)
|
121
|
+
@data.map! { |data| data.select { |k, _v| k.length <= level } }
|
122
|
+
@data.map! { |data| code_sum(data).merge(data) } if legal
|
123
|
+
base = accumulate_balance(@data)
|
124
|
+
length = [base[:debit].length, base[:credit].length].max
|
125
|
+
@statement = [].tap do |a|
|
126
|
+
length.times do |i|
|
127
|
+
{}.tap do |res|
|
128
|
+
res['debit_label'] = base[:debit][i] ? @dict.dig(base[:debit][i].keys[0], :label) : ''
|
129
|
+
res['debit_balance'] = base[:debit][i] ? @start_balance.dig(base[:debit][i].keys[0]) + base[:debit][i].values[0] : ''
|
130
|
+
res['debit_diff'] = base[:debit][i] ? base[:debit][i].values[0] : ''
|
131
|
+
res['credit_label'] = base[:credit][i] ? @dict.dig(base[:credit][i].keys[0], :label) : ''
|
132
|
+
res['credit_balance'] = base[:credit][i] ? @start_balance.dig(base[:credit][i].keys[0]) + base[:credit][i].values[0] : ''
|
133
|
+
res['credit_diff'] = base[:credit][i] ? base[:credit][i].values[0] : ''
|
134
|
+
a << res
|
135
|
+
end
|
136
|
+
end
|
120
137
|
end
|
138
|
+
puts YAML.dump(@statement)
|
121
139
|
self
|
122
140
|
end
|
123
141
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
142
|
+
def accumulate_balance(monthly_diffs)
|
143
|
+
data = monthly_diffs.each_with_object({}) do |month, h|
|
144
|
+
month.each do |k, v|
|
145
|
+
h[k] = h[k].nil? ? v : h[k] + v
|
146
|
+
end
|
147
|
+
end.sort.to_h
|
148
|
+
{ debit: [], credit: [] }.tap do |res|
|
149
|
+
data.each do |k, v|
|
150
|
+
case k
|
151
|
+
when /^[0-4].*/
|
152
|
+
res[:debit] << { k => v }
|
153
|
+
when /^[5-9].*/
|
154
|
+
res[:credit] << { k => v }
|
155
|
+
end
|
156
|
+
end
|
127
157
|
end
|
128
|
-
self
|
129
158
|
end
|
130
159
|
|
131
|
-
def
|
132
|
-
|
133
|
-
|
160
|
+
def pl
|
161
|
+
@statement = @data.map { |data| data.select { |k, _v| /^[A-H_].+/.match(k) } }
|
162
|
+
@statement << @statement.each_with_object({}) { |item, h| item.each { |k, v| h[k].nil? ? h[k] = v : h[k] += v } }
|
163
|
+
.tap { |h| h['_d'] = 'Total' }
|
164
|
+
self
|
134
165
|
end
|
135
166
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
.inject(0){|sum, item| sum + item[:amount] }
|
167
|
+
def self.accumulate_month(year, month)
|
168
|
+
monthly_record, count = net(year, month)
|
169
|
+
[total_subaccount(monthly_record), count]
|
140
170
|
end
|
141
171
|
|
172
|
+
# Accumulate Level 2, 3 account.
|
173
|
+
#
|
142
174
|
def self.total_subaccount(report)
|
143
|
-
|
175
|
+
{}.tap do |res|
|
176
|
+
res['A0'] = sum_matched(report, /^[A][0-9A-Z]{2,}/)
|
177
|
+
res['B0'] = sum_matched(report, /^[B][0-9A-Z]{2,}/)
|
178
|
+
res['BA'] = res['A0'] - res['B0']
|
179
|
+
res['C0'] = sum_matched(report, /^[C][0-9A-Z]{2,}/)
|
180
|
+
res['CA'] = res['BA'] - res['C0']
|
181
|
+
res['D0'] = sum_matched(report, /^[D][0-9A-Z]{2,}/)
|
182
|
+
res['E0'] = sum_matched(report, /^[E][0-9A-Z]{2,}/)
|
183
|
+
res['EA'] = res['CA'] + res['D0'] - res['E0']
|
184
|
+
res['F0'] = sum_matched(report, /^[F][0-9A-Z]{2,}/)
|
185
|
+
res['G0'] = sum_matched(report, /^[G][0-9][0-9A-Z]{1,}/)
|
186
|
+
res['GA'] = res['EA'] + res['F0'] - res['G0']
|
187
|
+
res['H0'] = sum_matched(report, /^[H][0-9][0-9A-Z]{1,}/)
|
188
|
+
res['HA'] = res['GA'] - res['H0']
|
189
|
+
|
190
|
+
res['9142'] = (report['9142'] || 0) + res['HA']
|
191
|
+
res['10'] = sum_matched(report, /^[123][0-9A-Z]{2,}/)
|
192
|
+
res['40'] = sum_matched(report, /^[4][0-9A-Z]{2,}/)
|
193
|
+
res['50'] = sum_matched(report, /^[56][0-9A-Z]{2,}/)
|
194
|
+
res['70'] = sum_matched(report, /^[78][0-9A-Z]{2,}/)
|
195
|
+
res['8ZZ'] = res['50'] + res['70']
|
196
|
+
res['9ZZ'] = sum_matched(report, /^[9][0-9A-Z]{2,}/)
|
197
|
+
|
198
|
+
res['1'] = res['10'] + res['40']
|
199
|
+
res['5'] = res['8ZZ'] + res['9ZZ'] + res['HA']
|
200
|
+
res['_d'] = report['_d']
|
201
|
+
|
144
202
|
report.each do |k, v|
|
145
203
|
if k.length >= 4
|
146
204
|
if res[k[0, 3]]
|
@@ -150,26 +208,23 @@ module LucaBook
|
|
150
208
|
end
|
151
209
|
end
|
152
210
|
end
|
153
|
-
res
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
res['BA'] = res['A0'] - res['B0']
|
161
|
-
res['C0'] = sum_matched(report, /^[C].[^0]/)
|
162
|
-
res['CA'] = res['BA'] - res['C0']
|
163
|
-
res['D0'] = sum_matched(report, /^[D].[^0]/)
|
164
|
-
res['E0'] = sum_matched(report, /^[E].[^0]/)
|
165
|
-
res['EA'] = res['CA'] + res['D0'] - res['E0']
|
166
|
-
res['F0'] = sum_matched(report, /^[F].[^0]/)
|
167
|
-
res['G0'] = sum_matched(report, /^[G].[^0]/)
|
168
|
-
res['GA'] = res['EA'] + res['F0'] - res['G0']
|
169
|
-
res['HA'] = res['GA'] - sum_matched(report, /^[H].[^0]/)
|
211
|
+
res.sort.to_h
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def code_sum(report)
|
216
|
+
legal_items.each.with_object({}) do |k, h|
|
217
|
+
h[k] = self.class.sum_matched(report, /^#{k}.*/)
|
170
218
|
end
|
171
219
|
end
|
172
220
|
|
221
|
+
def set_balance
|
222
|
+
base = Dict.latest_balance.each_with_object({}) do |(k, v), h|
|
223
|
+
h[k] = v[:balance].to_i if v[:balance]
|
224
|
+
end
|
225
|
+
code_sum(base).merge(self.class.total_subaccount(base))
|
226
|
+
end
|
227
|
+
|
173
228
|
def self.sum_matched(report, reg)
|
174
229
|
report.select { |k, v| reg.match(k)}.values.sum
|
175
230
|
end
|
@@ -181,7 +236,7 @@ module LucaBook
|
|
181
236
|
# TODO: date based range search
|
182
237
|
end
|
183
238
|
|
184
|
-
sum = { debit: {}, credit: {} }
|
239
|
+
sum = { debit: {}, credit: {}, debit_count: {}, credit_count: {} }
|
185
240
|
idx_memo = []
|
186
241
|
asof(year, month) do |f, _path|
|
187
242
|
CSV.new(f, headers: false, col_sep: "\t", encoding: 'UTF-8')
|
@@ -190,14 +245,26 @@ module LucaBook
|
|
190
245
|
case i
|
191
246
|
when 0
|
192
247
|
idx_memo = row.map(&:to_s)
|
193
|
-
idx_memo.each
|
248
|
+
idx_memo.each do |r|
|
249
|
+
sum[:debit][r] ||= 0
|
250
|
+
sum[:debit_count][r] ||= 0
|
251
|
+
end
|
194
252
|
when 1
|
195
|
-
row.each_with_index
|
253
|
+
row.each_with_index do |r, j|
|
254
|
+
sum[:debit][idx_memo[j]] += r.to_i # TODO: bigdecimal support
|
255
|
+
sum[:debit_count][idx_memo[j]] += 1
|
256
|
+
end
|
196
257
|
when 2
|
197
258
|
idx_memo = row.map(&:to_s)
|
198
|
-
idx_memo.each
|
259
|
+
idx_memo.each do |r|
|
260
|
+
sum[:credit][r] ||= 0
|
261
|
+
sum[:credit_count][r] ||= 0
|
262
|
+
end
|
199
263
|
when 3
|
200
|
-
row.each_with_index
|
264
|
+
row.each_with_index do |r, j|
|
265
|
+
sum[:credit][idx_memo[j]] += r.to_i # TODO: bigdecimal support
|
266
|
+
sum[:credit_count][idx_memo[j]] += 1
|
267
|
+
end
|
201
268
|
else
|
202
269
|
puts row # for debug
|
203
270
|
end
|
@@ -210,17 +277,20 @@ module LucaBook
|
|
210
277
|
def self.net(year, month = nil, code = nil, date_range = nil)
|
211
278
|
g = gross(year, month, code, date_range)
|
212
279
|
idx = (g[:debit].keys + g[:credit].keys).uniq.sort
|
213
|
-
{}
|
280
|
+
count = {}
|
281
|
+
diff = {}.tap do |sum|
|
214
282
|
idx.each do |code|
|
215
|
-
sum[code] = g.dig(:debit, code).nil? ? 0 : calc_diff(g[:debit][code], code)
|
216
|
-
sum[code] -= g.dig(:credit, code).nil? ? 0 : calc_diff(g[:credit][code], code)
|
283
|
+
sum[code] = g.dig(:debit, code).nil? ? 0 : Util.calc_diff(g[:debit][code], code)
|
284
|
+
sum[code] -= g.dig(:credit, code).nil? ? 0 : Util.calc_diff(g[:credit][code], code)
|
285
|
+
count[code] = (g.dig(:debit_count, code) || 0) + (g.dig(:credit_count, code) || 0)
|
217
286
|
end
|
218
287
|
end
|
288
|
+
[diff, count]
|
219
289
|
end
|
220
290
|
|
221
291
|
# TODO: replace load_tsv -> generic load_tsv_dict
|
222
292
|
def load_start
|
223
|
-
file = LucaSupport::Config::Pjdir
|
293
|
+
file = Pathname(LucaSupport::Config::Pjdir) / 'data' / 'balance' / 'start.tsv'
|
224
294
|
{}.tap do |dic|
|
225
295
|
load_tsv(file) do |row|
|
226
296
|
dic[row[0]] = row[2].to_i if ! row[2].nil?
|
@@ -235,24 +305,15 @@ module LucaBook
|
|
235
305
|
data.each { |row| yield row }
|
236
306
|
end
|
237
307
|
|
238
|
-
|
239
|
-
amount = /\./.match(num.to_s) ? BigDecimal(num) : num.to_i
|
240
|
-
amount * pn_debit(code.to_s)
|
241
|
-
end
|
308
|
+
private
|
242
309
|
|
243
|
-
def
|
244
|
-
|
245
|
-
when /^[0-4BCEGH]/
|
246
|
-
1
|
247
|
-
when /^[5-9ADF]/
|
248
|
-
-1
|
249
|
-
else
|
250
|
-
nil
|
251
|
-
end
|
252
|
-
end
|
310
|
+
def legal_items
|
311
|
+
return [] unless LucaSupport::Config::COUNTRY
|
253
312
|
|
254
|
-
|
255
|
-
|
313
|
+
case LucaSupport::Config::COUNTRY
|
314
|
+
when 'jp'
|
315
|
+
['91', '911', '912', '913', '9131', '9132', '914', '9141', '9142', '915', '916', '92', '93']
|
316
|
+
end
|
256
317
|
end
|
257
318
|
end
|
258
319
|
end
|