debitcredit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: be9238d1bb6d86514b17b8a40affb2f5bd66f4a1
4
+ data.tar.gz: 9279fc5c208136740b3b043ad1aa72606b4f539a
5
+ SHA512:
6
+ metadata.gz: f7d490ab14bae8fa8c936f2cfda2b741745be0b63b75e2d0038bf7c403340d9c16d53ee5e5f41534a9425b0f9c9c65b6c44620c49c80c86a4af44d345038cf88
7
+ data.tar.gz: 32bf86b2f2733e799acbfab832bb9b1823d95f6cbf033d1788e0789f21f90d8f7afdba55ad4bd8de9e05fb853814f59303925cf087aab5638fe8d9aab691128b
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
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/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Debitcredit'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/test_app/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rspec/core/rake_task'
25
+
26
+ RSpec::Core::RakeTask.new(:spec)
27
+
28
+ task default: :spec
@@ -0,0 +1,42 @@
1
+ module Debitcredit
2
+ class Account < ActiveRecord::Base
3
+ belongs_to :reference, polymorphic: true
4
+ has_many :items, dependent: :destroy
5
+
6
+ validates :name, :balance, presence: true
7
+
8
+ scope :asset, ->{where(type: AssetAccount.name)}
9
+ scope :equity, ->{where(type: EquityAccount.name)}
10
+ scope :liability, ->{where(type: LiabilityAccount.name)}
11
+ scope :expense, ->{where(type: ExpenseAccount.name)}
12
+ scope :income, ->{where(type: IncomeAccount.name)}
13
+
14
+ scope :by_id, ->{order(:id)}
15
+
16
+ class << self
17
+ def [](name)
18
+ find_by!(name: name)
19
+ end
20
+
21
+ def total_balance
22
+ + asset.sum(:balance) \
23
+ + expense.sum(:balance) \
24
+ - liability.sum(:balance) \
25
+ - equity.sum(:balance) \
26
+ - income.sum(:balance)
27
+ end
28
+
29
+ def balanced?
30
+ transaction do
31
+ by_id.lock.first
32
+ 0 == total_balance
33
+ end
34
+ end
35
+ end
36
+
37
+ def update_balance!(item)
38
+ item.balance = send(item.kind, item.amount)
39
+ save!
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ module Debitcredit
2
+ class AssetAccount < DebitAccount
3
+ end
4
+ end
@@ -0,0 +1,11 @@
1
+ module Debitcredit
2
+ class CreditAccount < Account
3
+ def debit(amount)
4
+ self.balance -= amount
5
+ end
6
+
7
+ def credit(amount)
8
+ self.balance += amount
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Debitcredit
2
+ class DebitAccount < Account
3
+ def debit(amount)
4
+ self.balance += amount
5
+ end
6
+
7
+ def credit(amount)
8
+ self.balance -= amount
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ module Debitcredit
2
+ class EquityAccount < CreditAccount
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Debitcredit
2
+ class ExpenseAccount < DebitAccount
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Debitcredit
2
+ class IncomeAccount < CreditAccount
3
+ end
4
+ end
@@ -0,0 +1,24 @@
1
+ module Debitcredit
2
+ class Item < ActiveRecord::Base
3
+ belongs_to :transaction
4
+ belongs_to :account
5
+
6
+ validate :transaction, :account, presence: true
7
+ validate :amount, numericality: true, greater_than_or_equal_to: 0
8
+
9
+ scope :debit, ->{where(debit: true)}
10
+ scope :credit, ->{where(debit: false)}
11
+
12
+ def credit?
13
+ !debit?
14
+ end
15
+
16
+ def value_for_balance
17
+ credit?? amount : -amount
18
+ end
19
+
20
+ def kind
21
+ debit?? :debit : :credit
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ module Debitcredit
2
+ class LiabilityAccount < CreditAccount
3
+ end
4
+ end
@@ -0,0 +1,21 @@
1
+ class Debitcredit::Transaction::DSL
2
+ attr_accessor :transaction
3
+ attr_accessor :accounts
4
+ def initialize(transaction)
5
+ @transaction = transaction
6
+ end
7
+
8
+ def debit(account, amount, comment = nil)
9
+ build_item true, account, amount, comment
10
+ end
11
+
12
+ def credit(account, amount, comment = nil)
13
+ build_item false, account, amount, comment
14
+ end
15
+
16
+ private
17
+ def build_item(debit, account, amount, comment)
18
+ account = transaction.reference.accounts[account] if account.is_a?(Symbol) && transaction.reference.try(:respond_to?, :accounts)
19
+ transaction.items.build debit: debit, account: account, amount: amount, comment: comment
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ require 'docile'
2
+ module Debitcredit
3
+ class Transaction < ActiveRecord::Base
4
+ belongs_to :reference, polymorphic: true
5
+ has_many :items, dependent: :destroy, autosave: true
6
+
7
+ validates :reference, :description, presence: true
8
+ validate :ensure_balanced
9
+
10
+ before_create :lock_and_update_balances
11
+
12
+ # XXX
13
+ # prevent items and balance changes on update
14
+ # prevent update at all?
15
+
16
+ def self.prepare(opts = {}, &block)
17
+ new(opts).tap do |t|
18
+ Docile.dsl_eval(DSL.new(t), &block)
19
+ end
20
+ end
21
+
22
+ def items_balance
23
+ items.map(&:value_for_balance).sum
24
+ end
25
+
26
+ def balanced?
27
+ 0 == items_balance
28
+ end
29
+
30
+ protected
31
+ def ensure_balanced
32
+ errors.add(:base, :unbalanced) unless balanced?
33
+ end
34
+
35
+ def lock_and_update_balances
36
+ items.sort_by(&:account_id).each do |item|
37
+ item.account.lock!.update_balance!(item)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,8 @@
1
+ ---
2
+ en:
3
+ activerecord:
4
+ errors:
5
+ models:
6
+ debitcredit/transaction:
7
+ unbalanced: item amounts must balance to 0
8
+
@@ -0,0 +1,14 @@
1
+ class CreateDebitcreditAccounts < ActiveRecord::Migration
2
+ def change
3
+ create_table :debitcredit_accounts do |t|
4
+ t.string :name, null: false, limit: 16
5
+ t.string :type, null: false, limit: 32
6
+ t.integer :reference_id, null: true
7
+ t.string :reference_type, null: true, limit: 32
8
+ t.decimal :balance, null: false, precision: 20, scale: 2, default: 0
9
+
10
+ t.timestamps
11
+ end
12
+ add_index :debitcredit_accounts, [:name, :reference_id, :reference_type], unique: true, name: :uindex
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class CreateDebitcreditTransactions < ActiveRecord::Migration
2
+ def change
3
+ create_table :debitcredit_transactions do |t|
4
+ t.integer :reference_id, null: true
5
+ t.string :reference_type, null: true, limit: 32
6
+ t.string :kind, null: true
7
+ t.string :description, null: false
8
+
9
+ t.timestamps
10
+ end
11
+ add_index :debitcredit_transactions, [:reference_id, :reference_type, :id], name: :rindex
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ class CreateDebitcreditItems < ActiveRecord::Migration
2
+ def change
3
+ create_table :debitcredit_items do |t|
4
+ t.references :transaction, null: false
5
+ t.references :account, null: false
6
+ t.boolean :debit, null: false
7
+ t.string :comment, null: true
8
+ t.decimal :amount, null: false, precision: 20, scale: 2, default: 0
9
+ t.decimal :balance, null: false, precision: 20, scale: 2, default: 0
10
+ t.timestamps
11
+ end
12
+ add_index :debitcredit_items, :account_id
13
+ add_index :debitcredit_items, :transaction_id
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ module Debitcredit
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Debitcredit
4
+ config.generators do |g|
5
+ g.test_framework :rspec
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Debitcredit
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,5 @@
1
+ require "debitcredit/engine"
2
+
3
+ module Debitcredit
4
+ end
5
+ I18n.load_path += Dir.glob(File.expand_path("../../config/locales/*.{rb,yml}", __FILE__))
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :debitcredit do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,232 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: debitcredit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Vitaly Kushner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: docile
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: spork
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: spork-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-bundler
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: guard-spork
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: guard-rspec
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: pg
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ description: Double entry accounting for Rails applications
182
+ email:
183
+ - vitaly@astrails.com
184
+ executables: []
185
+ extensions: []
186
+ extra_rdoc_files: []
187
+ files:
188
+ - app/models/debitcredit/account.rb
189
+ - app/models/debitcredit/asset_account.rb
190
+ - app/models/debitcredit/credit_account.rb
191
+ - app/models/debitcredit/debit_account.rb
192
+ - app/models/debitcredit/equity_account.rb
193
+ - app/models/debitcredit/expense_account.rb
194
+ - app/models/debitcredit/income_account.rb
195
+ - app/models/debitcredit/item.rb
196
+ - app/models/debitcredit/liability_account.rb
197
+ - app/models/debitcredit/transaction/dsl.rb
198
+ - app/models/debitcredit/transaction.rb
199
+ - config/locales/en.yml
200
+ - db/migrate/20140121145455_create_debitcredit_accounts.rb
201
+ - db/migrate/20140121181304_create_debitcredit_transactions.rb
202
+ - db/migrate/20140121181326_create_debitcredit_items.rb
203
+ - lib/debitcredit/engine.rb
204
+ - lib/debitcredit/version.rb
205
+ - lib/debitcredit.rb
206
+ - lib/tasks/debitcredit_tasks.rake
207
+ - MIT-LICENSE
208
+ - Rakefile
209
+ homepage: http://github.com/astrails/debitcredit
210
+ licenses: []
211
+ metadata: {}
212
+ post_install_message:
213
+ rdoc_options: []
214
+ require_paths:
215
+ - lib
216
+ required_ruby_version: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - '>='
219
+ - !ruby/object:Gem::Version
220
+ version: '0'
221
+ required_rubygems_version: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - '>='
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ requirements: []
227
+ rubyforge_project:
228
+ rubygems_version: 2.0.6
229
+ signing_key:
230
+ specification_version: 4
231
+ summary: Double entry accounting for Rails applications
232
+ test_files: []