class LucaBook::State

Attributes

bs_data[R]
pl_data[R]
start_balance[R]
statement[R]

Public Class Methods

accumulate_term(start_year, start_month, end_year, end_month) click to toggle source
# File lib/luca_book/state.rb, line 189
def self.accumulate_term(start_year, start_month, end_year, end_month)
  date = Date.new(start_year, start_month, 1)
  last_date = Date.new(end_year, end_month, -1)
  return nil if date > last_date

  {}.tap do |res|
    diff, _count = net(date.year, date.month, last_date.year, last_date.month)
    diff.each do |k, v|
      next if /^[_]/.match(k)

      res[k] = res[k].nil? ? v : res[k] + v
    end
  end
end
by_code(code, from_year, from_month, to_year = from_year, to_month = from_month, recursive: false) click to toggle source
# File lib/luca_book/state.rb, line 52
def self.by_code(code, from_year, from_month, to_year = from_year, to_month = from_month, recursive: false)
  code = search_code(code) if code
  date = Date.new(from_year.to_i, from_month.to_i, -1)
  last_date = Date.new(to_year.to_i, to_month.to_i, -1)
  raise 'invalid term specified' if date > last_date

  balance = start_balance(date.year, date.month, recursive: recursive)[code] || 0
  first_line = { 'code' => nil, 'label' => nil, 'debit_amount' => nil, 'debit_count' => nil, 'credit_amount' => nil, 'credit_count' => nil, 'net' => nil, 'balance' => balance, '_d' => nil }
  reports = [first_line].tap do |r|
    while date <= last_date do
      diff = {}.tap do |h|
        g = gross(date.year, date.month, code: code, recursive: recursive)
        sum = g.dig(:debit, code).nil? ? BigDecimal('0') : Util.calc_diff(g[:debit][code], code)
        sum -= g.dig(:credit, code).nil? ? BigDecimal('0') : Util.calc_diff(g[:credit][code], code)
        balance += sum
        h['code'] = code
        h['label'] = @@dict.dig(code, :label)
        h['debit_amount'] = g.dig(:debit, code)
        h['debit_count'] = g.dig(:debit_count, code)
        h['credit_amount'] = g.dig(:credit, code)
        h['credit_count'] = g.dig(:credit_count, code)
        h['net'] = sum
        h['balance'] = balance
        h['_d'] = date.to_s
      end
      r << diff
      date = Date.new(date.next_month.year, date.next_month.month, -1)
    end
  end
  LucaSupport::Code.readable(reports)
end
new(data, count = nil, start_d: nil, end_d: nil) click to toggle source
# File lib/luca_book/state.rb, line 24
def initialize(data, count = nil, start_d: nil, end_d: nil)
  @monthly = data
  @count = count
  @start_date = start_d
  @end_date = end_d
  set_balance
end
range(from_year, from_month, to_year = from_year, to_month = from_month) click to toggle source
# File lib/luca_book/state.rb, line 32
def self.range(from_year, from_month, to_year = from_year, to_month = from_month)
  date = Date.new(from_year.to_i, from_month.to_i, -1)
  last_date = Date.new(to_year.to_i, to_month.to_i, -1)
  raise 'invalid term specified' if date > last_date

  counts = []
  reports = [].tap do |r|
    while date <= last_date do
      diff, count = accumulate_month(date.year, date.month)
      r << diff.tap { |c| c['_d'] = date.to_s }
      counts << count.tap { |c| c['_d'] = date.to_s }
      date = Date.new(date.next_month.year, date.next_month.month, -1)
    end
  end
  new(reports, counts,
      start_d: Date.new(from_year.to_i, from_month.to_i, 1),
      end_d: Date.new(to_year.to_i, to_month.to_i, -1)
     )
end
search_code(code) click to toggle source
# File lib/luca_book/state.rb, line 269
def self.search_code(code)
  return code if @@dict.dig(code)

  @@dict.search(code).tap do |new_code|
    if new_code.nil?
      puts "Search word is not matched with labels"
      exit 1
    end
  end
