aloe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/Rakefile +26 -0
- data/lib/aloe.rb +14 -0
- data/lib/aloe/account.rb +176 -0
- data/lib/aloe/account_configuration.rb +21 -0
- data/lib/aloe/account_repository.rb +45 -0
- data/lib/aloe/engine.rb +6 -0
- data/lib/aloe/entry.rb +59 -0
- data/lib/aloe/inoperable_account_error.rb +11 -0
- data/lib/aloe/insufficient_balance_error.rb +14 -0
- data/lib/aloe/invalid_amount_error.rb +3 -0
- data/lib/aloe/invalid_currency_error.rb +16 -0
- data/lib/aloe/ledger.rb +83 -0
- data/lib/aloe/ledger_entry.rb +83 -0
- data/lib/aloe/reports/account_history.rb +32 -0
- data/lib/aloe/reports/accounts_list.rb +39 -0
- data/lib/aloe/transaction.rb +80 -0
- data/lib/aloe/transaction_rollback.rb +34 -0
- data/lib/aloe/version.rb +3 -0
- data/lib/generators/aloe/aloe_generator.rb +17 -0
- data/lib/generators/aloe/templates/migration.rb +44 -0
- data/lib/tasks/aloe_tasks.rake +24 -0
- data/spec/aloe/account_configuration_spec.rb +21 -0
- data/spec/aloe/account_integration_spec.rb +144 -0
- data/spec/aloe/account_spec.rb +189 -0
- data/spec/aloe/accounting_integration_spec.rb +172 -0
- data/spec/aloe/entry_spec.rb +43 -0
- data/spec/aloe/ledger_entry_spec.rb +105 -0
- data/spec/aloe/ledger_spec.rb +98 -0
- data/spec/aloe/transaction_rollback_spec.rb +49 -0
- data/spec/aloe/transaction_spec.rb +59 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20131003200954_create_users.rb +8 -0
- data/spec/dummy/db/migrate/20131003203647_create_aloe_tables.rb +43 -0
- data/spec/dummy/db/schema.rb +63 -0
- data/spec/dummy/log/test.log +3308 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/test/fixtures/users.yml +11 -0
- data/spec/dummy/test/models/user_test.rb +7 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/account_helpers.rb +17 -0
- data/spec/support/user.rb +1 -0
- metadata +278 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'aloe/invalid_currency_error'
|
2
|
+
require 'aloe/invalid_amount_error'
|
3
|
+
require 'aloe/insufficient_balance_error'
|
4
|
+
require 'aloe/inoperable_account_error'
|
5
|
+
|
6
|
+
module Aloe
|
7
|
+
# LedgerEntry is use-case class that encompasses the process of moving funds
|
8
|
+
# between accounts.
|
9
|
+
class LedgerEntry < Struct.new(:amount, :options)
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
raise Aloe::InvalidAmountError if amount.zero?
|
14
|
+
unless same_currencies?
|
15
|
+
raise Aloe::InvalidCurrencyError.new(debit_account, credit_account,
|
16
|
+
amount.currency)
|
17
|
+
end
|
18
|
+
unless debit_account.open?
|
19
|
+
raise Aloe::InoperableAccountError.new(debit_account)
|
20
|
+
end
|
21
|
+
unless credit_account.open?
|
22
|
+
raise Aloe::InoperableAccountError.new(credit_account)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def create!
|
27
|
+
ActiveRecord::Base.transaction do
|
28
|
+
debit_entry = debit_account.create_entry(-amount.cents)
|
29
|
+
credit_entry = credit_account.create_entry(amount.cents)
|
30
|
+
attributes = { credit_entry: credit_entry,
|
31
|
+
debit_entry: debit_entry,
|
32
|
+
category: category }.merge options
|
33
|
+
Aloe::Transaction::create! attributes
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def category
|
40
|
+
@category ||= options.delete(:type)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Aloe::Account]
|
44
|
+
def credit_account
|
45
|
+
@credit_account ||= may_find_account(options.delete(:to), currency)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [String]
|
49
|
+
def currency
|
50
|
+
@currency ||= amount.currency
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Aloe::Account]
|
54
|
+
def debit_account
|
55
|
+
@debit_account ||= may_find_account(options.delete(:from), currency)
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Class]
|
59
|
+
def ledger
|
60
|
+
Aloe::Ledger
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param [Object, Aloe::Aloe] account_or_owner
|
64
|
+
# @param [String, Symbol] currency
|
65
|
+
# @return [Aloe::Account]
|
66
|
+
def may_find_account(account_or_owner, currency)
|
67
|
+
if account_or_owner.respond_to?(:create_entry)
|
68
|
+
account_or_owner
|
69
|
+
elsif account_or_owner
|
70
|
+
ledger.find_account account_or_owner, currency
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Are the two accounts of the matching currency as the given amount?
|
75
|
+
#
|
76
|
+
# @return [true, false]
|
77
|
+
def same_currencies?
|
78
|
+
debit_account.currency?(amount.currency) &&
|
79
|
+
credit_account.currency?(amount.currency)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Aloe
|
2
|
+
module Reports
|
3
|
+
class AccountHistory
|
4
|
+
|
5
|
+
attr_reader :account
|
6
|
+
|
7
|
+
def initialize(account)
|
8
|
+
@account = account
|
9
|
+
end
|
10
|
+
|
11
|
+
def header
|
12
|
+
['Transaction', 'Credit', 'Debit', 'Amount', 'Time']
|
13
|
+
end
|
14
|
+
|
15
|
+
def body
|
16
|
+
account.entries.map do |e|
|
17
|
+
tr = e.transaction
|
18
|
+
[tr.category,
|
19
|
+
tr.credit_entry.account.to_s,
|
20
|
+
tr.debit_entry.account.to_s,
|
21
|
+
e.amount.format,
|
22
|
+
e.created_at.to_s]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def footer
|
27
|
+
['', '', '', account.balance.format, '']
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Aloe
|
2
|
+
module Reports
|
3
|
+
class AccountsList
|
4
|
+
|
5
|
+
attr_reader :currency
|
6
|
+
|
7
|
+
def initialize(currency = Money.default_currency.to_s)
|
8
|
+
@currency = currency
|
9
|
+
end
|
10
|
+
|
11
|
+
def header
|
12
|
+
['Type', 'Id', 'Name', 'Owner', 'Currency', 'Balance']
|
13
|
+
end
|
14
|
+
|
15
|
+
def body
|
16
|
+
accounts.map do |a|
|
17
|
+
type = a.name? ? 'System' : 'Entity'
|
18
|
+
[type, a.id, a.name, a.owner_type_and_id, a.currency, a.balance.format]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def footer
|
23
|
+
['', '', '', '', '', trial_balance.format]
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def accounts
|
29
|
+
@accounts ||= Aloe::Account.currency(@currency).all
|
30
|
+
end
|
31
|
+
|
32
|
+
def trial_balance
|
33
|
+
amount = Aloe::Account.trial_balance(@currency)
|
34
|
+
Money.new(amount, @currency)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'aloe/entry'
|
2
|
+
require 'aloe/transaction_rollback'
|
3
|
+
require 'active_record'
|
4
|
+
require 'uuid'
|
5
|
+
|
6
|
+
module Aloe
|
7
|
+
class Transaction < ActiveRecord::Base
|
8
|
+
|
9
|
+
# Associations
|
10
|
+
|
11
|
+
belongs_to :credit_entry, class_name: "Aloe::Entry"
|
12
|
+
has_one :credit_account, through: :credit_entry, source: :account
|
13
|
+
belongs_to :debit_entry, class_name: "Aloe::Entry"
|
14
|
+
has_one :debit_account, through: :debit_entry, source: :account
|
15
|
+
belongs_to :adjustment_transaction, class_name: "Transaction"
|
16
|
+
has_one :adjusted_transaction, class_name: "Transaction",
|
17
|
+
foreign_key: "adjustment_transaction_id"
|
18
|
+
|
19
|
+
# Validations
|
20
|
+
|
21
|
+
validates_presence_of :credit_entry
|
22
|
+
validates_presence_of :debit_entry
|
23
|
+
validates_presence_of :category
|
24
|
+
|
25
|
+
# Callbacks
|
26
|
+
|
27
|
+
before_create :assign_uuid
|
28
|
+
|
29
|
+
# Instance methods
|
30
|
+
|
31
|
+
serialize :details
|
32
|
+
|
33
|
+
# Return transaction details hash.
|
34
|
+
#
|
35
|
+
# @return [Hash]
|
36
|
+
def details
|
37
|
+
attributes["details"] ||= {}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return entries of transaction.
|
41
|
+
#
|
42
|
+
# @return [Array<Aloe::Entry>]
|
43
|
+
def entries
|
44
|
+
[debit_entry, credit_entry]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return the type of transaction.
|
48
|
+
#
|
49
|
+
# Type of transaction is stored in +category+ attribute
|
50
|
+
# internally because AR uses +type+ for STI.
|
51
|
+
#
|
52
|
+
# @return [Fixnum]
|
53
|
+
def type
|
54
|
+
category
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the amount of transaction.
|
58
|
+
#
|
59
|
+
# @return [Money]
|
60
|
+
def amount
|
61
|
+
credit_entry.amount.abs
|
62
|
+
end
|
63
|
+
|
64
|
+
# Rollback transaction by creating balancing entries.
|
65
|
+
def rollback
|
66
|
+
Aloe::TransactionRollback.new(self).rollback!
|
67
|
+
end
|
68
|
+
|
69
|
+
def number
|
70
|
+
uuid.first(8)
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
def assign_uuid
|
76
|
+
write_attribute :uuid, UUID.new.generate(:compact)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Aloe
|
2
|
+
# Use case class for rolling back a transaction.
|
3
|
+
class TransactionRollback < Struct.new(:transaction)
|
4
|
+
|
5
|
+
def rollback!
|
6
|
+
ActiveRecord::Base.transaction do
|
7
|
+
e1 = credit_entry.account.create_entry debit_entry.amount.cents
|
8
|
+
e2 = debit_entry.account.create_entry credit_entry.amount.cents
|
9
|
+
rollback = Aloe::Transaction.create! credit_entry: e2,
|
10
|
+
debit_entry: e1,
|
11
|
+
category: Aloe::ROLLBACK_TRANSACTION
|
12
|
+
transaction.update_attribute :adjustment_transaction, rollback
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Return credit entry of the transaction.
|
19
|
+
#
|
20
|
+
# @return [Aloe::Entry]
|
21
|
+
def credit_entry
|
22
|
+
transaction.credit_entry
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return debit entry of the transaction.
|
26
|
+
#
|
27
|
+
# @return [Aloe::Entry]
|
28
|
+
def debit_entry
|
29
|
+
transaction.debit_entry
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/lib/aloe/version.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
class AloeGenerator < Rails::Generators::Base
|
5
|
+
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
def self.next_migration_number(path)
|
10
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_migration_file
|
14
|
+
migration_template 'migration.rb', 'db/migrate/create_aloe_tables.rb'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class CreateAloeTables < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
create_table :aloe_accounts do |t|
|
5
|
+
t.string :name
|
6
|
+
t.string :currency, limit: 3
|
7
|
+
t.string :state
|
8
|
+
t.text :configuration
|
9
|
+
t.column :balance, :bigint, default: 0
|
10
|
+
t.references :owner, polymorphic: true
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index :aloe_accounts, [:owner_id, :owner_type]
|
15
|
+
add_index :aloe_accounts, :status
|
16
|
+
|
17
|
+
create_table :aloe_entries do |t|
|
18
|
+
t.column :amount, :bigint
|
19
|
+
t.references :account
|
20
|
+
t.timestamps
|
21
|
+
end
|
22
|
+
|
23
|
+
add_index :aloe_entries, :account_id
|
24
|
+
|
25
|
+
create_table :aloe_transactions do |t|
|
26
|
+
t.string :uuid
|
27
|
+
t.string :category
|
28
|
+
t.string :code
|
29
|
+
t.text :description
|
30
|
+
t.text :details
|
31
|
+
t.references :credit_entry
|
32
|
+
t.references :debit_entry
|
33
|
+
t.references :adjustment_transaction
|
34
|
+
t.timestamps
|
35
|
+
end
|
36
|
+
|
37
|
+
add_index :aloe_transactions, :uuid
|
38
|
+
add_index :aloe_transactions, :credit_entry_id
|
39
|
+
add_index :aloe_transactions, :debit_entry_id
|
40
|
+
add_index :aloe_transactions, :adjustment_transaction_id
|
41
|
+
add_index :aloe_transactions, :category
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'terminal-table'
|
2
|
+
|
3
|
+
task :aloe do
|
4
|
+
|
5
|
+
desc "Show overview of accounts and their balances"
|
6
|
+
task :list_accounts => :environment do
|
7
|
+
currency = ENV["CURRENCY"].presence || Money.default_currency.to_s
|
8
|
+
report = Aloe::Reports::AccountsList.new currency
|
9
|
+
rows = report.body + [:separator] + [report.footer]
|
10
|
+
table = Terminal::Table.new headings: report.header, rows: rows
|
11
|
+
table.align_column(5, :right)
|
12
|
+
puts table
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Show history of account"
|
16
|
+
task :account_history => :environment do
|
17
|
+
account = Aloe::Account.find ENV["ACCOUNT_ID"]
|
18
|
+
report = Aloe::Reports::AccountHistory.new(account)
|
19
|
+
rows = report.body + [:separator] + [report.footer]
|
20
|
+
table = Terminal::Table.new headings: report.header, rows: rows
|
21
|
+
puts table
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Aloe::AccountConfiguration do
|
6
|
+
describe "#allow_negative_balance" do
|
7
|
+
it "is true by default" do
|
8
|
+
subject.allow_negative_balance.should be_true
|
9
|
+
end
|
10
|
+
|
11
|
+
it "return true if underlying variable is true" do
|
12
|
+
subject.allow_negative_balance = true
|
13
|
+
subject.allow_negative_balance.should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "return false if underlying variable is false" do
|
17
|
+
subject.allow_negative_balance = false
|
18
|
+
subject.allow_negative_balance.should be_false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'timecop'
|
5
|
+
|
6
|
+
describe 'Account integration spec', integration: true do
|
7
|
+
let(:owner) { User.create! }
|
8
|
+
let(:owner2) { User.create! }
|
9
|
+
|
10
|
+
describe 'default scope' do
|
11
|
+
before do
|
12
|
+
closed = create_account owner2, :GBP
|
13
|
+
closed.close!
|
14
|
+
@account = create_account owner, :GBP
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'excludes closed account' do
|
18
|
+
Aloe::Account.all.should eq [@account]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#closed scope' do
|
23
|
+
before do
|
24
|
+
@closed = create_account owner2, :GBP
|
25
|
+
@closed.close!
|
26
|
+
account = create_account owner, :GBP
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'includes closed accounts only' do
|
30
|
+
Aloe::Account.closed.should eq [@closed]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'default account state' do
|
35
|
+
let(:account) { Aloe::Account.new }
|
36
|
+
|
37
|
+
it 'is open' do
|
38
|
+
account.state.should eq 'open'
|
39
|
+
account.should be_open
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'closing account' do
|
44
|
+
context 'an account with zero balance' do
|
45
|
+
let(:account) { create_account owner, :GBP }
|
46
|
+
|
47
|
+
it 'can be safely closed' do
|
48
|
+
account.close!
|
49
|
+
account.should be_closed
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'an account with non-zero balance' do
|
54
|
+
let(:account) { create_account owner, :GBP }
|
55
|
+
|
56
|
+
before do
|
57
|
+
account.update_column :balance, 123
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'cannot be closed' do
|
61
|
+
lambda do
|
62
|
+
account.close!
|
63
|
+
end.should raise_error(StateMachine::InvalidTransition)
|
64
|
+
account.should_not be_closed
|
65
|
+
account.should be_open
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'opening closed account' do
|
71
|
+
let(:account) { create_account owner, :GBP }
|
72
|
+
|
73
|
+
before do
|
74
|
+
account.close!
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'can be safely closed' do
|
78
|
+
account.reopen!
|
79
|
+
account.should be_open
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'suspending open account' do
|
84
|
+
let(:account) { create_account owner, :GBP }
|
85
|
+
|
86
|
+
it 'can be safely closed' do
|
87
|
+
account.suspend!
|
88
|
+
account.should be_suspended
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'unsuspending suspended account' do
|
93
|
+
let(:account) { create_account owner, :GBP }
|
94
|
+
|
95
|
+
before do
|
96
|
+
account.suspend!
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'can be safely closed' do
|
100
|
+
account.unsuspend!
|
101
|
+
account.should be_open
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '.turnover' do
|
106
|
+
let(:account) { create_account owner, :GBP }
|
107
|
+
let(:amounts1) { [765, 927, 123, -1239, 212, -232] }
|
108
|
+
let(:amounts2) { [23, 123, 145, 7864, -7231] }
|
109
|
+
|
110
|
+
before do
|
111
|
+
account.update_column :created_at, 30.days.ago
|
112
|
+
Timecop.travel(30.days.ago) do
|
113
|
+
amounts1.each { |amount| account.create_entry(amount) }
|
114
|
+
end
|
115
|
+
Timecop.travel(20.days.ago) do
|
116
|
+
amounts2.each { |amount| account.create_entry(amount) }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'returns turnover in given period' do
|
121
|
+
account.turnover(21.days.ago..Time.now).should eq Money.new(amounts2.sum, :GBP)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '.balance_at' do
|
126
|
+
let(:account) { create_account owner, :GBP }
|
127
|
+
let(:amounts1) { [765, 927, 123, -1239, 212, -232] }
|
128
|
+
let(:amounts2) { [23, 123, 145, 7864, -7231] }
|
129
|
+
|
130
|
+
before do
|
131
|
+
account.update_column :created_at, 30.days.ago
|
132
|
+
Timecop.travel(30.days.ago) do
|
133
|
+
amounts1.each { |amount| account.create_entry(amount) }
|
134
|
+
end
|
135
|
+
Timecop.travel(20.days.ago) do
|
136
|
+
amounts2.each { |amount| account.create_entry(amount) }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'returns turnover in given period' do
|
141
|
+
account.balance_at(29.days.ago).should eq Money.new(amounts1.sum, :GBP)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|