Class: LucaBook::Journal
- Inherits:
-
LucaRecord::Base
- Object
- LucaRecord::Base
- LucaBook::Journal
- Defined in:
- lib/luca_book/journal.rb
Overview
Journal has several annotations on headers:
- x-customer
-
Identifying customer.
- x-editor
-
Application name editing the journal.
- x-tax
-
For tracking tax related transaction.
Direct Known Subclasses
Constant Summary collapse
- ACCEPTED_HEADERS =
['x-customer', 'x-editor', 'x-tax']
Class Method Summary collapse
-
.add_header(journal_hash, key, val) ⇒ Object
Set accepted header with key/value, update record if exists.
-
.create(dat) ⇒ Object
create journal from hash.
-
.filter_by_code(start_year, start_month, end_year, end_month, code, recursive = true, basedir = @dirname) ⇒ Object
Load data based on account code.
-
.journal2csv(d) ⇒ Object
Convert journal object to TSV format.
-
.load_data(io, path) ⇒ Object
override de-serializing journal format.
-
.save(dat) ⇒ Object
update journal with hash.
-
.serialize_on_key(array_of_hash, key) ⇒ Object
collect values on specified key.
- .update_codes(obj) ⇒ Object
- .validate(obj) ⇒ Object
Class Method Details
.add_header(journal_hash, key, val) ⇒ Object
Set accepted header with key/value, update record if exists.
77 78 79 80 81 82 83 84 85 86 |
# File 'lib/luca_book/journal.rb', line 77 def self.add_header(journal_hash, key, val) return journal_hash if val.nil? return journal_hash unless ACCEPTED_HEADERS.include?(key) journal_hash.tap do |o| o[:headers] = {} unless o.dig(:headers) o[:headers][key] = val save o if o[:id] end end |
.create(dat) ⇒ Object
create journal from hash
23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/luca_book/journal.rb', line 23 def self.create(dat) d = LucaSupport::Code.keys_stringify(dat) validate(d) raise 'NoDateKey' unless d.key?('date') date = Date.parse(d['date']) # TODO: need to sync filename & content. Limit code length for filename # codes = (debit_code + credit_code).uniq codes = nil create_record(nil, date, codes) { |f| f.write journal2csv(d) } end |
.filter_by_code(start_year, start_month, end_year, end_month, code, recursive = true, basedir = @dirname) ⇒ Object
Load data based on account code.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/luca_book/journal.rb', line 168 def self.filter_by_code(start_year, start_month, end_year, end_month, code, recursive = true, basedir = @dirname) return enum_for(:filter_by_code, start_year, start_month, end_year, end_month, code, basedir) unless block_given? re = recursive ? "^#{code}" : "^#{code}$" LucaSupport::Code.encode_term(start_year, start_month, end_year, end_month).each do |subdir| open_records(basedir, subdir, nil, nil) do |f, path| CSV.new(f, headers: false, col_sep: "\t", encoding: 'UTF-8') .each.with_index(0) do |line, i| case i when 0 if line.find { |cd| /#{re}/.match(cd) } f.rewind yield load_data(f, path), path break end when 2 if line.find { |cd| /#{re}/.match(cd) } f.rewind yield load_data(f, path), path end when 3 break else # skip end end end end end |
.journal2csv(d) ⇒ Object
Convert journal object to TSV format.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/luca_book/journal.rb', line 54 def self.journal2csv(d) debit_amount = LucaSupport::Code.decimalize(serialize_on_key(d['debit'], 'amount')) credit_amount = LucaSupport::Code.decimalize(serialize_on_key(d['credit'], 'amount')) raise 'BalanceUnmatch' if debit_amount.inject(:+) != credit_amount.inject(:+) debit_code = serialize_on_key(d['debit'], 'code') credit_code = serialize_on_key(d['credit'], 'code') csv = CSV.generate(String.new, col_sep: "\t", headers: false) do |f| f << debit_code f << LucaSupport::Code.readable(debit_amount) f << credit_code f << LucaSupport::Code.readable(credit_amount) ACCEPTED_HEADERS.each do |x_header| f << [x_header, d['headers'][x_header]] if d.dig('headers', x_header) end f << [] f << [d.dig('note')] end end |
.load_data(io, path) ⇒ Object
override de-serializing journal format. Sample format is:
{
id: '2021A/V001',
headers: {
'x-customer' => 'Some Customer Co.'
},
debit: [
{ code: 'A12', amount: 1000 }
],
credit: [
{ code: '311', amount: 1000 }
],
note: 'note for each journal'
}
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/luca_book/journal.rb', line 132 def self.load_data(io, path) {}.tap do |record| body = false record[:id] = "#{path[0]}/#{path[1]}" CSV.new(io, headers: false, col_sep: "\t", encoding: 'UTF-8') .each.with_index(0) do |line, i| case i when 0 record[:debit] = line.map { |row| { code: row } } when 1 line.each_with_index { |amount, j| record[:debit][j][:amount] = BigDecimal(amount.to_s) } when 2 record[:credit] = line.map { |row| { code: row } } when 3 line.each_with_index { |amount, j| record[:credit][j][:amount] = BigDecimal(amount.to_s) } else case body when false if line.empty? record[:note] ||= [] body = true else record[:headers] ||= {} record[:headers][line[0]] = line[1] end when true record[:note] << line.join(' ') if body end end end record[:note] = record[:note]&.join('\n') end end |
.save(dat) ⇒ Object
update journal with hash. If record not found with id, no record will be created.
40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/luca_book/journal.rb', line 40 def self.save(dat) d = LucaSupport::Code.keys_stringify(dat) raise 'record has no id.' if d['id'].nil? validate(d) parts = d['id'].split('/') raise 'invalid ID' if parts.length != 2 codes = nil open_records(@dirname, parts[0], parts[1], codes, 'w') { |f, _path| f.write journal2csv(d) } end |
.serialize_on_key(array_of_hash, key) ⇒ Object
collect values on specified key
112 113 114 |
# File 'lib/luca_book/journal.rb', line 112 def self.serialize_on_key(array_of_hash, key) array_of_hash.map { |h| h[key] } end |
.update_codes(obj) ⇒ Object
88 89 90 91 92 93 |
# File 'lib/luca_book/journal.rb', line 88 def self.update_codes(obj) debit_code = serialize_on_key(obj[:debit], :code) credit_code = serialize_on_key(obj[:credit], :code) codes = (debit_code + credit_code).uniq.sort.compact change_codes(obj[:id], codes) end |
.validate(obj) ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/luca_book/journal.rb', line 95 def self.validate(obj) raise 'NoDebitKey' unless obj.key?('debit') raise 'NoCreditKey' unless obj.key?('credit') debit_codes = serialize_on_key(obj['debit'], 'code').compact debit_amount = serialize_on_key(obj['debit'], 'amount').compact raise 'NoDebitCode' if debit_codes.empty? raise 'NoDebitAmount' if debit_amount.empty? raise 'UnmatchDebit' if debit_codes.length != debit_amount.length credit_codes = serialize_on_key(obj['credit'], 'code').compact credit_amount = serialize_on_key(obj['credit'], 'amount').compact raise 'NoCreditCode' if credit_codes.empty? raise 'NoCreditAmount' if credit_amount.empty? raise 'UnmatchCredit' if credit_codes.length != credit_amount.length end |