class LucaBook::Import

Constants

CREDIT_DEFAULT
DEBIT_DEFAULT

Public Class Methods

import_json(io) click to toggle source

JSON Format:

[
  {
    "date": "2020-05-04",
    "debit" : [
      {
        "label": "savings accounts",
        "amount": 20000
      }
    ],
    "credit" : [
      {
        "label": "trade notes receivable",
        "amount": 20000
      }
    ],
    "note": "settlement for the last month trade"
  }
]
# File lib/luca_book/import.rb, line 51
def self.import_json(io)
  JSON.parse(io).each do |d|
    code_map = LucaRecord::Dict.reverse(LucaRecord::Dict.load('base.tsv'))
    d['debit'].each { |h| h['code'] = code_map.dig(h['label']) || DEBIT_DEFAULT }
    d['credit'].each { |h| h['code'] = code_map.dig(h['label']) || CREDIT_DEFAULT }

    LucaBook::Journal.create(d)
  end
end
new(path, dict) click to toggle source
# File lib/luca_book/import.rb, line 20
def initialize(path, dict)
  raise 'no such file' unless FileTest.file?(path)

  @target_file = path
  # TODO: yaml need to be configurable
  @dict_name = dict
  @dict = LucaBook::Dict.new("import-#{dict}.yaml")
  @code_map = LucaRecord::Dict.reverse(LucaRecord::Dict.load('base.tsv'))
  @config = @dict.csv_config if dict
end

Public Instance Methods

import_csv() click to toggle source
# File lib/luca_book/import.rb, line 61
def import_csv
  @dict.load_csv(@target_file) do |row|
    if @config[:type] == 'single'
      LucaBook::Journal.create(parse_single(row))
    elsif @config[:type] == 'double'
      p parse_double(row) # TODO: Not implemented yet
    else
      p row
    end
  end
end
tax_extension(code1, code2, amount, options) click to toggle source

TODO: need to be separated into pluggable l10n module. TODO: gensen rate >1m yen. TODO: gensen & consumption `round()` rules need to be confirmed. Profit or Loss account should be specified as code1.

# File lib/luca_book/import_jp.rb, line 14
def tax_extension(code1, code2, amount, options)
  return nil if options.nil? || options[:tax_options].nil?
  return nil if !options[:tax_options].include?('jp-gensen') && !options[:tax_options].include?('jp-consumption')

  gensen_rate = BigDecimal('0.1021')
  consumption_rate = BigDecimal('0.1')
  gensen_code = @code_map.dig(options[:gensen_label]) || @code_map.dig('預り金')
  gensen_idx = /^[5-8B-G]/.match(code1) ? 1 : 0
  consumption_idx = /^[A-G]/.match(code1) ? 0 : 1
  consumption_code = @code_map.dig(options[:consumption_label])
  consumption_code ||= /^[A]/.match(code1) ? @code_map.dig('仮受消費税等') : @code_map.dig('仮払消費税等')
  if options[:tax_options].include?('jp-gensen') && options[:tax_options].include?('jp-consumption')
    paid_rate = BigDecimal('1') + consumption_rate - gensen_rate
    gensen_amount = (amount / paid_rate * gensen_rate).round
    consumption_amount = (amount / paid_rate * consumption_rate).round
    [].tap do |res|
      res << [].tap do |res1|
        amount1 = amount
        amount1 -= consumption_amount if consumption_idx == 0
        amount1 += gensen_amount if gensen_idx == 1
        res1 << { 'code' => code1, 'amount' => amount1 }
        res1 << { 'code' => consumption_code, 'amount' => consumption_amount } if consumption_idx == 0
        res1 << { 'code' => gensen_code, 'amount' => gensen_amount } if gensen_idx == 0
      end
      res << [].tap do |res2|
        amount2 = amount
        amount2 -= consumption_amount if consumption_idx == 1
        amount2 += gensen_amount if gensen_idx == 0
        res2 << { 'code' => code2, 'amount' => amount2 }
        res2 << { 'code' => consumption_code, 'amount' => consumption_amount } if consumption_idx == 1
        res2 << { 'code' => gensen_code, 'amount' => gensen_amount } if gensen_idx == 1
      end
    end
  elsif options[:tax_options].include?('jp-gensen')
    paid_rate = BigDecimal('1') - gensen_rate
    gensen_amount = (amount / paid_rate * gensen_rate).round
    [].tap do |res|
      res << [].tap do |res1|
        amount1 = amount
        amount1 += gensen_amount if gensen_idx == 1
        res1 << { 'code' => code, 'amount' => amount1 }
        res1 << { 'code' => gensen_code, 'amount' => gensen_amount } if gensen_idx == 0
      end
      res << [].tap do |res2|
        amount2 = amount
        amount2 += gensen_amount if gensen_idx == 0
        mount2 ||= amount
        res2 << { 'code' => code2, 'amount' => amount2 }
        res2 << { 'code' => gensen_code, 'amount' => gensen_amount } if gensen_idx == 1
      end
    end
  elsif options[:tax_options].include?('jp-consumption')
    paid_rate = BigDecimal('1') + consumption_rate - gensen_rate
    consumption_amount = (amount / paid_rate * consumption_rate).round
    res << [].tap do |res1|
      amount1 = amount
      amount1 -= consumption_amount if consumption_idx == 0
      res1 << { 'code' => code1, 'amount' => amount1 }
      res1 << { 'code' => consumption_code, 'amount' => consumption_amount } if consumption_idx == 0
    end
    res << [].tap do |res2|
      amount2 = amount
      amount2 -= consumption_amount if consumption_idx == 1
      res2 << { 'code' => code2, 'amount' => amount2 }
      res2 << { 'code' => consumption_code, 'amount' => consumption_amount } if consumption_idx == 1
    end
  end
