schurig/ynab-bank-importer

View on GitHub
lib/transaction_creator.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'ynab'

# Calculates the correct parameters for the YNAB transaction
# and build it.
class TransactionCreator
  attr_accessor :account_id, :date, :amount, :payee_name, :payee_id,
                :category_name, :category_id, :memo,
                :import_id, :is_withdrawal

  class <<self
    require 'ynab/models/save_transaction'

    # rubocop:disable Metrics/MethodLength
    def call(options = {})
      YNAB::SaveTransaction.new(
        account_id: options.fetch(:account_id),
        date: options.fetch(:date),
        amount: options.fetch(:amount),
        payee_id: payee_id(options),
        payee_name: payee_name(options),
        category_id: category_id(options),
        memo: memo(options),
        import_id: options.fetch(:import_id),
        flag_color: flag_color(options),
        cleared: 'cleared' # TODO: shouldn't be cleared if date is in the future
      )
    end
    # rubocop:enable Metrics/MethodLength

    def payee_id(options)
      payee_id = options.fetch(:payee_id, nil)
      return payee_id if payee_id

      if withdrawal?(options) && cash_account_id
        return find_payee_id_by_account_id(cash_account_id)
      end

      account_payee_id = account_payee_id(options)
      return account_payee_id if account_payee_id
      nil
    end

    def payee_name(options)
      return nil if payee_id(options)
      payee = options.fetch(:payee_name, nil)
      # The api has a limit of 50 characters for the payee field
      payee = truncate(payee, 50)
      payee
    end

    def memo(options)
      memo = options.fetch(:memo, nil)
      # The api has a limit of 100 characters for the momo field
      memo = truncate(memo, 100)
      return "ATM withdrawal #{memo}" if withdrawal?(options)
      memo
    end

    def category_id(_options)
      # TODO: query through all categories and match by category_name
      nil
    end

    # Helper methods

    def truncate(string, max)
      return nil unless string
      string.length > max ? string[0...max] : string
    end

    def cash_account_id
      Settings.all['ynab'].fetch('cash_account_id', nil)
    end

    def withdrawal?(options)
      options.fetch(:is_withdrawal, nil)
    end

    def account_payee_id(options)
      result = Settings.all['accounts'].find do |account|
        payee_iban = payee_iban(options)
        account['ynab_id'] && payee_iban && account['iban'] == payee_iban
      end

      return nil unless result
      find_payee_id_by_account_id(result['ynab_id'])
    end

    def find_payee_id_by_account_id(account_id)
      return nil unless account_id
      budget_id = Settings.all['ynab'].fetch('budget_id')
      access_token = Settings.all['ynab'].fetch('access_token')
      YNAB::API.new(access_token)
               .accounts
               .get_account_by_id(budget_id, account_id)
               .data.account.transfer_payee_id
    end

    def flag_color(_options)
      nil
    end

    def payee_iban(options)
      iban = options.fetch(:payee_iban, nil)
      iban.delete(' ') if iban
    end
  end
end