Class: LucaBook::Dict

Inherits:
LucaRecord::Dict
  • Object
show all
Includes:
Accumulator, Util, LucaRecord::IO, LucaSupport::Range
Defined in:
lib/luca_book/dict.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Accumulator

#credit_amount, #credit_count, #debit_amount, #debit_count, #each_month, #gross_amount, #gross_count, included, #net_amount

Methods included from Util

amount_by_code, calc_diff, current_fy, diff_by_code, pn_debit, previous_fy, validate_balance

Class Method Details

.checksum(start_date, end_date) ⇒ Object



189
190
191
192
193
194
195
# File 'lib/luca_book/dict.rb', line 189

def self.checksum(start_date, end_date)
  digest = update_digest(String.new, File.read(latest_balance_path(start_date)))
  term_by_month(start_date, end_date)
    .map { |date| dir_digest(date.year, date.month) }
    .each { |month_digest| digest = update_digest(digest, month_digest) }
  digest
end

.export_balance(date) ⇒ Object

TODO: support date in the middle of month.



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/dict.rb', line 144

def self.export_balance(date)
  start_date, end_date = Util.current_fy(date, to: date)
  labels = load('base.tsv')
  bs = load_balance(start_date, end_date)
  dat = [].tap do |res|
    item = {}
    item['date'] = date.to_s
    item['debit'] = []
    item['credit'] = []
    bs.each do |code, balance|
      next if balance.zero?

      if /^[1-4]/.match(code)
        item['debit'] << { 'label' => labels.dig(code, :label), 'amount' => LucaSupport::Code.readable(balance) }
      elsif /^[5-9]/.match(code)
        item['credit'] << { 'label' => labels.dig(code, :label), 'amount' => LucaSupport::Code.readable(balance) }
      end
    end
    item['x-editor'] = 'LucaBook'
    res << item
  end
  puts JSON.dump(dat)
end

.generate_balance(year, month = nil) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/luca_book/dict.rb', line 118

def self.generate_balance(year, month = nil)
  start_date = Date.new((year.to_i - 1), LucaSupport::CONST.config['fy_start'], 1)
  month ||= LucaSupport::CONST.config['fy_start'] - 1
  end_date = Date.new(year.to_i, month, -1)
  labels = load('base.tsv')
  bs = load_balance(start_date, end_date)
  fy_digest = checksum(start_date, end_date)
  current_ref = gitref
  csv = CSV.generate(String.new, col_sep: "\t", headers: false) do |f|
    f << ['code', 'label', 'balance']
    f << ['_date', end_date]
    f << ['_digest', fy_digest]
    f << ['_gitref', current_ref] if current_ref
    bs.each do |code, balance|
      next if LucaSupport::Code.readable(balance) == 0

      f << [code, labels.dig(code, :label), LucaSupport::Code.readable(balance)]
    end
  end
  dict_dir = Pathname(LucaSupport::CONST.pjdir) / 'data' / 'balance'
  filepath = dict_dir / "start-#{end_date.to_s}.tsv"

  File.open(filepath, 'w') { |f| f.write csv }
end

.gitrefObject



197
198
199
200
# File 'lib/luca_book/dict.rb', line 197

def self.gitref
  digest = `git rev-parse HEAD`
  $?.exitstatus == 0 ? digest.strip : nil
end

.issue_date(obj) ⇒ Object



114
115
116
# File 'lib/luca_book/dict.rb', line 114

def self.issue_date(obj)
  Date.parse(obj.dig('_date', :label))
end

.latest_balance(date) ⇒ Object

Find balance at financial year start by given date. If not found ‘start-yyyy-mm-*.tsv’, use ‘start.tsv’ as default.



100
101
102
# File 'lib/luca_book/dict.rb', line 100

def self.latest_balance(date)
  load_tsv_dict(latest_balance_path(date))
end

.latest_balance_path(date) ⇒ Object



104
105
106
107
108
109
110
111
112
# File 'lib/luca_book/dict.rb', line 104

def self.latest_balance_path(date)
  start_date, _ = LucaBook::Util.current_fy(date)
  dict_dir = Pathname(LucaSupport::CONST.pjdir) / 'data' / 'balance'
  paths = Dir.glob(%Q(start-*-*-*), base: dict_dir, sort: true)
            .reject {|path| path > %Q(start-#{start_date.year}-#{format("%02d", start_date.month)}-) }
  return dict_dir / 'start.tsv' if paths.empty?

  dict_dir / paths.reverse.first
end

.load_balance(start_date, end_date) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/luca_book/dict.rb', line 168

def self.load_balance(start_date, end_date)
  base = latest_balance(start_date).each_with_object({}) do |(k, v), h|
    h[k] = BigDecimal(v[:balance].to_s) if v[:balance]
  end

  search_range = term_by_month(start_date, end_date)
  bs = search_range.each_with_object(base) do |date, h|
    net(date.year, date.month)[0].each do |code, amount|
      next if /^[^1-9]/.match(code)

      h[code] ||= BigDecimal('0')
      h[code] += amount
    end
  end
  bs['9142'] ||= BigDecimal('0')
  bs['9142'] += LucaBook::State
                 .range(start_date.year, start_date.month, end_date.year, end_date.month)
                 .net_income
  bs.sort
end

Instance Method Details

#csv_configObject

Column number settings for CSV/TSV convert

:label

for double entry data

:counter_label

must be specified with label

:debit_label

for double entry data
  • debit_amount

:credit_label

for double entry data
  • credit_amount

:note

can be the same column as another label

:encoding

file encoding


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/luca_book/dict.rb', line 38

def csv_config
  {}.tap do |config|
    if @config.dig('label')
      config[:label] = @config['label'].to_i
      if @config.dig('counter_label')
        config[:counter_label] = @config['counter_label']
        config[:type] = 'single'
      end
    elsif @config.dig('debit_label')
      config[:debit_label] = @config['debit_label'].to_i
      if @config.dig('credit_label')
        config[:credit_label] = @config['credit_label'].to_i
        config[:type] = 'double'
      end
    end
    config[:type] ||= 'invalid'
    config[:debit_amount] = @config['debit_amount'].to_i if @config.dig('debit_amount')
    config[:credit_amount] = @config['credit_amount'].to_i if @config.dig('credit_amount')
    config[:note] = @config['note'] if @config.dig('note')
    config[:encoding] = @config['encoding'] if @config.dig('encoding')

    config[:year] = @config['year'] if @config.dig('year')
    config[:month] = @config['month'] if @config.dig('month')
    config[:day] = @config['day'] if @config.dig('day')
    config[:default_debit] = @config['default_debit'] if @config.dig('default_debit')
    config[:default_credit] = @config['default_credit'] if @config.dig('default_credit')
  end
end

#filter_amount(settings, amount = nil) ⇒ Object

Choose setting on Big or small condition.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/luca_book/dict.rb', line 78

def filter_amount(settings, amount = nil)
  return settings[0] if amount.nil?

  settings.each do |item|
    return item unless item[1].keys.include?(:on_amount)

    condition = item.dig(1, :on_amount)
    case condition[0]
    when '>'
      return item if amount > BigDecimal(condition[1..])
    when '<'
      return item if amount < BigDecimal(condition[1..])
    else
      return item
    end
  end
  nil
end

#search(word, default_word = nil, amount = nil) ⇒ Object



67
68
69
70
71
72
73
74
# File 'lib/luca_book/dict.rb', line 67

def search(word, default_word = nil, amount = nil)
  res = super(word, default_word, main_key: 'account_label')
  if res.is_a?(Array) && res[0].is_a?(Array)
    filter_amount(res, amount)
  else
    res
  end
end