end

Private Instance Methods

parse_date(row) click to toggle source
# File lib/luca_book/import.rb, line 131
def parse_date(row)
  return nil if row.dig(@config[:year]).empty?

  "#{row.dig(@config[:year])}-#{row.dig(@config[:month])}-#{row.dig(@config[:day])}"
end
parse_double(row) click to toggle source

convert double entry data

# File lib/luca_book/import.rb, line 110
def parse_double(row)
  {}.tap do |d|
    d['date'] = parse_date(row)
    d['debit'] = {
      'code' => search_code(row[@config[:label]], @config.dig(:default_debit)) || DEBIT_DEFAULT,
      'amount' => row.dig(@config[:debit_amount])
    }
    d['credit'] = {
      'code' => search_code(row[@config[:label]], @config.dig(:default_credit)) || CREDIT_DEFAULT,
      'amount' => row.dig(@config[:credit_amount])
    }
    d['note'] = Array(@config[:note]).map{ |col| row[col] }.join(' ')
    d['x-editor'] = "LucaBook::Import/#{@dict_name}"
  end
end
parse_single(row) click to toggle source

convert single entry data

# File lib/luca_book/import.rb, line 77
def parse_single(row)
  if (row.dig(@config[:credit_amount]) || []).empty?
    amount = BigDecimal(row[@config[:debit_amount]])
    debit = true
  else
    amount = BigDecimal(row[@config[:credit_amount]])
  end
  default_label = debit ? @config.dig(:default_debit) : @config.dig(:default_credit)
  code, options = search_code(row[@config[:label]], default_label, amount)
  counter_code = @code_map.dig(@config[:counter_label])
  if options
    x_customer = options[:'x-customer'] if options[:'x-customer']
    data, data_c = tax_extension(code, counter_code, amount, options) if respond_to? :tax_extension
  end
  data ||= [{ 'code' => code, 'amount' => amount }]
  data_c ||= [{ 'code' => counter_code, 'amount' => amount }]
  {}.tap do |d|
    d['date'] = parse_date(row)
    if debit
      d['debit'] = data
      d['credit'] = data_c
    else
      d['debit'] = data_c
      d['credit'] = data
    end
    d['note'] = Array(@config[:note]).map{ |col| row[col] }.join(' ')
    d['headers'] = { 'x-editor' => "LucaBook::Import/#{@dict_name}" }
    d['headers']['x-customer'] = x_customer if x_customer
  end
end
search_code(label, default_label, amount = nil) click to toggle source
# File lib/luca_book/import.rb, line 126
def search_code(label, default_label, amount = nil)
  label, options = @dict.search(label, default_label, amount)
  [@code_map.dig(label), options]
end