end
start_balance(year, month, recursive: true) click to toggle source
# File lib/luca_book/state.rb, line 214
def self.start_balance(year, month, recursive: true)
  start_date = Date.new(year, month, 1)
  base = Dict.latest_balance(start_date).each_with_object({}) do |(k, v), h|
    h[k] = BigDecimal(v[:balance].to_s) if v[:balance]
    h[k] ||= BigDecimal('0') if k.length == 2
  end
  if month == CONFIG['fy_start'].to_i
    return recursive ? total_subaccount(base) : base
  end

  pre_last = start_date.prev_month
  year -= 1 if month <= CONFIG['fy_start'].to_i
  pre = accumulate_term(year, CONFIG['fy_start'], pre_last.year, pre_last.month)
  total = {}.tap do |h|
    (pre.keys + base.keys).uniq.each do |k|
      h[k] = (base[k] || BigDecimal('0')) + (pre[k] || BigDecimal('0'))
    end
  end
  recursive ? total_subaccount(total) : total
end

Public Instance Methods

accumulate_balance(data) click to toggle source
# File lib/luca_book/state.rb, line 159
def accumulate_balance(data)
  { debit: [], credit: [] }.tap do |res|
    data.sort.to_h.each do |k, v|
      case k
      when /^[0-4].*/
        res[:debit] << { k => v }
      when /^[5-9].*/
        res[:credit] << { k => v }
      end
    end
  end
end
bs(level = 3, legal: false) click to toggle source
# File lib/luca_book/state.rb, line 139
def bs(level = 3, legal: false)
  set_bs(level, legal: legal)
  base = accumulate_balance(@bs_data)
  rows = [base[:debit].length, base[:credit].length].max
  @statement = [].tap do |a|
    rows.times do |i|
      {}.tap do |res|
        res['debit_label'] = base[:debit][i] ? @@dict.dig(base[:debit][i].keys[0], :label) : ''
        #res['debit_balance'] = base[:debit][i] ? (@start_balance.dig(base[:debit][i].keys[0]) || 0) + base[:debit][i].values[0] : ''
        res['debit_balance'] = base[:debit][i] ? base[:debit][i].values[0] : ''
        res['credit_label'] = base[:credit][i] ? @dict.dig(base[:credit][i].keys[0], :label) : ''
        #res['credit_balance'] = base[:credit][i] ? (@start_balance.dig(base[:credit][i].keys[0]) || 0) + base[:credit][i].values[0] : ''
        res['credit_balance'] = base[:credit][i] ? base[:credit][i].values[0] : ''
        a << res
      end
    end
  end
  readable(@statement)
end
code2label() click to toggle source
# File lib/luca_book/state.rb, line 84
def code2label
  @statement ||= @monthly
  @statement.map do |report|
    {}.tap do |h|
      report.each { |k, v| h[@@dict.dig(k, :label) || k] = v }
    end
  end
