double_double

Build Status Dependency Status Code Climate

A double-entry accrual accounting system for your application. Currency-agnostic but uses the Money gem. Account holder support and contexts are built-in.

Installation

Gem

Add this line to your application's Gemfile:

gem 'double_double'

And then execute:

$ bundle

Or install it yourself as:

$ gem install double_double

Database structure

Create the expected database structure. If using Rails, generate a migration:

$ rails generate migration CreateDoubleDouble

Edit the migration to match:

class CreateDoubleDouble < ActiveRecord::Migration
  def change
    create_table :double_double_accounts do |t|
      t.integer :number,        null: false
      t.string  :name,          null: false
      t.string  :type,          null: false
      t.boolean :contra,        default: false
    end
    add_index :double_double_accounts, [:name, :type]

    create_table :double_double_transactions do |t|
      t.string :description
      t.references :initiator,        polymorphic: true
      t.references :transaction_type
      t.timestamps
    end
    add_index :double_double_transactions, :initiator_id
    add_index :double_double_transactions, :initiator_type
    add_index :double_double_transactions, :transaction_type_id

    create_table :double_double_transaction_types do |t|
      t.string :description,    null: false
    end
    add_index :double_double_transaction_types, :description

    create_table :double_double_amounts do |t|
      t.string :type
      t.references :account
      t.references :transaction
      t.references :context,    polymorphic: true
      t.references :subcontext, polymorphic: true
      t.references :accountee,  polymorphic: true

      t.integer :amount_cents, limit: 8, default: 0, null: false
      t.string  :currency
    end
    add_index :double_double_amounts, :context_id
    add_index :double_double_amounts, :context_type
    add_index :double_double_amounts, :subcontext_id
    add_index :double_double_amounts, :subcontext_type
    add_index :double_double_amounts, :accountee_id
    add_index :double_double_amounts, :accountee_type
    add_index :double_double_amounts, :type
    add_index :double_double_amounts, [:account_id, :transaction_id]
    add_index :double_double_amounts, [:transaction_id, :account_id]
  end
end

Rake the new migration

$ rake db:migrate

Overview

Double-entry accounting practices have been traced back to the 13th century. double_double strives to make accepted practices accessible and relatively easy to implement within other applications.

As with many off-the-shelf accounting systems, this project supports:

  • Accountee: an account holder.
  • Context: to track activity on invoices, purchase orders, jobs, campaigns, etc.
  • Initiator: who authorized or performed the action.

Accounts

All accounts created in a double-entry system make up the chart of accounts. This collection of accounts will determine how money is tracked as it moves through the system. It is important to design and create the chart of accounts prior to creating transactions. *If we want people to hold "an individual account" in this system, we will configure them as an accountee, not with a new account. See the section on accountees *

In double_double, all accounts created are considered to be the chart of accounts. All accounts are "on the books."

Account Class Normal Balance Description Example Uses
DoubleDouble::Asset Debit Resources owned or controlled Cash, Office Computers, Grandma's Jewelry
DoubleDouble::Liability Credit Obligations Accounts Payable, Bank Loan
DoubleDouble::Equity Credit The claim to assets after all liabilities are paid Paid-In Capital, Dividends, Retained Earnings
DoubleDouble::Revenue Credit Income Sales, Interest Earned
DoubleDouble::Expense Debit Expenditures Utilities, Salaries, Rent, Taco Tuesday

Accounts have the following attributes:

  • name
  • number, for reporting purposes
  • contra flag, optional default: false

An example 'Cash' asset account as account number 20

DoubleDouble::Asset.create! name: 'Cash', number: 20

An example 'Sales' revenue account and a 'Discounts' contra revenue account.

DoubleDouble::Revenue.create! name: 'Sales',     number: 40
DoubleDouble::Revenue.create! name: 'Discounts', number: 50, contra: true

Contra accounts are used to offset a related account of the same class. The example above is a common method to track sales. The full sales value of the sale would be assigned to 'Sales' while any discounts given would be assigned to 'Discounts.'

Amounts & Transactions

  • TODO: Transaction basics
  • TODO: Amount basics
  • TODO: Accountees
  • TODO: Contexts
  • TODO: Initiated_by

Example Scenarios

Basic Scenarios

Basic Scenario: We are creating a personal application to only track loan payments back to Grandpa.

We've decided to keep things very simple and only create a few accounts:

  • 'Cash' an asset account.
  • 'Grandpa Loan' a liability account.
  • 'Spending' an expense account
DoubleDouble::Asset.create! name:'Cash', number: 11
DoubleDouble::Liability.create! name:'Grandpa Loan', number: 12
DoubleDouble::Expense.create! name:'Spending', number: 13

Grandpa was kind enough to loan us $800 USD in cash for college textbooks. To enter this we will require a transaction which will affect both 'Cash' and 'Grandpa Loan'

DoubleDouble::Transaction.create!(
  description: 
    'We received a loan from Grandpa',
  debits:[
    {account: 'Cash', amount: '$800'}],
  credits:[
    {account: 'Grandpa Loan', amount: '$800'}])

We buy our college textbooks.

DoubleDouble::Transaction.create!(
  description: 
    'Purchase textbooks from bookstore',
  debits:[
    {account: 'Spending', amount: '$480'}],
  credits:[
    {account: 'Cash', amount: '$480'}])

How much cash is left?

DoubleDouble::Account.named('Cash').balance.to_s           # => "320.00"

We deceided that we wanted to return $320 of the loan.

DoubleDouble::Transaction.create!(
  description: 
    'Payed back $320 to Grandpa',
  debits:[
    {account: 'Grandpa Loan', amount: '$320'}],
  credits:[
    {account: 'Cash', amount: '$320'}])

How much do we still owe Grandpa?

DoubleDouble::Account.named('Grandpa Loan').balance.to_s   # => "480.00"

How much did we spend?

DoubleDouble::Account.named('Spending').balance.to_s       # => "480.00"

How much cash do we have left?

DoubleDouble::Account.named('Cash').balance.to_s           # => "0.00"

Realistic Scenarios

  • TODO: Write a realistic scenario

Complex Scenarios

  • TODO: Write a realistic & complex scenario

Tests

All code is backed by Rspec tests. Clone this repository and either rspec spec or rake spec if Rake is installed.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes & tests (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Notes

double_double was influenced by mbulat's plutus project and regularly working with quickbooks.