scottohara/loot

View on GitHub
app/models/split_transaction.rb

Summary

Maintainability
A
2 hrs
Test Coverage
A
100%
# Copyright (c) 2016 Scott O'Hara, oharagroup.net
# frozen_string_literal: true

# Split transaction
class SplitTransaction < PayeeCashTransaction
    has_one :transaction_account, foreign_key: 'transaction_id', autosave: true, dependent: :destroy
    has_one :account, through: :transaction_account
    has_many :transaction_splits, foreign_key: 'parent_id', inverse_of: :parent, dependent: :destroy
    has_many :subtransactions, -> { where transaction_type: 'Sub' }, class_name: 'SubTransaction', through: :transaction_splits, source: :trx
    has_many :subtransfers, -> { where transaction_type: 'Subtransfer' }, class_name: 'SubtransferTransaction', through: :transaction_splits, source: :trx
    after_initialize do |t|
        t.transaction_type = 'Split'
    end

    include ::Categorisable

    class << self
        def create_from_json(json)
            s = super
            s.build_transaction_account(direction: json['direction'], status: json['status']).account = ::Account.find json['primary_account']['id']
            s.create_children json['subtransactions']
            s.save!
            s
        end

        def update_from_json(json)
            s = includes(:header, :transaction_account).find json[:id]
            s.update_from_json json
            s
        end
    end

    def create_children(children)
        children.each do |child|
            # Keys could be symbols or strings
            child = child.with_indifferent_access if child.is_a?(::Hash)

            # Clear the id and copy the header details from the parent
            child['id'] = nil
            child['transaction_date'] = header.transaction_date
            child['payee'] = {id: header.payee.id}

            unless header.schedule.nil?
                child['next_due_date'] = header.schedule.next_due_date
                child['frequency'] = header.schedule.frequency
            end

            transaction_splits.build.trx = ::Transaction.class_for(child['transaction_type']).create_from_json child
        end
    end

    def update_from_json(json)
        super
        transaction_account.direction = json['direction']
        self.account = ::Account.find json['primary_account']['id']
        subtransactions.each(&:destroy)
        subtransfers.each(&:destroy)
        create_children json['subtransactions']
        save!
    end

    def as_json(options = {})
        super.merge(
            primary_account: account.as_json,
            category: {
                id: (transaction_account.direction.eql?('inflow') && 'SplitFrom') || 'SplitTo',
                name: (transaction_account.direction.eql?('inflow') && 'Split From') || 'Split To'
            },
            direction: transaction_account.direction,
            status: transaction_account.status
        )
    end

    def children
        # Get the child transactions
        transactions =
            ::TransactionSplit
            .select(
                'transactions.id',
                'transactions.transaction_type',
                'parent_transactions.transaction_type AS parent_transaction_type',
                'categories.id AS category_id',
                'categories.name AS category_name',
                'categories.direction AS category_direction',
                'parent_categories.id AS parent_category_id',
                'parent_categories.name AS parent_category_name',
                'accounts.id AS account_id',
                'accounts.name AS account_name',
                'transactions.amount',
                'transaction_accounts.direction',
                'transactions.memo',
                'transaction_flags.flag_type',
                'transaction_flags.memo AS flag'
            )
            .joins(
                [
                    'JOIN transaction_accounts ON transaction_accounts.transaction_id = transaction_splits.parent_id',
                    'JOIN transactions ON transactions.id = transaction_splits.transaction_id',
                    'JOIN transactions parent_transactions ON parent_transactions.id = transaction_splits.parent_id',
                    'LEFT OUTER JOIN transaction_categories ON transaction_categories.transaction_id = transactions.id',
                    'LEFT OUTER JOIN categories ON categories.id = transaction_categories.category_id',
                    'LEFT OUTER JOIN categories parent_categories ON parent_categories.id = categories.parent_id',
                    'LEFT OUTER JOIN transaction_accounts transfer_transaction_accounts ON transfer_transaction_accounts.transaction_id = transactions.id AND transfer_transaction_accounts.account_id != transaction_accounts.account_id',
                    'LEFT OUTER JOIN accounts ON accounts.id = transfer_transaction_accounts.account_id',
                    'LEFT OUTER JOIN transaction_flags ON transaction_flags.transaction_id = transactions.id'
                ]
            )
            .where transaction_splits: {parent_id: id}

        # Remap to the desired output format
        transactions.map do |trx|
            {
                id: trx['id'],
                transaction_type: trx['transaction_type'],
                category: self.class.transaction_category(trx),
                subcategory: self.class.basic_subcategory(trx),
                account: {
                    id: trx['account_id'],
                    name: trx['account_name']
                },
                amount: trx['amount'],
                direction: (trx['transaction_type'].eql?('Subtransfer') && ((trx['parent_transaction_type'].eql?('Payslip') && 'outflow') || trx['direction'])) || trx['category_direction'],
                memo: trx['memo'],
                flag_type: trx['flag_type'],
                flag: trx['flag']
            }
        end
    end
end