lucabook 0.2.11 → 0.2.19
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 +25 -18
- data/lib/luca_book/list.rb +138 -0
- data/lib/luca_book/setup.rb +23 -3
- data/lib/luca_book/state.rb +206 -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
@@ -12,28 +12,39 @@ module LucaBook
|
|
12
12
|
|
13
13
|
# create journal from hash
|
14
14
|
#
|
15
|
-
def self.create
|
15
|
+
def self.create(d)
|
16
16
|
date = Date.parse(d['date'])
|
17
17
|
|
18
|
-
debit_amount = serialize_on_key(d['debit'], 'value')
|
19
|
-
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'))
|
20
20
|
raise 'BalanceUnmatch' if debit_amount.inject(:+) != credit_amount.inject(:+)
|
21
21
|
|
22
22
|
debit_code = serialize_on_key(d['debit'], 'code')
|
23
23
|
credit_code = serialize_on_key(d['credit'], 'code')
|
24
24
|
|
25
|
-
# TODO:
|
26
|
-
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
|
27
28
|
create_record!(date, codes) do |f|
|
28
29
|
f << debit_code
|
29
|
-
f << debit_amount
|
30
|
+
f << LucaSupport::Code.readable(debit_amount)
|
30
31
|
f << credit_code
|
31
|
-
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
|
32
36
|
f << []
|
33
37
|
f << [d.dig('note')]
|
34
38
|
end
|
35
39
|
end
|
36
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
|
+
|
37
48
|
# define new transaction ID & write data at once
|
38
49
|
def self.create_record!(date_obj, codes = nil)
|
39
50
|
create_record(nil, date_obj, codes) do |f|
|
@@ -52,32 +63,28 @@ module LucaBook
|
|
52
63
|
def self.load_data(io, path)
|
53
64
|
{}.tap do |record|
|
54
65
|
body = false
|
55
|
-
record[:id] = path[0]
|
66
|
+
record[:id] = "#{path[0]}/#{path[1]}"
|
56
67
|
CSV.new(io, headers: false, col_sep: "\t", encoding: 'UTF-8')
|
57
68
|
.each.with_index(0) do |line, i|
|
58
69
|
case i
|
59
70
|
when 0
|
60
71
|
record[:debit] = line.map { |row| { code: row } }
|
61
72
|
when 1
|
62
|
-
line.each_with_index
|
63
|
-
record[:debit][i][:amount] = amount.to_i # TODO: bigdecimal support
|
64
|
-
end
|
73
|
+
line.each_with_index { |amount, j| record[:debit][j][:amount] = BigDecimal(amount.to_s) }
|
65
74
|
when 2
|
66
75
|
record[:credit] = line.map { |row| { code: row } }
|
67
76
|
when 3
|
68
|
-
line.each_with_index
|
69
|
-
record[:credit][i][:amount] = amount.to_i # TODO: bigdecimal support
|
70
|
-
end
|
77
|
+
line.each_with_index { |amount, j| record[:credit][j][:amount] = BigDecimal(amount.to_s) }
|
71
78
|
else
|
72
|
-
if line.empty?
|
79
|
+
if body == false && line.empty?
|
73
80
|
record[:note] ||= []
|
74
81
|
body = true
|
75
|
-
|
82
|
+
else
|
83
|
+
record[:note] << line.join(' ') if body
|
76
84
|
end
|
77
|
-
record[:note] << line.join(' ') if body
|
78
85
|
end
|
79
|
-
record[:note]&.join('\n')
|
80
86
|
end
|
87
|
+
record[:note] = record[:note]&.join('\n')
|
81
88
|
end
|
82
89
|
end
|
83
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,12 @@ module LucaBook
|
|
17
17
|
|
18
18
|
attr_reader :statement
|
19
19
|
|
20
|
-
def initialize(data)
|
20
|
+
def initialize(data, count = nil, date: nil)
|
21
21
|
@data = data
|
22
|
+
@count = count
|
22
23
|
@dict = LucaRecord::Dict.load('base.tsv')
|
24
|
+
@start_date = date
|
25
|
+
@start_balance = set_balance
|
23
26
|
end
|
24
27
|
|
25
28
|
# TODO: not compatible with LucaRecord::Base.open_records
|
@@ -41,66 +44,42 @@ module LucaBook
|
|
41
44
|
last_date = Date.new(to_year.to_i, to_month.to_i, -1)
|
42
45
|
raise 'invalid term specified' if date > last_date
|
43
46
|
|
47
|
+
counts = []
|
44
48
|
reports = [].tap do |r|
|
45
49
|
while date <= last_date do
|
46
|
-
|
47
|
-
|
50
|
+
diff, count = accumulate_month(date.year, date.month)
|
51
|
+
r << diff.tap { |c| c['_d'] = date.to_s }
|
52
|
+
counts << count.tap { |c| c['_d'] = date.to_s }
|
53
|
+
date = Date.new(date.next_month.year, date.next_month.month, -1)
|
48
54
|
end
|
49
55
|
end
|
50
|
-
new
|
56
|
+
new(reports, counts, date: Date.new(from_year.to_i, from_month.to_i, -1))
|
51
57
|
end
|
52
58
|
|
53
59
|
def by_code(code, year=nil, month=nil)
|
54
|
-
raise
|
60
|
+
raise 'not supported year range yet' if ! year.nil? && month.nil?
|
55
61
|
|
56
|
-
|
57
|
-
full_term = scan_terms
|
62
|
+
balance = @book.load_start.dig(code) || 0
|
63
|
+
full_term = self.class.scan_terms
|
58
64
|
if ! month.nil?
|
59
|
-
pre_term = full_term.select{|y,m| y <= year.to_i && m < month.to_i }
|
60
|
-
|
61
|
-
[{ code: code, balance:
|
65
|
+
pre_term = full_term.select { |y, m| y <= year.to_i && m < month.to_i }
|
66
|
+
balance += pre_term.map { |y, m| self.class.net(y, m)}.inject(0){|sum, h| sum + h[code] }
|
67
|
+
[{ code: code, balance: balance, note: "#{code} #{@dict.dig(code, :label)}" }] + records_with_balance(year, month, code, balance)
|
62
68
|
else
|
63
|
-
start = { code: code, balance:
|
64
|
-
full_term.map {|y, m| y }.uniq.map {|y|
|
65
|
-
records_with_balance(y, nil, code,
|
69
|
+
start = { code: code, balance: balance, note: "#{code} #{@dict.dig(code, :label)}" }
|
70
|
+
full_term.map { |y, m| y }.uniq.map { |y|
|
71
|
+
records_with_balance(y, nil, code, balance)
|
66
72
|
}.flatten.prepend(start)
|
67
73
|
end
|
68
74
|
end
|
69
75
|
|
70
76
|
def records_with_balance(year, month, code, balance)
|
71
77
|
@book.search(year, month, nil, code).each do |h|
|
72
|
-
balance +=
|
78
|
+
balance += Util.calc_diff(Util.amount_by_code(h[:debit], code), code) - Util.calc_diff(Util.amount_by_code(h[:credit], code), code)
|
73
79
|
h[:balance] = balance
|
74
80
|
end
|
75
81
|
end
|
76
82
|
|
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
83
|
def to_yaml
|
105
84
|
YAML.dump(code2label).tap { |data| puts data }
|
106
85
|
end
|
@@ -109,38 +88,150 @@ module LucaBook
|
|
109
88
|
@statement ||= @data
|
110
89
|
@statement.map do |report|
|
111
90
|
{}.tap do |h|
|
112
|
-
report.each { |k, v| h[@dict.dig(k, :label)] = v }
|
91
|
+
report.each { |k, v| h[@dict.dig(k, :label) || k] = v }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def stats(level = nil)
|
97
|
+
keys = @count.map(&:keys).flatten.push('_t').uniq.sort
|
98
|
+
@count.map! do |data|
|
99
|
+
sum = 0
|
100
|
+
keys.each do |k|
|
101
|
+
data[k] ||= 0
|
102
|
+
sum += data[k] if /^[^_]/.match(k)
|
103
|
+
next if level.nil? || k.length <= level
|
104
|
+
|
105
|
+
if data[k[0, level]]
|
106
|
+
data[k[0, level]] += data[k]
|
107
|
+
else
|
108
|
+
data[k[0, level]] = data[k]
|
109
|
+
end
|
113
110
|
end
|
111
|
+
data.select! { |k, _v| k.length <= level } if level
|
112
|
+
data['_t'] = sum
|
113
|
+
data.sort.to_h
|
114
114
|
end
|
115
|
+
keys.map! { |k| k[0, level] }.uniq.select! { |k| k.length <= level } if level
|
116
|
+
@count.prepend({}.tap { |header| keys.each { |k| header[k] = @dict.dig(k, :label) }})
|
117
|
+
puts YAML.dump(@count)
|
118
|
+
@count
|
115
119
|
end
|
116
120
|
|
117
|
-
def bs
|
118
|
-
@
|
119
|
-
|
121
|
+
def bs(level = 3, legal: false)
|
122
|
+
@start_balance.keys.each { |k| @data.first[k] ||= 0 }
|
123
|
+
@data.map! { |data| data.select { |k, _v| k.length <= level } }
|
124
|
+
@data.map! { |data| code_sum(data).merge(data) } if legal
|
125
|
+
base = accumulate_balance(@data)
|
126
|
+
rows = [base[:debit].length, base[:credit].length].max
|
127
|
+
@statement = [].tap do |a|
|
128
|
+
rows.times do |i|
|
129
|
+
{}.tap do |res|
|
130
|
+
res['debit_label'] = base[:debit][i] ? @dict.dig(base[:debit][i].keys[0], :label) : ''
|
131
|
+
res['debit_balance'] = base[:debit][i] ? (@start_balance.dig(base[:debit][i].keys[0]) || 0) + base[:debit][i].values[0] : ''
|
132
|
+
res['debit_diff'] = base[:debit][i] ? base[:debit][i].values[0] : ''
|
133
|
+
res['credit_label'] = base[:credit][i] ? @dict.dig(base[:credit][i].keys[0], :label) : ''
|
134
|
+
res['credit_balance'] = base[:credit][i] ? (@start_balance.dig(base[:credit][i].keys[0]) || 0) + base[:credit][i].values[0] : ''
|
135
|
+
res['credit_diff'] = base[:credit][i] ? base[:credit][i].values[0] : ''
|
136
|
+
a << res
|
137
|
+
end
|
138
|
+
end
|
120
139
|
end
|
140
|
+
puts YAML.dump(@statement)
|
121
141
|
self
|
122
142
|
end
|
123
143
|
|
144
|
+
def accumulate_balance(monthly_diffs)
|
145
|
+
data = monthly_diffs.each_with_object({}) do |month, h|
|
146
|
+
month.each do |k, v|
|
147
|
+
h[k] = h[k].nil? ? v : h[k] + v
|
148
|
+
end
|
149
|
+
end
|
150
|
+
{ debit: [], credit: [] }.tap do |res|
|
151
|
+
data.sort.to_h.each do |k, v|
|
152
|
+
case k
|
153
|
+
when /^[0-4].*/
|
154
|
+
res[:debit] << { k => v }
|
155
|
+
when /^[5-9].*/
|
156
|
+
res[:credit] << { k => v }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
124
162
|
def pl
|
125
|
-
@statement = @data.map
|
126
|
-
|
163
|
+
@statement = @data.map { |data| data.select { |k, _v| /^[A-H_].+/.match(k) } }
|
164
|
+
term = @statement.each_with_object({}) { |item, h| item.each { |k, v| h[k].nil? ? h[k] = v : h[k] += v } }
|
165
|
+
fy = @start_balance.select { |k, _v| /^[A-H].+/.match(k) }
|
166
|
+
fy = {}.tap do |h|
|
167
|
+
(term.keys + fy.keys).uniq.each do |k|
|
168
|
+
h[k] = (fy[k] || 0).to_i + (term[k] || 0).to_i
|
169
|
+
end
|
127
170
|
end
|
171
|
+
@statement << term.tap { |h| h['_d'] = 'Period Total' }
|
172
|
+
@statement << fy.tap { |h| h['_d'] = 'FY Total' }
|
128
173
|
self
|
129
174
|
end
|
130
175
|
|
131
|
-
def self.
|
132
|
-
|
133
|
-
|
176
|
+
def self.accumulate_term(start_year, start_month, end_year, end_month)
|
177
|
+
date = Date.new(start_year, start_month, 1)
|
178
|
+
last_date = Date.new(end_year, end_month, -1)
|
179
|
+
return nil if date > last_date
|
180
|
+
|
181
|
+
{}.tap do |res|
|
182
|
+
while date <= last_date do
|
183
|
+
diff, _count = net(date.year, date.month)
|
184
|
+
diff.each do |k, v|
|
185
|
+
next if /^[_]/.match(k)
|
186
|
+
|
187
|
+
res[k] = res[k].nil? ? v : res[k] + v
|
188
|
+
end
|
189
|
+
date = date.next_month
|
190
|
+
end
|
191
|
+
end
|
134
192
|
end
|
135
193
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
.inject(0){|sum, item| sum + item[:amount] }
|
194
|
+
def self.accumulate_month(year, month)
|
195
|
+
monthly_record, count = net(year, month)
|
196
|
+
[total_subaccount(monthly_record), count]
|
140
197
|
end
|
141
198
|
|
199
|
+
# Accumulate Level 2, 3 account.
|
200
|
+
#
|
142
201
|
def self.total_subaccount(report)
|
143
|
-
|
202
|
+
{}.tap do |res|
|
203
|
+
res['A0'] = sum_matched(report, /^[A][0-9A-Z]{2,}/)
|
204
|
+
res['B0'] = sum_matched(report, /^[B][0-9A-Z]{2,}/)
|
205
|
+
res['BA'] = res['A0'] - res['B0']
|
206
|
+
res['C0'] = sum_matched(report, /^[C][0-9A-Z]{2,}/)
|
207
|
+
res['CA'] = res['BA'] - res['C0']
|
208
|
+
res['D0'] = sum_matched(report, /^[D][0-9A-Z]{2,}/)
|
209
|
+
res['E0'] = sum_matched(report, /^[E][0-9A-Z]{2,}/)
|
210
|
+
res['EA'] = res['CA'] + res['D0'] - res['E0']
|
211
|
+
res['F0'] = sum_matched(report, /^[F][0-9A-Z]{2,}/)
|
212
|
+
res['G0'] = sum_matched(report, /^[G][0-9][0-9A-Z]{1,}/)
|
213
|
+
res['GA'] = res['EA'] + res['F0'] - res['G0']
|
214
|
+
res['H0'] = sum_matched(report, /^[H][0-9][0-9A-Z]{1,}/)
|
215
|
+
res['HA'] = res['GA'] - res['H0']
|
216
|
+
|
217
|
+
report['9142'] = (report['9142'] || 0) + res['HA']
|
218
|
+
res['9142'] = report['9142']
|
219
|
+
res['10'] = sum_matched(report, /^[123][0-9A-Z]{2,}/)
|
220
|
+
res['40'] = sum_matched(report, /^[4][0-9A-Z]{2,}/)
|
221
|
+
res['50'] = sum_matched(report, /^[56][0-9A-Z]{2,}/)
|
222
|
+
res['70'] = sum_matched(report, /^[78][0-9A-Z]{2,}/)
|
223
|
+
res['91'] = sum_matched(report, /^91[0-9A-Z]{1,}/)
|
224
|
+
res['8ZZ'] = res['50'] + res['70']
|
225
|
+
res['9ZZ'] = sum_matched(report, /^[9][0-9A-Z]{2,}/)
|
226
|
+
|
227
|
+
res['1'] = res['10'] + res['40']
|
228
|
+
res['5'] = res['8ZZ'] + res['9ZZ']
|
229
|
+
res['_d'] = report['_d']
|
230
|
+
|
231
|
+
report.each do |k, v|
|
232
|
+
res[k] = v if k.length == 3
|
233
|
+
end
|
234
|
+
|
144
235
|
report.each do |k, v|
|
145
236
|
if k.length >= 4
|
146
237
|
if res[k[0, 3]]
|
@@ -150,26 +241,35 @@ module LucaBook
|
|
150
241
|
end
|
151
242
|
end
|
152
243
|
end
|
153
|
-
res
|
154
|
-
res['40'] = sum_matched(report, /^[4].[^0]}/)
|
155
|
-
res['50'] = sum_matched(report, /^[56].[^0]/)
|
156
|
-
res['70'] = sum_matched(report, /^[78].[^0]/)
|
157
|
-
res['90'] = sum_matched(report, /^[9].[^0]/)
|
158
|
-
res['A0'] = sum_matched(report, /^[A].[^0]/)
|
159
|
-
res['B0'] = sum_matched(report, /^[B].[^0]/)
|
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]/)
|
244
|
+
res.sort.to_h
|
170
245
|
end
|
171
246
|
end
|
172
247
|
|
248
|
+
def code_sum(report)
|
249
|
+
legal_items.each.with_object({}) do |k, h|
|
250
|
+
h[k] = self.class.sum_matched(report, /^#{k}.*/)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def set_balance
|
255
|
+
pre_last = @start_date.prev_month
|
256
|
+
pre = if @start_date.month > LucaSupport::CONFIG['fy_start'].to_i
|
257
|
+
self.class.accumulate_term(pre_last.year, LucaSupport::CONFIG['fy_start'], pre_last.year, pre_last.month)
|
258
|
+
elsif @start_date.month < LucaSupport::CONFIG['fy_start'].to_i
|
259
|
+
self.class.accumulate_term(pre_last.year - 1, LucaSupport::CONFIG['fy_start'], pre_last.year, pre_last.month)
|
260
|
+
end
|
261
|
+
|
262
|
+
base = Dict.latest_balance.each_with_object({}) do |(k, v), h|
|
263
|
+
h[k] = v[:balance].to_i if v[:balance]
|
264
|
+
end
|
265
|
+
if pre
|
266
|
+
idx = (pre.keys + base.keys).uniq
|
267
|
+
base = {}.tap { |h| idx.each { |k| h[k] = (base[k] || 0) + (pre[k] || 0) } }
|
268
|
+
end
|
269
|
+
#code_sum(base).merge(self.class.total_subaccount(base))
|
270
|
+
self.class.total_subaccount(base)
|
271
|
+
end
|
272
|
+
|
173
273
|
def self.sum_matched(report, reg)
|
174
274
|
report.select { |k, v| reg.match(k)}.values.sum
|
175
275
|
end
|
@@ -181,7 +281,7 @@ module LucaBook
|
|
181
281
|
# TODO: date based range search
|
182
282
|
end
|
183
283
|
|
184
|
-
sum = { debit: {}, credit: {} }
|
284
|
+
sum = { debit: {}, credit: {}, debit_count: {}, credit_count: {} }
|
185
285
|
idx_memo = []
|
186
286
|
asof(year, month) do |f, _path|
|
187
287
|
CSV.new(f, headers: false, col_sep: "\t", encoding: 'UTF-8')
|
@@ -190,14 +290,26 @@ module LucaBook
|
|
190
290
|
case i
|
191
291
|
when 0
|
192
292
|
idx_memo = row.map(&:to_s)
|
193
|
-
idx_memo.each
|
293
|
+
idx_memo.each do |r|
|
294
|
+
sum[:debit][r] ||= 0
|
295
|
+
sum[:debit_count][r] ||= 0
|
296
|
+
end
|
194
297
|
when 1
|
195
|
-
row.each_with_index
|
298
|
+
row.each_with_index do |r, j|
|
299
|
+
sum[:debit][idx_memo[j]] += r.to_i # TODO: bigdecimal support
|
300
|
+
sum[:debit_count][idx_memo[j]] += 1
|
301
|
+
end
|
196
302
|
when 2
|
197
303
|
idx_memo = row.map(&:to_s)
|
198
|
-
idx_memo.each
|
304
|
+
idx_memo.each do |r|
|
305
|
+
sum[:credit][r] ||= 0
|
306
|
+
sum[:credit_count][r] ||= 0
|
307
|
+
end
|
199
308
|
when 3
|
200
|
-
row.each_with_index
|
309
|
+
row.each_with_index do |r, j|
|
310
|
+
sum[:credit][idx_memo[j]] += r.to_i # TODO: bigdecimal support
|
311
|
+
sum[:credit_count][idx_memo[j]] += 1
|
312
|
+
end
|
201
313
|
else
|
202
314
|
puts row # for debug
|
203
315
|
end
|
@@ -210,20 +322,23 @@ module LucaBook
|
|
210
322
|
def self.net(year, month = nil, code = nil, date_range = nil)
|
211
323
|
g = gross(year, month, code, date_range)
|
212
324
|
idx = (g[:debit].keys + g[:credit].keys).uniq.sort
|
213
|
-
{}
|
325
|
+
count = {}
|
326
|
+
diff = {}.tap do |sum|
|
214
327
|
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)
|
328
|
+
sum[code] = g.dig(:debit, code).nil? ? 0 : Util.calc_diff(g[:debit][code], code)
|
329
|
+
sum[code] -= g.dig(:credit, code).nil? ? 0 : Util.calc_diff(g[:credit][code], code)
|
330
|
+
count[code] = (g.dig(:debit_count, code) || 0) + (g.dig(:credit_count, code) || 0)
|
217
331
|
end
|
218
332
|
end
|
333
|
+
[diff, count]
|
219
334
|
end
|
220
335
|
|
221
336
|
# TODO: replace load_tsv -> generic load_tsv_dict
|
222
337
|
def load_start
|
223
|
-
file = LucaSupport::Config::Pjdir
|
224
|
-
{}.tap do |
|
338
|
+
file = Pathname(LucaSupport::Config::Pjdir) / 'data' / 'balance' / 'start.tsv'
|
339
|
+
{}.tap do |dict|
|
225
340
|
load_tsv(file) do |row|
|
226
|
-
|
341
|
+
dict[row[0]] = row[2].to_i if ! row[2].nil?
|
227
342
|
end
|
228
343
|
end
|
229
344
|
end
|
@@ -235,24 +350,15 @@ module LucaBook
|
|
235
350
|
data.each { |row| yield row }
|
236
351
|
end
|
237
352
|
|
238
|
-
|
239
|
-
amount = /\./.match(num.to_s) ? BigDecimal(num) : num.to_i
|
240
|
-
amount * pn_debit(code.to_s)
|
241
|
-
end
|
353
|
+
private
|
242
354
|
|
243
|
-
def
|
244
|
-
|
245
|
-
when /^[0-4BCEGH]/
|
246
|
-
1
|
247
|
-
when /^[5-9ADF]/
|
248
|
-
-1
|
249
|
-
else
|
250
|
-
nil
|
251
|
-
end
|
252
|
-
end
|
355
|
+
def legal_items
|
356
|
+
return [] unless LucaSupport::Config::COUNTRY
|
253
357
|
|
254
|
-
|
255
|
-
|
358
|
+
case LucaSupport::Config::COUNTRY
|
359
|
+
when 'jp'
|
360
|
+
['91', '911', '912', '913', '9131', '9132', '914', '9141', '9142', '915', '916', '92', '93']
|
361
|
+
end
|
256
362
|
end
|
257
363
|
end
|
258
364
|
end
|