acts_as_account 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.specification ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_account
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors: []
7
+
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-03-18 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email:
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib
26
+ - lib/acts_as_account
27
+ - lib/acts_as_account.rb
28
+ has_rdoc: true
29
+ homepage:
30
+ licenses: []
31
+
32
+ post_install_message:
33
+ rdoc_options: []
34
+
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: "0"
42
+ version:
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ requirements: []
50
+
51
+ rubyforge_project:
52
+ rubygems_version: 1.3.5
53
+ signing_key:
54
+ specification_version: 3
55
+ summary:
56
+ test_files: []
57
+
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 gut.org gAG
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.rdoc ADDED
@@ -0,0 +1,31 @@
1
+ acts_as_account implements double entry accounting for Rails models. Your models get accounts and you can do consistent transactions between them. Since the documentation is sparse, see the transfer.feature for usage examples.
2
+
3
+ == Theory
4
+
5
+ Acts as account hooks into ActiveRecord and allows to add accounts to any model by
6
+ simply adding "has_account" to your model. Because the accounts
7
+ are connected via a has_many relation no migration to the account-hoder
8
+ tables is needed.
9
+
10
+ We also hook into the ActionController request cycle to warn the developer
11
+ if a Request has left the uncommitted changes in the system.
12
+
13
+ If you would like to run the cucumber features from the acs_as_account gem, just execute
14
+ * rake features:create_database
15
+ * cucumber
16
+
17
+ == Links
18
+
19
+ * Double Entry Accounting in a Relational Database: http://homepages.tcp.co.uk/~m-wigley/gc_wp_ded.html
20
+
21
+ == Todo
22
+
23
+ * add transaction isolation tests
24
+
25
+ == Credits
26
+
27
+ This gem was written for the payment backend of betterplace.org by Thies C. Arntzen (http://github.com/thieso2) and Norman Timmler (http://github.com/unnu).
28
+
29
+ == Copyright
30
+
31
+ Copyright (c) 2010 gut.org gAG
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "acts_as_account"
8
+ gem.summary = %Q{acts_as_account implements double entry accounting for Rails models}
9
+ gem.description = %Q{acts_as_account implements double entry accounting for Rails models. Your models get accounts and you can do consistent transactions between them. Since the documentation is sparse, see the transfer.feature for usage examples.}
10
+ gem.email = "thieso@gmail.com"
11
+ gem.homepage = "http://github.com/thieso2/acts_as_account"
12
+ gem.authors = ["Thies C. Arntzen, Norman Timmler, Matthias Frick, Phillip Oertel"]
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
17
+ end
18
+
19
+ namespace :features do
20
+ desc "create test database out of db/schema.rb"
21
+ task :create_database do
22
+ require 'rubygems'
23
+ require 'active_record'
24
+ access_data = YAML.load_file(File.dirname(__FILE__) + '/db/database.yml')['acts_as_account']
25
+ conn = ActiveRecord::Base.establish_connection(Hash[access_data.select { |k, v| k != 'database'}]).connection
26
+ conn.execute('DROP DATABASE IF EXISTS acts_as_account')
27
+ conn.execute('CREATE DATABASE acts_as_account')
28
+ conn.execute('USE acts_as_account')
29
+ load(File.dirname(__FILE__) + '/db/schema.rb')
30
+ end
31
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ default: --require features/support/env.rb
data/db/database.yml ADDED
@@ -0,0 +1,15 @@
1
+ acts_as_account:
2
+ adapter: mysql
3
+ encoding: utf8
4
+ database: acts_as_account
5
+ username: root
6
+ password:
7
+ host: localhost
8
+
9
+ betterplace:
10
+ adapter: mysql
11
+ encoding: utf8
12
+ database: betterplace_production20100222
13
+ username: root
14
+ password:
15
+ host: localhost
data/db/schema.rb ADDED
@@ -0,0 +1,65 @@
1
+ ActiveRecord::Schema.define(:version => 1) do
2
+ if ENV['DROP_FK']
3
+ execute "ALTER TABLE acts_as_account_postings DROP FOREIGN KEY account_id"
4
+ execute "ALTER TABLE acts_as_account_postings DROP FOREIGN KEY other_account_id"
5
+ execute "ALTER TABLE acts_as_account_postings DROP FOREIGN KEY journal_id"
6
+ end
7
+
8
+ create_table "acts_as_account_accounts", :force => true do |t|
9
+ t.integer "holder_id", :null => false
10
+ t.string "holder_type", :null => false
11
+ t.string "name", :null => false
12
+
13
+ t.integer "balance", :default => 0
14
+ t.integer "postings_count", :default => 0
15
+ t.datetime "last_valuta"
16
+
17
+ t.datetime "created_at"
18
+ t.datetime "updated_at"
19
+ end
20
+ add_index "acts_as_account_accounts", ["holder_id", "holder_type", "name"], :name => 'account_unique', :unique => true
21
+
22
+ create_table "acts_as_account_journals", :force => true do |t|
23
+ t.datetime "created_at"
24
+ t.datetime "updated_at"
25
+ end
26
+
27
+ create_table "acts_as_account_postings", :force => true do |t|
28
+ t.integer "account_id", :null => false
29
+ t.integer "other_account_id", :null => false
30
+ t.integer "journal_id", :null => false
31
+ t.integer "amount", :null => false
32
+
33
+ t.integer "reference_id"
34
+ t.string "reference_type"
35
+
36
+ t.datetime "valuta"
37
+
38
+ t.datetime "created_at"
39
+ t.datetime "updated_at"
40
+ end
41
+ add_index "acts_as_account_postings", "account_id"
42
+ add_index "acts_as_account_postings", "journal_id"
43
+ add_index "acts_as_account_postings", ["reference_type", "reference_id"], :name => "reference"
44
+ add_index "acts_as_account_postings", ["valuta", "id"], :name => "sort_key"
45
+
46
+ execute "ALTER TABLE acts_as_account_postings ADD CONSTRAINT account_id FOREIGN KEY (account_id) REFERENCES acts_as_account_accounts (id)"
47
+ execute "ALTER TABLE acts_as_account_postings ADD CONSTRAINT other_account_id FOREIGN KEY (other_account_id) REFERENCES acts_as_account_accounts (id)"
48
+ execute "ALTER TABLE acts_as_account_postings ADD CONSTRAINT journal_id FOREIGN KEY (journal_id) REFERENCES acts_as_account_journals (id)"
49
+
50
+ create_table "acts_as_account_global_accounts", :force => true do |t|
51
+ t.string "name", :null => false
52
+ end
53
+
54
+ create_table "users", :force => true do |t|
55
+ t.string "name", :null => false
56
+ t.datetime "created_at"
57
+ t.datetime "updated_at"
58
+ end
59
+
60
+ create_table "cheques", :force => true do |t|
61
+ t.string "number"
62
+ t.datetime "created_at"
63
+ t.datetime "updated_at"
64
+ end
65
+ end
data/env.sh ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.8.6-p399@rails235
@@ -0,0 +1,11 @@
1
+ Feature: Creating an Account
2
+ Background: I have a User
3
+ Given I create a user A
4
+
5
+ Scenario: Every Holder should have a default Account
6
+ Then User A has an Account named default
7
+
8
+ Scenario: Creating a second Accounts with the same name for a Holder returns original account
9
+ Given User A has an Account named default
10
+ And I create an Account named default for User A
11
+ Then I get the original account
@@ -0,0 +1,125 @@
1
+ include ActsAsAccount
2
+
3
+ def german_date_time_to_local(datestring, timestring)
4
+ Time.local(*(datestring.split(".").reverse + timestring.split(":")).map(&:to_i))
5
+ end
6
+
7
+ Given /^I create a user (\w+)$/ do |name|
8
+ User.create!(:name => name)
9
+ end
10
+
11
+ Given /^I create a global ([_\w]+) account$/ do |name|
12
+ Account.for(name)
13
+ end
14
+
15
+ Then /^an account for user (\w+) exists$/ do |name|
16
+ @account = User.find_by_name(name).account
17
+ assert_not_nil @account
18
+ end
19
+
20
+ Then /^the account has (\d+) journals?$/ do |num_journals|
21
+ num_journals = num_journals.to_i
22
+
23
+ if num_journals == 1
24
+ @journal = @account.journals.first
25
+ else
26
+ @journals = @account.journals
27
+ end
28
+
29
+ assert_equal num_journals, @account.journals.count
30
+ end
31
+
32
+ Then /^the journal has (\d+) postings? with an amount of (\d+) €$/ do |num_postings, amount|
33
+ @postings = @journal.postings
34
+ assert_equal num_postings.to_i, @postings.size
35
+ @postings.each do |posting|
36
+ assert_equal amount.to_i, posting.amount
37
+ end
38
+ end
39
+
40
+ Then /^(\w+)'s account balance is (-?\d+) €$/ do |name, balance|
41
+ assert_equal balance.to_i, User.find_by_name(name).account.balance
42
+ end
43
+
44
+ Then /^the global (\w+) account balance is (-?\d+) €$/ do |name, balance|
45
+ assert_equal balance.to_i, Account.for(name).balance
46
+ end
47
+
48
+ When /^I transfer (\d+) € from (\w+)'s account to (\w+)'s account$/ do |amount, from, to|
49
+ from_account = User.find_by_name(from).account
50
+ to_account = User.find_by_name(to).account
51
+ Journal.current.transfer(amount.to_i, from_account, to_account, @reference, @valuta)
52
+ end
53
+
54
+ When /^I transfer (\d+) € from global (\w+) account to global (\w+) account$/ do |amount, from, to|
55
+ from_account = Account.for(from)
56
+ to_account = Account.for(to)
57
+ Journal.current.transfer(amount.to_i, from_account, to_account, @reference, @valuta)
58
+ end
59
+
60
+ Then /^the balance\-sheet should be:$/ do |table|
61
+ table.hashes.each do |row|
62
+ assert_equal row['Balance'].to_i, User.find_by_name(row['User']).account.balance
63
+ end
64
+ end
65
+
66
+ Then /^I fail with (.+)$/ do |exception|
67
+ assert_equal exception.constantize, @last_exception.class
68
+ end
69
+
70
+ When /^I create a Journal via (.+)$/ do |method|
71
+ @last_exception = nil
72
+ begin
73
+ eval <<-EOT
74
+ @journal = Journal.#{method}
75
+ EOT
76
+ rescue Exception => @last_exception
77
+ end
78
+ end
79
+
80
+ Then /^I have a Journal$/ do
81
+ assert_equal Journal, @journal.class
82
+ end
83
+
84
+ Then /^User (\w+) has an Account named (\w+)$/ do |user_name, account_name|
85
+ @account = User.find_by_name(user_name).account
86
+ assert_equal account_name, @account.name
87
+ end
88
+
89
+ Given /^I create an Account named (\w+) for User (\w+)$/ do |account_name, user_name|
90
+ user = User.find_by_name(user_name)
91
+ @created_account = Account.create!(:holder => user, :name => account_name)
92
+ end
93
+
94
+ Then /^I get the original account$/ do
95
+ assert_equal @account, @created_account
96
+ end
97
+
98
+ Given /I transfer (\d+) € from (\w+)'s account to (\w+)'s account referencing a (\w+) with (\w+) (\w+)$/ do |amount, from, to, reference, name, value|
99
+ @reference = reference.constantize.create!(name => value)
100
+ Given "I transfer #{amount} € from #{from}'s account to #{to}'s account"
101
+ end
102
+
103
+ Then /^all postings reference (\w+) with (\w+) (\w+)$/ do |reference_class, name, value|
104
+ reference = reference_class.constantize.find(:first, :conditions => "#{name} = #{value}")
105
+ Posting.all.each do |posting|
106
+ assert_equal reference, posting.reference
107
+ end
108
+ end
109
+
110
+ Given /^I transfer (\d+) € from (\w+)'s account to (\w+)'s account and specify (\S+) (\S+) as the booking time$/ do |amount, from, to, booking_date, booking_time|
111
+ @valuta = german_date_time_to_local(booking_date, booking_time)
112
+ Given "I transfer #{amount} € from #{from}'s account to #{to}'s account"
113
+ end
114
+
115
+ Then /^all postings have (\S+) (\S+) as the booking time$/ do |booking_date, booking_time|
116
+ valuta = german_date_time_to_local(booking_date, booking_time)
117
+ Posting.all.each do |posting|
118
+ assert_equal valuta, posting.valuta
119
+ end
120
+ end
121
+
122
+ Then /^(\w+) with (\w+) (\w+) references all postings$/ do |reference_class, name, value|
123
+ reference = reference_class.constantize.find(:first, :conditions => "#{name} = #{value}")
124
+ assert_equal Posting.all, reference.postings
125
+ end
@@ -0,0 +1,3 @@
1
+ class Cheque < ActiveRecord::Base
2
+ has_postings
3
+ end
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+
3
+ gem 'mysql'
4
+ gem 'activerecord'
5
+ gem 'actionpack'
6
+ require 'active_record'
7
+
8
+ require 'test/unit/assertions'
9
+
10
+ World(Test::Unit::Assertions)
11
+
12
+ ActiveRecord::Base.establish_connection(YAML.load_file(File.dirname(__FILE__) + '/../../db/database.yml')['acts_as_account'])
13
+
14
+ require File.dirname(__FILE__) + '/../../lib/acts_as_account'
15
+
16
+ #ActiveRecord::Base.logger = Logger.new(STDOUT)
17
+
18
+ require 'database_cleaner'
19
+ require 'database_cleaner/cucumber'
20
+ DatabaseCleaner.strategy = :transaction
21
+
22
+ Dir[File.dirname(__FILE__) + '/../step_definitions/*.rb'].each { |file| require file }
23
+
24
+ require File.dirname(__FILE__) + '/user'
25
+ require File.dirname(__FILE__) + '/cheque'
26
+
27
+ After do
28
+ ActsAsAccount::Journal.clear_current
29
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ has_account
3
+ end
@@ -0,0 +1,13 @@
1
+ Feature: Creating a journal
2
+
3
+ Scenario: Creating a Journal via new or create is not possible
4
+ When I create a Journal via new
5
+ Then I fail with NoMethodError
6
+ When I create a Journal via create
7
+ Then I fail with NoMethodError
8
+ When I create a Journal via create!
9
+ Then I fail with NoMethodError
10
+
11
+ Scenario: Creating a Journal via current
12
+ When I create a Journal via current
13
+ Then I have a Journal
@@ -0,0 +1,35 @@
1
+ Feature: Transfer
2
+ In order to transfer money from one account to another
3
+ As a Bank
4
+ I want not to loose money
5
+
6
+ Scenario: I transfer money between accounts having holders
7
+ Given I create a user Thies
8
+ Given I create a user Norman
9
+ When I transfer 30 € from Thies's account to Norman's account
10
+ Then Thies's account balance is -30 €
11
+ And Norman's account balance is 30 €
12
+
13
+ Scenario: I transfer money between global accounts
14
+ Given I create a global wirecard account
15
+ Given I create a global anonymous_donation account
16
+ When I transfer 30 € from global wirecard account to global anonymous_donation account
17
+ Then the global wirecard account balance is -30 €
18
+ And the global anonymous_donation account balance is 30 €
19
+
20
+ Scenario: I transfer money between accounts having a domain object
21
+ Given I create a user Thies
22
+ Given I create a user Norman
23
+ When I transfer 50 € from Thies's account to Norman's account referencing a Cheque with number 8723
24
+ Then Thies's account balance is -50 €
25
+ And Norman's account balance is 50 €
26
+ And all postings reference Cheque with number 8723
27
+ And Cheque with number 8723 references all postings
28
+
29
+ Scenario: I transfer money between accounts setting the booking time
30
+ Given I create a user Thies
31
+ Given I create a user Norman
32
+ When I transfer 50 € from Thies's account to Norman's account and specify 22.05.1968 07:45 as the booking time
33
+ Then Thies's account balance is -50 €
34
+ And Norman's account balance is 50 €
35
+ And all postings have 22.05.1968 07:45 as the booking time
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'acts_as_account'
@@ -0,0 +1,16 @@
1
+ require 'active_record'
2
+ require 'action_controller'
3
+
4
+ $: << File.expand_path(File.dirname(__FILE__))
5
+
6
+ require 'acts_as_account/transfer'
7
+ require 'acts_as_account/account'
8
+ require 'acts_as_account/journal'
9
+ require 'acts_as_account/posting'
10
+ require 'acts_as_account/active_record_extensions'
11
+
12
+ ActiveRecord::Base.class_eval do
13
+ include ActsAsAccount::ActiveRecordExtension
14
+ end
15
+
16
+ require 'acts_as_account/global_account'
@@ -0,0 +1,49 @@
1
+ module ActsAsAccount
2
+ class Account < ActiveRecord::Base
3
+ set_table_name :acts_as_account_accounts
4
+
5
+ belongs_to :holder, :polymorphic => true
6
+ has_many :postings
7
+ has_many :journals, :through => :postings
8
+
9
+ # TODO: discuss with norman:
10
+ # validates_presence_of will force an ActiveRecord::find on the object
11
+ # but we have to create accounts for deleted holder!
12
+ #
13
+ # validates_presence_of :holder
14
+
15
+ class << self
16
+ def for(name)
17
+ GlobalAccount.find_or_create_by_name(name.to_s).account
18
+ end
19
+
20
+ def create!(attributes = nil)
21
+ find_on_error(attributes) do
22
+ super
23
+ end
24
+ end
25
+
26
+ def create(attributes = nil)
27
+ find_on_error(attributes) do
28
+ super
29
+ end
30
+ end
31
+
32
+ def find_on_error(attributes)
33
+ yield
34
+
35
+ # Trying to create a duplicate key on a unique index raises StatementInvalid
36
+ rescue ActiveRecord::StatementInvalid => e
37
+ record = if attributes[:holder]
38
+ attributes[:holder].account(attributes[:name])
39
+ else
40
+ find(:first, :conditions => [
41
+ "holder_type = ? and holder_id = ? and name = ?",
42
+ attributes[:holder_type], attributes[:holder_id], attributes[:name]
43
+ ])
44
+ end
45
+ record || raise
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ module ActsAsAccount
2
+ module ActiveRecordExtension
3
+ def self.included(base) # :nodoc:
4
+ base.extend ClassMethods
5
+ base.class_eval do
6
+ def account(name = :default)
7
+ __send__("#{name}_account") || __send__("create_#{name}_account", :name => name.to_s)
8
+ end
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def has_account(name = :default)
15
+ has_one "#{name}_account", :conditions => "name = '#{name}'", :class_name => "ActsAsAccount::Account", :as => :holder
16
+ unless instance_methods.include?('accounts')
17
+ has_many :accounts, :class_name => "ActsAsAccount::Account", :as => :holder
18
+ end
19
+ end
20
+
21
+ def has_postings
22
+ has_many :postings, :class_name => "ActsAsAccount::Posting", :as => :reference
23
+ end
24
+
25
+ def has_global_account(name)
26
+ class_eval <<-EOS
27
+ def account
28
+ ActsAsAccount::Account.for(:#{name})
29
+ end
30
+ EOS
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,7 @@
1
+ module ActsAsAccount
2
+ class GlobalAccount < ActiveRecord::Base
3
+ set_table_name :acts_as_account_global_accounts
4
+
5
+ has_account
6
+ end
7
+ end
@@ -0,0 +1,52 @@
1
+ module ActsAsAccount
2
+ class Journal < ActiveRecord::Base
3
+ set_table_name :acts_as_account_journals
4
+
5
+ has_many :postings
6
+ has_many :accounts, :through => :postings
7
+
8
+ class << self
9
+ private :new
10
+ private :create
11
+ private :create!
12
+
13
+ def current
14
+ Thread.current[:acts_as_account_current] ||= create!
15
+ end
16
+
17
+ def clear_current
18
+ Thread.current[:acts_as_account_current] = nil
19
+ end
20
+ end
21
+
22
+ def transfer(amount, from_account, to_account, reference = nil, valuta = Time.now)
23
+ transaction do
24
+ logger.debug { "ActsAsAccount::Journal.transfer amount: #{amount} from:#{from_account.id} to:#{to_account.id} reference:#{reference.class.name}(#{reference.id}) valuta:#{valuta}" } if logger
25
+
26
+ # to avoid possible deadlocks we need to ensure that the locking order is always
27
+ # the same therfore the sort by id.
28
+ [from_account, to_account].sort_by(&:id).map(&:lock!)
29
+
30
+ add_posting(amount * -1, from_account, to_account, reference, valuta)
31
+ add_posting(amount, to_account, from_account, reference, valuta)
32
+ end
33
+ end
34
+
35
+ private
36
+ def add_posting(amount, account, other_account, reference, valuta)
37
+ posting = postings.build(
38
+ :amount => amount,
39
+ :account => account,
40
+ :other_account => other_account,
41
+ :reference => reference,
42
+ :valuta => valuta)
43
+
44
+ account.balance += posting.amount
45
+ account.postings_count += 1
46
+ account.last_valuta = account.last_valuta ? [account.last_valuta, posting.valuta].max : posting.valuta
47
+
48
+ posting.save_without_validation
49
+ account.save_without_validation
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,10 @@
1
+ module ActsAsAccount
2
+ class Posting < ActiveRecord::Base
3
+ set_table_name :acts_as_account_postings
4
+
5
+ belongs_to :account
6
+ belongs_to :other_account, :class_name => 'Account'
7
+ belongs_to :journal
8
+ belongs_to :reference, :polymorphic => true
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ module ActsAsAccount
2
+ class Transfer
3
+ attr_accessor :amount, :reference, :from, :to, :journal
4
+
5
+ def initialize(posting_1, posting_2)
6
+ @amount, @reference = posting_2.amount, posting_2.reference
7
+ @from, @to = posting_1.account, posting_2.account
8
+ @journal = posting_1.journal
9
+ end
10
+
11
+ def referencing_a?(klasse)
12
+ reference.kind_of?(klasse)
13
+ end
14
+
15
+ def reverse(reference = @reference, valuta = Time.now)
16
+ @journal.transfer(
17
+ @amount,
18
+ @to,
19
+ @from,
20
+ reference,
21
+ valuta)
22
+ end
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_account
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 0
10
+ version: 1.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Thies C. Arntzen, Norman Timmler, Matthias Frick, Phillip Oertel
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-12 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: acts_as_account implements double entry accounting for Rails models. Your models get accounts and you can do consistent transactions between them. Since the documentation is sparse, see the transfer.feature for usage examples.
23
+ email: thieso@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ files:
31
+ - .specification
32
+ - MIT-LICENSE
33
+ - README.rdoc
34
+ - Rakefile
35
+ - VERSION
36
+ - cucumber.yml
37
+ - db/database.yml
38
+ - db/schema.rb
39
+ - env.sh
40
+ - features/account/account_creation.feature
41
+ - features/step_definitions/account_steps.rb
42
+ - features/support/cheque.rb
43
+ - features/support/env.rb
44
+ - features/support/user.rb
45
+ - features/transfer/journal_creation.feature
46
+ - features/transfer/transfer.feature
47
+ - init.rb
48
+ - lib/acts_as_account.rb
49
+ - lib/acts_as_account/account.rb
50
+ - lib/acts_as_account/active_record_extensions.rb
51
+ - lib/acts_as_account/global_account.rb
52
+ - lib/acts_as_account/journal.rb
53
+ - lib/acts_as_account/posting.rb
54
+ - lib/acts_as_account/transfer.rb
55
+ has_rdoc: true
56
+ homepage: http://github.com/thieso2/acts_as_account
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --charset=UTF-8
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 3
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.3.7
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: acts_as_account implements double entry accounting for Rails models
89
+ test_files: []
90
+