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.
79 80 81 82 83 84 85 86 87 88 |
# File 'lib/luca_book/journal.rb', line 79 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 36 |
# 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 serialized = journal2csv(d) create_record(nil, date, codes) { |f| f.write serialized } end |
.filter_by_code(start_year, start_month, end_year, end_month, code, recursive = true, basedir = @dirname) ⇒ Object
Load data based on account code.
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 196 197 |
# File 'lib/luca_book/journal.rb', line 170 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.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/luca_book/journal.rb', line 56 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'
}
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 165 166 |
# File 'lib/luca_book/journal.rb', line 134 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.
41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/luca_book/journal.rb', line 41 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 serialized = journal2csv(d) open_records(@dirname, parts[0], parts[1], codes, 'w') { |f, _path| f.write serialized } end |
.serialize_on_key(array_of_hash, key) ⇒ Object
collect values on specified key
114 115 116 |
# File 'lib/luca_book/journal.rb', line 114 def self.serialize_on_key(array_of_hash, key) array_of_hash.map { |h| h[key] } end |
.update_codes(obj) ⇒ Object
90 91 92 93 94 95 |
# File 'lib/luca_book/journal.rb', line 90 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
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/luca_book/journal.rb', line 97 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 |