aloe 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|