double_booked 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Jay McAliley
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # DoubleBooked
2
+
3
+ Flexible double-entry accounting engine for Rails apps using ActiveRecord
4
+
5
+ ## Overview
6
+
7
+ The core of `double_booked` are Accounts and Transactions. The concept of an account is probably familiar to most developers-- think of your checking or savings accounts. A transaction links two accounts together, posting a debit to one, and a credit to another. The credits and debits must be equal in amount. That's it!
8
+
9
+ The system also includes invoices, payments, and the ability to mark any eligible transaction as payment for an invoice, as long as the buyer's account is involved in the transaction.
10
+
11
+ ## Installation
12
+
13
+ In your Gemfile, put:
14
+ ```
15
+ source :rubygems
16
+ gem 'double_booked'
17
+ ```
18
+
19
+ Then on the command line:
20
+ ```bash
21
+ bundle install
22
+ rails g double_booked:migrations
23
+ rake db:migrate
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ To use `double_booked`, you'll need to create a model that is a subclass of
29
+ `DetailAccount`. A `DetailAccount` represents an account that is directly
30
+ debited from or credited to, for example a bank account.
31
+
32
+ Let's say your users have a TokenAccount record, and are able to award each other tokens from their accounts. You'd set up the models as such:
33
+
34
+ ```ruby
35
+ class User < ActiveRecord::Base
36
+ has_one :token_account, :as => :owner
37
+ end
38
+
39
+ class TokenAccount < DetailAccount
40
+ owned_by :user
41
+ end
42
+ ```
43
+
44
+ You may want to set up a special user to issue tokens:
45
+
46
+ ```ruby
47
+ # Setup the token issuer and account (the "bank")
48
+ token_issuer = User.create :email => 'token_issuer@myapp.com', ...
49
+ token_bank = token_issuer.create_token_account
50
+
51
+ token_bank.current_balance
52
+ # => 0
53
+
54
+ # Issue 10 tokens to the user Jack
55
+ jack = User.find_by_name "Jack"
56
+ token_bank.transfer(10).to jack.token_account
57
+
58
+ jack.token_account.current_balance
59
+ # => 10
60
+
61
+ token_bank.current_balance
62
+ # => -10
63
+
64
+ # Now have Jack give 2 tokens to Bob
65
+ bob = User.find_by_name("Bob")
66
+
67
+ bob.token_account.current_balance
68
+ # => 0
69
+
70
+ jack.token_account.transfer(10).to bob.token_account
71
+
72
+ jack.token_account.current_balance
73
+ # => 8
74
+
75
+ bob.token_account.current_balance
76
+ # => 2
77
+ ```
78
+
79
+ At any time, the total tokens in all accounts should sum up to zero.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'DoubleBooked'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ begin
24
+ require 'rspec/core/rake_task'
25
+ RSpec::Core::RakeTask.new(:spec)
26
+ task :default => :spec
27
+ rescue LoadError => ls
28
+ puts "rspec failing to load."
29
+ exit(1)
30
+ end
31
+
32
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
33
+ load 'rails/tasks/engine.rake'
34
+
35
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ module DoubleBooked
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module DoubleBooked
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,44 @@
1
+ class Account < ActiveRecord::Base
2
+ has_many :balances
3
+ validate :no_direct_subclass
4
+
5
+ class << self
6
+ attr_accessor :owner_type
7
+ end
8
+
9
+ def self.owned_by(klass)
10
+ @owner_type = klass.to_s.classify.constantize
11
+ belongs_to :owner, :polymorphic => true
12
+ validate :check_owner_type
13
+ end
14
+
15
+ def balance_at(date)
16
+ balance = balances.where(:evaluated_at => date).first
17
+ balance ||= Balance.new(:account => self, :evaluated_at => date)
18
+ end
19
+
20
+ def balance_before(date)
21
+ balances.find :first, :conditions => ["evaluated_at < ?", date],
22
+ :order => "evaluated_at DESC"
23
+ end
24
+
25
+ def current_balance
26
+ balance_at(Time.now)
27
+ end
28
+
29
+ private
30
+ def check_owner_type
31
+ errors.add(:base, "owner must be an #{self.class.owner_type}") if
32
+ self.class.owner_type and !(owner.is_a? self.class.owner_type)
33
+ end
34
+
35
+ def no_direct_subclass
36
+ # FIXME -- refactor Account as a mixin instead of using STI
37
+ # consider using abstract_class for DetailAccount & SummaryAccount
38
+ msg = "Record must not be an Account or a direct subclass of it. " +
39
+ "Subclass the DetailAccount or SummaryAccount class instead."
40
+ direct_subclass = self.class.superclass == Account || self.class == Account
41
+ errors.add :base, msg if direct_subclass
42
+ end
43
+
44
+ end
@@ -0,0 +1,40 @@
1
+ class Balance < ActiveRecord::Base
2
+ belongs_to :account
3
+ validates_presence_of :account, :evaluated_at, :balance
4
+
5
+ def balance
6
+ super or self.balance = calculate_balance
7
+ end
8
+
9
+ def readonly?
10
+ !new_record?
11
+ end
12
+
13
+ private
14
+
15
+ def calculate_balance
16
+ entries.inject(previous_balance) {|balance, entry| balance + entry.amount}
17
+ end
18
+
19
+ def previous
20
+ account.balance_before(evaluated_at)
21
+ end
22
+
23
+ def previous_balance
24
+ previous ? previous.balance : 0
25
+ end
26
+
27
+ def entries
28
+ account.entries.find(:all, :conditions => entry_conditions,
29
+ :joins => :transaction)
30
+ end
31
+
32
+ def entry_conditions
33
+ column = "transactions.created_at"
34
+ if previous
35
+ ["#{column} > ? AND #{column} <= ?", previous.evaluated_at, evaluated_at]
36
+ else
37
+ ["#{column} <= ?", evaluated_at]
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ class BlankTransaction
2
+ attr_accessor :amount, :account_from, :account_to
3
+
4
+ def initialize(amount, account_from)
5
+ @amount = amount
6
+ @account_from = account_from
7
+ end
8
+
9
+ def to(account_to, args = {})
10
+ defaults = {
11
+ :account_from => @account_from,
12
+ :account_to => account_to,
13
+ :amount => @amount
14
+ }
15
+ Transaction.create args.merge(defaults)
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ class Credit < Entry
2
+
3
+ validate :require_debit
4
+ validate :sign_convention
5
+ validate :conservation_principle
6
+ attr_accessible :amount, :detail_account
7
+
8
+ def sign_convention
9
+ errors.add(:amount, "Credit must be non-negative") unless amount >= 0
10
+ end
11
+
12
+ def debit
13
+ transaction ? transaction.debit : nil
14
+ end
15
+
16
+ def require_debit
17
+ errors.add(:base, "Debit must be saved before credit") unless !debit.nil?
18
+ end
19
+
20
+ def conservation_principle
21
+ errors.add(:base, "Credit and debit amounts must add up to zero") unless
22
+ (debit.nil? or amount + debit.amount == 0)
23
+ end
24
+
25
+ end
@@ -0,0 +1,15 @@
1
+ class Debit < Entry
2
+
3
+ validate :sign_convention
4
+ has_one :credit, :through => :transaction
5
+ attr_accessible :amount, :detail_account
6
+
7
+ def sign_convention
8
+ errors.add(:base, "Debit must be non-positive") unless amount <= 0
9
+ end
10
+
11
+ def balanced?
12
+ !credit.nil? and (credit.amount + amount == 0)
13
+ end
14
+
15
+ end
@@ -0,0 +1,11 @@
1
+ class DetailAccount < Account
2
+ has_many :entries
3
+ has_many :transactions, :through => :entries
4
+ has_many :debits
5
+ has_many :credits
6
+
7
+ def transfer(amount)
8
+ BlankTransaction.new amount, self
9
+ end
10
+
11
+ end
@@ -0,0 +1,13 @@
1
+ class Entry < ActiveRecord::Base
2
+
3
+ belongs_to :detail_account
4
+ belongs_to :transaction
5
+ has_one :invoice_line, :as => :line_item
6
+ validates_presence_of :detail_account, :transaction
7
+ validates_numericality_of :amount
8
+
9
+ def readonly?
10
+ !new_record?
11
+ end
12
+
13
+ end
@@ -0,0 +1,82 @@
1
+ class Invoice < ActiveRecord::Base
2
+ belongs_to :buyer_account, :class_name => 'DetailAccount'
3
+ belongs_to :seller_account, :class_name => 'DetailAccount'
4
+ has_many :invoice_payments, :as => :auxilliary_model
5
+ has_many :invoice_lines
6
+ has_many :line_items, :through => :invoice_lines
7
+
8
+ validates_presence_of :buyer_account, :seller_account
9
+
10
+ def self.build(entries)
11
+ accounts = check_accounts entries
12
+ invoice = Invoice.create! :buyer_account => accounts[:buyer],
13
+ :seller_account => accounts[:seller]
14
+ entries.each do |entry|
15
+ line = invoice.invoice_lines.create :line_item => entry
16
+ line.save!
17
+ end
18
+ invoice.close
19
+ invoice
20
+ end
21
+
22
+ def close
23
+ update_attribute(:closed, true)
24
+ end
25
+
26
+ def open?
27
+ !closed?
28
+ end
29
+
30
+ def paid?
31
+ amount_owed <= 0
32
+ end
33
+
34
+ def amount_billed
35
+ line_items.inject(0) {|amount, item| amount + item.amount}
36
+ end
37
+
38
+ def amount_paid
39
+ invoice_payments(true).inject(0) {|amount, payment| amount + payment.amount}
40
+ end
41
+
42
+ def amount_owed
43
+ amount_billed - amount_paid
44
+ end
45
+
46
+ def pay_in_full(options = {})
47
+ pay(amount_owed, options)
48
+ end
49
+
50
+ def pay(amount, options = {})
51
+ options.merge!({:description => "Payment for Invoice ##{formatted_id}",
52
+ :auxilliary_model => self,
53
+ :account_from => buyer_account,
54
+ :account_to => seller_account,
55
+ :amount => amount})
56
+ InvoicePayment.create! options
57
+ end
58
+
59
+ def formatted_id
60
+ "%08i" % id
61
+ end
62
+
63
+ private
64
+ def self.check_accounts(entries)
65
+ accounts, last_accounts = {}, {}
66
+ entries.each do |entry|
67
+ accounts = get_accounts entry
68
+ raise ArgumentError, "All entries must involve the same accounts" unless
69
+ last_accounts.empty? || accounts == last_accounts
70
+ last_accounts = accounts
71
+ end
72
+ accounts
73
+ end
74
+
75
+ def self.get_accounts(entry)
76
+ buyer = entry.detail_account
77
+ seller = entry.transaction.debited_account
78
+ seller = entry.transaction.credited_account if seller == buyer
79
+ {:seller => seller, :buyer => buyer}
80
+ end
81
+
82
+ end
@@ -0,0 +1,26 @@
1
+ class InvoiceLine < ActiveRecord::Base
2
+ belongs_to :invoice
3
+ belongs_to :line_item, :class_name => 'Entry'
4
+ validates_presence_of :invoice, :line_item
5
+ validate :open_invoice
6
+ validate :correct_account
7
+ validates_uniqueness_of :line_item_id
8
+
9
+ def paid?
10
+ invoice.paid?
11
+ end
12
+
13
+ private
14
+
15
+ def open_invoice
16
+ return true unless invoice # another validation will catch
17
+ errors.add(:invoice, "is closed") unless invoice(true).open?
18
+ end
19
+
20
+ def correct_account
21
+ return true unless invoice && line_item # another validation will catch
22
+ errors.add(:base, "Line item is applied to the wrong account") unless
23
+ line_item.detail_account == invoice.buyer_account
24
+ end
25
+
26
+ end
@@ -0,0 +1,15 @@
1
+ class InvoicePayment < Transaction
2
+ validate :valid_invoice
3
+
4
+ alias :invoice :auxilliary_model
5
+
6
+ def pays_in_full?
7
+ amount >= invoice.amount_billed
8
+ end
9
+
10
+ private
11
+ def valid_invoice
12
+ invoice.is_a? Invoice
13
+ end
14
+
15
+ end
@@ -0,0 +1,30 @@
1
+ class Statement
2
+ attr_accessor :account, :period_start, :period_end
3
+
4
+ def initialize(account, period_start, period_end)
5
+ @account = account
6
+ @period_start = period_start
7
+ @period_end = period_end
8
+ end
9
+
10
+ def entries
11
+ account.entries.where(entry_conditions).
12
+ joins(:transaction).
13
+ order("created_at ASC")
14
+ end
15
+
16
+ def start_balance
17
+ account.balance_at(period_start).balance
18
+ end
19
+
20
+ def end_balance
21
+ account.balance_at(period_end).balance
22
+ end
23
+
24
+ private
25
+ def entry_conditions
26
+ column = "transactions.created_at"
27
+ ["#{column} > ? AND #{column} <= ?", period_start, period_end]
28
+ end
29
+
30
+ end
@@ -0,0 +1,11 @@
1
+ class SummaryAccount < Account
2
+ has_and_belongs_to_many :accounts, :join_table => :account_joins
3
+ has_many :entries, :through => :accounts
4
+ validate :no_recursion
5
+
6
+ private
7
+ def no_recursion
8
+ errors.add(:base, "Summary account cannot summarize itself") if
9
+ accounts.include? self
10
+ end
11
+ end
@@ -0,0 +1,64 @@
1
+ class Transaction < ActiveRecord::Base
2
+ has_one :debit
3
+ has_one :credit
4
+ has_one :credited_account, :through => :credit, :source => :detail_account
5
+ has_one :debited_account, :through => :debit, :source => :detail_account
6
+ belongs_to :auxilliary_model, :polymorphic => true
7
+
8
+ validates_presence_of :description, :account_from, :account_to, :amount
9
+ validate :sufficient_funds, :if => :require_funds?
10
+
11
+ attr_accessor :account_from, :account_to, :amount
12
+
13
+ def self.belongs_to_auxilliary_model(model)
14
+ klass = model.to_s.classify
15
+ belongs_to klass.underscore.to_sym, :foreign_key => :auxilliary_model_id
16
+ end
17
+
18
+ def self.required_auxilliary_model(model)
19
+ belongs_to_auxilliary_model(model)
20
+ validates_presence_of model.to_s.classify.underscore.to_sym
21
+ end
22
+
23
+ def completed?
24
+ !debit.nil? and !credit.nil?
25
+ end
26
+
27
+ def amount
28
+ completed? ? credit.amount : (@amount or 0)
29
+ end
30
+
31
+ def account_from
32
+ completed? ? debit.detail_account : @account_from
33
+ end
34
+
35
+ def account_to
36
+ completed? ? credit.detail_account : @account_to
37
+ end
38
+
39
+ def transaction_date
40
+ super or created_at
41
+ end
42
+
43
+ def readonly?
44
+ !new_record?
45
+ end
46
+
47
+ private
48
+ def create
49
+ # Saving of debit, credit, and transaction should be done in one
50
+ # atomic commit (the following block is an *SQL* transaction, not related
51
+ # to our Transaction model)
52
+ transaction do
53
+ super
54
+ create_debit :amount => -amount, :detail_account => account_from
55
+ create_credit :amount => amount, :detail_account => account_to
56
+ end
57
+ completed?
58
+ end
59
+
60
+ def sufficient_funds
61
+ sufficient = account_from && account_from.current_balance.balance >= amount
62
+ errors.add :base, "Insufficient funds in debit account" unless sufficient
63
+ end
64
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>DoubleBooked</title>
5
+ <%= stylesheet_link_tag "double_booked/application", :media => "all" %>
6
+ <%= javascript_include_tag "double_booked/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ # DoubleBooked::Engine.routes.draw do
2
+ # end
@@ -0,0 +1,5 @@
1
+ module DoubleBooked
2
+ class Engine < ::Rails::Engine
3
+
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module DoubleBooked
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "double_booked/engine"
2
+
3
+ module DoubleBooked
4
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ ./script/generate double_booked Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,22 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module DoubleBooked
5
+ class MigrationsGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ # Implement the required interface for Rails::Generators::Migration.
9
+ # taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
10
+ def self.next_migration_number(dirname)
11
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
12
+ end
13
+
14
+ source_root File.expand_path("../templates", __FILE__)
15
+
16
+ desc "Creates migrations."
17
+
18
+ def create_migrations
19
+ migration_template "double_booked.rb.erb", "db/migrate/create_double_booked.rb"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,70 @@
1
+ class CreateDoubleBookedModels < ActiveRecord::Migration
2
+ def self.up
3
+
4
+ create_table :accounts do |t|
5
+ t.string :type
6
+ t.string :name
7
+ t.references :owner, :polymorphic => true
8
+ end
9
+ add_index :accounts, [:owner_id, :owner_type]
10
+
11
+ create_table :account_joins, :id => false do |t|
12
+ t.integer :detail_account_id, :references => :accounts
13
+ t.integer :summary_account_id, :references => :accounts
14
+ end
15
+ add_index :account_joins, :detail_account_id
16
+ add_index :account_joins, :summary_account_id
17
+
18
+ create_table :balances do |t|
19
+ t.integer :account_id
20
+ t.datetime :evaluated_at
21
+ t.decimal :balance, :precision => 14, :scale => 2
22
+ end
23
+ add_index :balances, :account_id
24
+
25
+ create_table :transactions do |t|
26
+ t.string :type
27
+ t.string :description
28
+ t.datetime :transaction_date
29
+ t.references :auxilliary_model, :polymorphic => true
30
+ t.boolean :require_funds
31
+ t.timestamps
32
+ end
33
+ add_index :transactions, :transaction_date
34
+ add_index :transactions, [:auxilliary_model_id, :auxilliary_model_type], :name => :index_transactions_on_auxilliary_model
35
+
36
+ create_table :entries do |t|
37
+ t.string :type
38
+ t.integer :detail_account_id, :references => :accounts
39
+ t.references :transaction
40
+ t.decimal :amount, :precision => 14, :scale => 2
41
+ end
42
+ add_index :entries, :detail_account_id
43
+ add_index :entries, :transaction_id
44
+
45
+ create_table :invoices do |t|
46
+ t.integer :buyer_account_id, :references => :accounts
47
+ t.integer :seller_account_id, :references => :accounts
48
+ t.boolean :closed, :default => false
49
+ end
50
+ add_index :invoices, :buyer_account_id
51
+ add_index :invoices, :seller_account_id
52
+
53
+ create_table :invoice_lines do |t|
54
+ t.references :invoice
55
+ t.integer :line_item_id, :references => :entries
56
+ end
57
+ add_index :invoice_lines, :invoice_id
58
+ add_index :invoice_lines, :line_item_id
59
+ end
60
+
61
+ def self.down
62
+ drop_table :invoice_lines
63
+ drop_table :invoices
64
+ drop_table :entries
65
+ drop_table :transactions
66
+ drop_table :balances
67
+ drop_table :account_joins
68
+ drop_table :accounts
69
+ end
70
+ end
@@ -0,0 +1,8 @@
1
+ namespace :double_booked do
2
+ desc "Cache balances for all DetailAccounts"
3
+ task :update_balances => :environment do
4
+ DetailAccount.all.each do |a|
5
+ a.balance_at(Date.today).save
6
+ end
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: double_booked
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jay McAliley
9
+ - John McAliley
10
+ - Jim Van Fleet
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2013-02-26 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rails
18
+ requirement: &70271980125640 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: 3.0.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *70271980125640
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: &70271980125020 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *70271980125020
38
+ description: Double-entry accounting issues credits and debits, calculates balances,
39
+ allows for summary accounts and more.
40
+ email:
41
+ - jay@logicleague.com
42
+ executables: []
43
+ extensions: []
44
+ extra_rdoc_files: []
45
+ files:
46
+ - app/assets/javascripts/double_booked/application.js
47
+ - app/assets/stylesheets/double_booked/application.css
48
+ - app/controllers/double_booked/application_controller.rb
49
+ - app/helpers/double_booked/application_helper.rb
50
+ - app/models/account.rb
51
+ - app/models/balance.rb
52
+ - app/models/blank_transaction.rb
53
+ - app/models/credit.rb
54
+ - app/models/debit.rb
55
+ - app/models/detail_account.rb
56
+ - app/models/entry.rb
57
+ - app/models/invoice.rb
58
+ - app/models/invoice_line.rb
59
+ - app/models/invoice_payment.rb
60
+ - app/models/statement.rb
61
+ - app/models/summary_account.rb
62
+ - app/models/transaction.rb
63
+ - app/views/layouts/double_booked/application.html.erb
64
+ - config/routes.rb
65
+ - lib/double_booked/engine.rb
66
+ - lib/double_booked/version.rb
67
+ - lib/double_booked.rb
68
+ - lib/generators/double_booked/migrations/migrations_generator.rb
69
+ - lib/generators/double_booked/migrations/templates/double_booked.rb.erb
70
+ - lib/generators/double_booked/migrations/USAGE
71
+ - lib/tasks/double_booked_tasks.rake
72
+ - MIT-LICENSE
73
+ - Rakefile
74
+ - README.md
75
+ homepage: https://github.com/logicleague/double_booked
76
+ licenses: []
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 1.8.10
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Flexible double-entry accounting engine for Rails apps
99
+ test_files: []