Use a String For a Variable name and convert Dictionaries to DataFrames

And here’s a simpler way, in case it’s OK to have missing instead of 0 values for accounts that are not touched by a transaction:

using DataFrames

accounts = DataFrame()
journal = DataFrame()

function transaction!(date, debit_account, debit_amount, credit_account, credit_amount)
    if debit_amount != credit_amount
        error("Debit ($debit_amount) and credit ($credit_amount) must match")
    end

    push!(accounts, cols=:union, Dict(debit_account*"_debit" => debit_amount,
                                      credit_account*"_credit" => credit_amount))

    push!(journal, (; date, debit_account, credit_account, debit_amount, credit_amount))
end

transaction!("Jan 1, 2019", "Wages", 1000, "Chequing", 1000)
transaction!("Jan 2, 2019", "Personal", 300, "Rent", 300)


julia> accounts
2×4 DataFrame
 Row │ Chequing_credit  Wages_debit  Rent_credit  Personal_debit 
     │ Int64?           Int64?       Int64?       Int64?         
─────┼───────────────────────────────────────────────────────────
   1 │            1000         1000      missing         missing 
   2 │         missing      missing          300             300

julia> journal
2×5 DataFrame
 Row │ date         debit_account  credit_account  debit_amount  credit_amount 
     │ String       String         String          Int64         Int64         
─────┼─────────────────────────────────────────────────────────────────────────
   1 │ Jan 1, 2019  Wages          Chequing                1000           1000
   2 │ Jan 2, 2019  Personal       Rent                     300            300

For the journal, columns are created implicitly when the first row is added. For accounts, columns are added as required every time a row is pushed that uses new accounts. With the keyword argument cols=:union we don’t need to give default values for the row: unspecified values are set to missing.