end
code_sum(report) click to toggle source
# File lib/luca_book/state.rb, line 204
def code_sum(report)
  legal_items.each.with_object({}) do |k, h|
    h[k] = self.class.sum_matched(report, /^#{k}.*/)
  end
end
net_income() click to toggle source
# File lib/luca_book/state.rb, line 184
def net_income
  set_pl(2)
  @pl_data['HA']
end
pl(level = 2) click to toggle source
# File lib/luca_book/state.rb, line 172
def pl(level = 2)
  set_pl(level)
  @statement = @monthly.map do |data|
    {}.tap do |h|
      @pl_data.keys.each { |k| h[k] = data[k] || BigDecimal('0') }
    end
  end
  @statement << @pl_data
  readable(code2label)
end
render_xbrl(filename = nil) click to toggle source
# File lib/luca_book/state.rb, line 235
def render_xbrl(filename = nil)
  set_bs(3, legal: true)
  set_pl(3)
  country_suffix = CONFIG['country'] || 'en'
  @company = CGI.escapeHTML(CONFIG.dig('company', 'name'))
  @balance_sheet_selected = 'true'
  @pl_selected = 'true'
  @capital_change_selected = 'true'
  @issue_date = Date.today

  prior_bs = @start_balance.filter { |k, _v| /^[9]/.match(k) }
  @xbrl_entries = @bs_data.map{ |k, v| xbrl_line(k, v, prior_bs[k]) }.compact.join("\n")
  @xbrl_entries += @pl_data.map{ |k, v| xbrl_line(k, v) }.compact.join("\n")
  @filename = filename || @issue_date.to_s

  File.open("#{@filename}.xbrl", 'w') { |f| f.write render_erb(search_template("base-#{country_suffix}.xbrl.erb")) }
  File.open("#{@filename}.xsd", 'w') { |f| f.write render_erb(search_template("base-#{country_suffix}.xsd.erb")) }
end
report_mail(level = 3) click to toggle source

TODO: pl/bs may not be immutable

# File lib/luca_book/state.rb, line 118
def report_mail(level = 3)
  @company = CONFIG.dig('company', 'name')
  {}.tap do |res|
    pl(level).reverse.each do |month|
      month.each do |k, v|
        res[k] ||= []
        res[k] << v
      end
    end
    @months = res['_d']
    @pl = res.select{ |k,v| k != '_d' }
  end
  @bs = bs

  mail = Mail.new
  mail.to = CONFIG.dig('mail', 'preview') || CONFIG.dig('mail', 'from')
  mail.subject = 'Financial Report available'
  mail.html_part = Mail::Part.new(body: render_erb(search_template('monthly-report.html.erb')), content_type: 'text/html; charset=UTF-8')
  LucaSupport::Mail.new(mail, PJDIR).deliver
end
set_balance() click to toggle source
# File lib/luca_book/state.rb, line 210
def set_balance
  @start_balance = self.class.start_balance(@start_date.year, @start_date.month)
end
stats(level = nil) click to toggle source
# File lib/luca_book/state.rb, line 93
def stats(level = nil)
  keys = @count.map(&:keys).flatten.push('_t').uniq.sort
  @count.map! do |data|
    sum = 0
    keys.each do |k|
      data[k] ||= 0
      sum += data[k] if /^[^_]/.match(k)
      next if level.nil? || k.length <= level

      if data[k[0, level]]
        data[k[0, level]] += data[k]
      else
        data[k[0, level]] = data[k]
      end
    end
    data.select! { |k, _v| k.length <= level } if level
    data['_t'] = sum
    data.sort.to_h
  end
  keys.map! { |k| k[0, level] }.uniq.select! { |k| k.length <= level } if level
  @count.prepend({}.tap { |header| keys.each { |k| header[k] = @@dict.dig(k, :label) }})
  @count
end
xbrl_line(code, amount, prior_amount = nil) click to toggle source
# File lib/luca_book/state.rb, line 254
def xbrl_line(code, amount, prior_amount = nil)
  return nil if /^_/.match(code)

  context = /^[0-9]/.match(code) ? 'CurrentYearNonConsolidatedInstant' : 'CurrentYearNonConsolidatedDuration'
  tag = @@dict.dig(code, :xbrl_id)
  #raise "xbrl_id not found: #{code}" if tag.nil?
  return nil if tag.nil?
  return nil if readable(amount).zero? && prior_amount.nil?

  prior = prior_amount.nil? ? '' : "<#{tag} decimals=\"0\" unitRef=\"JPY\" contextRef=\"Prior1YearNonConsolidatedInstant\">#{readable(prior_amount)}</#{tag}>\n"
  current = "<#{tag} decimals=\"0\" unitRef=\"JPY\" contextRef=\"#{context}\">#{readable(amount)}</#{tag}>"

  prior + current
end

Private Instance Methods

lib_path() click to toggle source
# File lib/luca_book/state.rb, line 320
def lib_path
  __dir__
end
set_bs(level = 3, legal: false) click to toggle source
# File lib/luca_book/state.rb, line 282
def set_bs(level = 3, legal: false)
  @start_balance.each do |k, v|
    next if /^_/.match(k)
    @monthly.first[k] = (v || 0) + (@monthly.first[k] || 0)
  end
  list = @monthly.map { |data| data.select { |k, _v| k.length <= level } }
  list.map! { |data| code_sum(data).merge(data) } if legal
  @bs_data = list.each_with_object({}) do |month, h|
    month.each do |k, v|
      next if /^_/.match(k)

      h[k] = (h[k] || BigDecimal('0')) + v
    end
  end
end
set_pl(level = 2) click to toggle source
# File lib/luca_book/state.rb, line 298
def set_pl(level = 2)
  keys = @monthly.inject([]) { |a, data| a + data.keys }
           .compact.select { |k| /^[A-H_].+/.match(k) }
           .uniq.sort
  keys.select! { |k| k.length <= level }
  @pl_data = @monthly.each_with_object({}) do |item, h|
    keys.each do |k|
      h[k] = (h[k] || BigDecimal('0')) + (item[k] || BigDecimal('0')) if /^[^_]/.match(k)
    end
    h['_d'] = 'Period Total'
  end
end