plutus 0.11.0 → 0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/README.markdown +66 -40
  3. data/app/assets/javascripts/plutus/application.js +5 -4
  4. data/app/assets/javascripts/plutus/reports.js +5 -0
  5. data/app/assets/stylesheets/bootstrap-theme.min.css +5 -0
  6. data/app/assets/stylesheets/bootstrap.min.css +5 -0
  7. data/app/assets/stylesheets/plutus/application.css +11 -5
  8. data/app/controllers/plutus/accounts_controller.rb +1 -16
  9. data/app/controllers/plutus/application_controller.rb +4 -0
  10. data/app/controllers/plutus/entries_controller.rb +7 -17
  11. data/app/controllers/plutus/reports_controller.rb +40 -0
  12. data/app/models/plutus/account.rb +92 -7
  13. data/app/models/plutus/amount.rb +2 -11
  14. data/app/models/plutus/amounts_extension.rb +31 -6
  15. data/app/models/plutus/asset.rb +21 -18
  16. data/app/models/plutus/entry.rb +15 -3
  17. data/app/models/plutus/equity.rb +22 -19
  18. data/app/models/plutus/expense.rb +21 -18
  19. data/app/models/plutus/liability.rb +27 -19
  20. data/app/models/plutus/revenue.rb +22 -19
  21. data/app/models/plutus/tenancy.rb +5 -1
  22. data/app/views/layouts/plutus/_messages.html.erb +9 -0
  23. data/app/views/layouts/plutus/_navigation.html.erb +19 -0
  24. data/app/views/layouts/plutus/_navigation_links.html.erb +5 -0
  25. data/app/views/layouts/plutus/application.html.erb +19 -0
  26. data/app/views/plutus/accounts/index.html.erb +9 -12
  27. data/app/views/plutus/entries/index.html.erb +22 -11
  28. data/app/views/plutus/reports/_account.html.erb +28 -0
  29. data/app/views/plutus/reports/balance_sheet.html.erb +21 -0
  30. data/app/views/plutus/reports/income_statement.html.erb +24 -0
  31. data/config/routes.rb +6 -3
  32. data/{lib/generators/plutus/templates/migration.rb → db/migrate/20160422010135_create_plutus_tables.rb} +6 -10
  33. data/lib/generators/plutus/add_date_upgrade_generator.rb +11 -0
  34. data/lib/generators/plutus/base_generator.rb +19 -0
  35. data/lib/generators/plutus/plutus_generator.rb +8 -22
  36. data/lib/generators/plutus/templates/add_date_migration.rb +12 -0
  37. data/lib/generators/plutus/templates/tenant_migration.rb +1 -1
  38. data/lib/generators/plutus/templates/update_migration.rb +2 -2
  39. data/lib/generators/plutus/tenancy_generator.rb +2 -17
  40. data/lib/generators/plutus/upgrade_plutus_generator.rb +6 -22
  41. data/lib/plutus.rb +2 -5
  42. data/lib/plutus/engine.rb +5 -0
  43. data/lib/plutus/version.rb +1 -1
  44. data/spec/controllers/accounts_controller_spec.rb +11 -20
  45. data/spec/controllers/entries_controller_spec.rb +11 -20
  46. data/spec/controllers/reports_controller_spec.rb +24 -0
  47. data/spec/factories/account_factory.rb +6 -6
  48. data/spec/factories/amount_factory.rb +3 -3
  49. data/spec/factories/entry_factory.rb +1 -1
  50. data/spec/factories/tenant_factory.rb +12 -0
  51. data/spec/models/account_spec.rb +71 -8
  52. data/spec/models/amount_spec.rb +6 -1
  53. data/spec/models/entry_spec.rb +35 -63
  54. data/spec/models/tenancy_spec.rb +26 -8
  55. data/spec/routing/accounts_routing_spec.rb +6 -25
  56. data/spec/routing/entries_routing_spec.rb +6 -25
  57. data/spec/spec_helper.rb +4 -2
  58. data/spec/support/account_shared_examples.rb +14 -10
  59. data/spec/support/amount_shared_examples.rb +4 -4
  60. data/spec/support/shoulda_matchers.rb +8 -0
  61. metadata +100 -28
  62. data/app/views/plutus/accounts/show.html.erb +0 -59
  63. data/app/views/plutus/entries/show.html.erb +0 -46
  64. data/spec/schema.rb +0 -31
@@ -2,11 +2,6 @@
2
2
  require "rails"
3
3
 
4
4
  module Plutus
5
- class Engine < Rails::Engine
6
- isolate_namespace Plutus
7
- end
8
-
9
-
10
5
  # ------------------------------ tenancy ------------------------------
11
6
  # configuration to enable or disable tenancy
12
7
  mattr_accessor :enable_tenancy
@@ -21,3 +16,5 @@ module Plutus
21
16
  yield(self)
22
17
  end
23
18
  end
19
+
20
+ require "plutus/engine"
@@ -0,0 +1,5 @@
1
+ module Plutus
2
+ class Engine < Rails::Engine
3
+ isolate_namespace Plutus
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module Plutus
2
- VERSION = "0.11.0"
2
+ VERSION = "0.16"
3
3
  end
@@ -2,27 +2,18 @@ require 'spec_helper'
2
2
 
3
3
  module Plutus
4
4
  describe AccountsController do
5
- # Run these tests if you enable routing in your rails app. See README
5
+ routes { Plutus::Engine.routes }
6
6
 
7
- #def mock_account(stubs={})
8
- #@mock_account ||= mock_model(Account, stubs)
9
- #end
10
-
11
- #describe "GET index" do
12
- #it "assigns all accounts as @accounts" do
13
- #Account.stub(:find).with(:all).and_return([mock_account])
14
- #get :index
15
- #assigns[:accounts].should == [mock_account]
16
- #end
17
- #end
18
-
19
- #describe "GET show" do
20
- #it "assigns the requested account as @account" do
21
- #Account.stub(:find).with("37").and_return(mock_account)
22
- #get :show, :id => "37"
23
- #assigns[:account].should equal(mock_account)
24
- #end
25
- #end
7
+ def mock_account(stubs={})
8
+ @mock_account ||= FactoryGirl.create(:asset)
9
+ end
26
10
 
11
+ describe "GET index" do
12
+ it "assigns all accounts as @accounts" do
13
+ allow(Account).to receive(:all).and_return([mock_account])
14
+ get :index
15
+ expect(assigns[:accounts]).to eq([mock_account])
16
+ end
17
+ end
27
18
  end
28
19
  end
@@ -2,27 +2,18 @@ require 'spec_helper'
2
2
 
3
3
  module Plutus
4
4
  describe EntriesController do
5
- # Run these tests if you enable routing in your rails app. See README
5
+ routes { Plutus::Engine.routes }
6
6
 
7
- #def mock_entry(stubs={})
8
- #@mock_entry ||= mock_model(Entry, stubs)
9
- #end
10
-
11
- #describe "GET index" do
12
- #it "assigns all entries as @entries" do
13
- #Entry.stub(:find).with(:all).and_return([mock_entry])
14
- #get :index
15
- #assigns[:entries].should == [mock_entry]
16
- #end
17
- #end
18
-
19
- #describe "GET show" do
20
- #it "assigns the requested entry as @entry" do
21
- #Entry.stub(:find).with("37").and_return(mock_entry)
22
- #get :show, :id => "37"
23
- #assigns[:entry].should equal(mock_entry)
24
- #end
25
- #end
7
+ def mock_entry(stubs={})
8
+ @mock_entry ||= FactoryGirl.create(:entry_with_credit_and_debit)
9
+ end
26
10
 
11
+ describe "GET index" do
12
+ it "assigns all entries as @entries" do
13
+ allow(Entry).to receive_message_chain(:per, :order).and_return([mock_entry])
14
+ get :index
15
+ expect(assigns[:entries]).to eq([mock_entry])
16
+ end
17
+ end
27
18
  end
28
19
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module Plutus
4
+ describe ReportsController do
5
+ routes { Plutus::Engine.routes }
6
+
7
+ def mock_entry(stubs={})
8
+ @mock_entry ||= FactoryGirl.create(:entry_with_credit_and_debit)
9
+ end
10
+
11
+ describe "GET balance_sheet" do
12
+ it "renders when one entry exists" do
13
+ allow(Entry).to receive_message_chain(:order).and_return([mock_entry])
14
+ get :balance_sheet
15
+ assert_template 'reports/balance_sheet'
16
+ end
17
+ it "renders when no entries exist" do
18
+ allow(Entry).to receive_message_chain(:order).and_return([])
19
+ get :balance_sheet
20
+ assert_template 'reports/balance_sheet'
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,32 +1,32 @@
1
1
  FactoryGirl.define do
2
2
  factory :account, :class => Plutus::Account do |account|
3
3
  account.name
4
- account.contra false
4
+ account.contra { false }
5
5
  end
6
6
 
7
7
  factory :asset, :class => Plutus::Asset do |account|
8
8
  account.name
9
- account.contra false
9
+ account.contra { false }
10
10
  end
11
11
 
12
12
  factory :equity, :class => Plutus::Equity do |account|
13
13
  account.name
14
- account.contra false
14
+ account.contra { false }
15
15
  end
16
16
 
17
17
  factory :expense, :class => Plutus::Expense do |account|
18
18
  account.name
19
- account.contra false
19
+ account.contra { false }
20
20
  end
21
21
 
22
22
  factory :liability, :class => Plutus::Liability do |account|
23
23
  account.name
24
- account.contra false
24
+ account.contra { false }
25
25
  end
26
26
 
27
27
  factory :revenue, :class => Plutus::Revenue do |account|
28
28
  account.name
29
- account.contra false
29
+ account.contra { false }
30
30
  end
31
31
 
32
32
  sequence :name do |n|
@@ -1,18 +1,18 @@
1
1
  FactoryGirl.define do
2
2
  factory :amount, :class => Plutus::Amount do |amount|
3
- amount.amount BigDecimal.new('473')
3
+ amount.amount { BigDecimal('473') }
4
4
  amount.association :entry, :factory => :entry_with_credit_and_debit
5
5
  amount.association :account, :factory => :asset
6
6
  end
7
7
 
8
8
  factory :credit_amount, :class => Plutus::CreditAmount do |credit_amount|
9
- credit_amount.amount BigDecimal.new('473')
9
+ credit_amount.amount { BigDecimal('473') }
10
10
  credit_amount.association :entry, :factory => :entry_with_credit_and_debit
11
11
  credit_amount.association :account, :factory => :revenue
12
12
  end
13
13
 
14
14
  factory :debit_amount, :class => Plutus::DebitAmount do |debit_amount|
15
- debit_amount.amount BigDecimal.new('473')
15
+ debit_amount.amount { BigDecimal('473') }
16
16
  debit_amount.association :entry, :factory => :entry_with_credit_and_debit
17
17
  debit_amount.association :account, :factory => :asset
18
18
  end
@@ -1,6 +1,6 @@
1
1
  FactoryGirl.define do
2
2
  factory :entry, :class => Plutus::Entry do |entry|
3
- entry.description 'factory description'
3
+ entry.description { 'factory description' }
4
4
  factory :entry_with_credit_and_debit, :class => Plutus::Entry do |entry_cd|
5
5
  entry_cd.after_build do |t|
6
6
  t.credit_amounts << FactoryGirl.build(:credit_amount, :entry => t)
@@ -0,0 +1,12 @@
1
+ module Plutus
2
+ class Tenant < ActiveRecord::Base
3
+ end
4
+ end
5
+
6
+ FactoryGirl.define do
7
+ factory :tenant, :class => Plutus::Tenant do
8
+ sequence :name do |n|
9
+ "Tenant #{n}"
10
+ end
11
+ end
12
+ end
@@ -5,27 +5,33 @@ module Plutus
5
5
  let(:account) { FactoryGirl.build(:account) }
6
6
  subject { account }
7
7
 
8
- it { should_not be_valid } # must construct a child type instead
8
+ it { is_expected.not_to be_valid } # must construct a child type instead
9
9
 
10
10
  describe "when using a child type" do
11
11
  let(:account) { FactoryGirl.create(:account, type: "Finance::Asset") }
12
- it { should be_valid }
12
+ it { is_expected.to be_valid }
13
13
 
14
14
  it "should be unique per name" do
15
15
  conflict = FactoryGirl.build(:account, name: account.name, type: account.type)
16
- conflict.should_not be_valid
17
- conflict.errors[:name].should == ["has already been taken"]
16
+ expect(conflict).not_to be_valid
17
+ expect(conflict.errors[:name]).to eq(["has already been taken"])
18
18
  end
19
19
  end
20
20
 
21
- it { should_not respond_to(:balance) }
21
+ it "calling the instance method #balance should raise a NoMethodError" do
22
+ expect { subject.balance }.to raise_error NoMethodError, "undefined method 'balance'"
23
+ end
24
+
25
+ it "calling the class method ::balance should raise a NoMethodError" do
26
+ expect { subject.class.balance }.to raise_error NoMethodError, "undefined method 'balance'"
27
+ end
22
28
 
23
29
  describe ".trial_balance" do
24
30
  subject { Account.trial_balance }
25
- it { should be_kind_of BigDecimal }
31
+ it { is_expected.to be_kind_of BigDecimal }
26
32
 
27
33
  context "when given no entries" do
28
- it { should == 0 }
34
+ it { is_expected.to eq(0) }
29
35
  end
30
36
 
31
37
  context "when given correct entries" do
@@ -63,8 +69,65 @@ module Plutus
63
69
  FactoryGirl.create(:entry, :credit_amounts => [ca5], :debit_amounts => [da5])
64
70
  }
65
71
 
66
- it { should == 0 }
72
+ it { is_expected.to eq(0) }
73
+ end
74
+ end
75
+
76
+ describe "#amounts" do
77
+ it "returns all credit and debit amounts" do
78
+ equity = FactoryGirl.create(:equity)
79
+ asset = FactoryGirl.create(:asset)
80
+ expense = FactoryGirl.create(:expense)
81
+
82
+ investment = Entry.new(
83
+ description: "Initial investment",
84
+ date: Date.today,
85
+ debits: [{ account_name: equity.name, amount: 1000 }],
86
+ credits: [{ account_name: asset.name, amount: 1000 }],
87
+ )
88
+ investment.save
89
+
90
+ purchase = Entry.new(
91
+ description: "First computer",
92
+ date: Date.today,
93
+ debits: [{ account_name: asset.name, amount: 900 }],
94
+ credits: [{ account_name: expense.name, amount: 900 }],
95
+ )
96
+ purchase.save
97
+
98
+ expect(equity.amounts.size).to eq 1
99
+ expect(asset.amounts.size).to eq 2
100
+ expect(expense.amounts.size).to eq 1
101
+ end
102
+ end
103
+
104
+ describe "#entries" do
105
+ it "returns all credit and debit entries" do
106
+ equity = FactoryGirl.create(:equity)
107
+ asset = FactoryGirl.create(:asset)
108
+ expense = FactoryGirl.create(:expense)
109
+
110
+ investment = Entry.new(
111
+ description: "Initial investment",
112
+ date: Date.today,
113
+ debits: [{ account_name: equity.name, amount: 1000 }],
114
+ credits: [{ account_name: asset.name, amount: 1000 }],
115
+ )
116
+ investment.save
117
+
118
+ purchase = Entry.new(
119
+ description: "First computer",
120
+ date: Date.today,
121
+ debits: [{ account_name: asset.name, amount: 900 }],
122
+ credits: [{ account_name: expense.name, amount: 900 }],
123
+ )
124
+ purchase.save
125
+
126
+ expect(equity.entries.size).to eq 1
127
+ expect(asset.entries.size).to eq 2
128
+ expect(expense.entries.size).to eq 1
67
129
  end
68
130
  end
131
+
69
132
  end
70
133
  end
@@ -2,7 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  module Plutus
4
4
  describe Amount do
5
+
6
+ describe "attributes" do
7
+ it { is_expected.to delegate_method(:name).to(:account).with_prefix }
8
+ end
9
+
5
10
  subject { FactoryGirl.build(:amount) }
6
- it { should_not be_valid } # construct a child class instead
11
+ it { is_expected.not_to be_valid } # construct a child class instead
7
12
  end
8
13
  end
@@ -5,15 +5,15 @@ module Plutus
5
5
  let(:entry) { FactoryGirl.build(:entry) }
6
6
  subject { entry }
7
7
 
8
- it { should_not be_valid }
8
+ it { is_expected.not_to be_valid }
9
9
 
10
10
  context "with credit and debit" do
11
11
  let(:entry) { FactoryGirl.build(:entry_with_credit_and_debit) }
12
- it { should be_valid }
12
+ it { is_expected.to be_valid }
13
13
 
14
14
  it "should require a description" do
15
15
  entry.description = nil
16
- entry.should_not be_valid
16
+ expect(entry).not_to be_valid
17
17
  end
18
18
  end
19
19
 
@@ -21,13 +21,13 @@ module Plutus
21
21
  before {
22
22
  entry.debit_amounts << FactoryGirl.build(:debit_amount, entry: entry)
23
23
  }
24
- it { should_not be_valid }
24
+ it { is_expected.not_to be_valid }
25
25
 
26
26
  context "with an invalid credit" do
27
27
  before {
28
28
  entry.credit_amounts << FactoryGirl.build(:credit_amount, entry: entry, amount: nil)
29
29
  }
30
- it { should_not be_valid }
30
+ it { is_expected.not_to be_valid }
31
31
  end
32
32
  end
33
33
 
@@ -35,29 +35,47 @@ module Plutus
35
35
  before {
36
36
  entry.credit_amounts << FactoryGirl.build(:credit_amount, entry: entry)
37
37
  }
38
- it { should_not be_valid }
38
+ it { is_expected.not_to be_valid }
39
39
 
40
40
  context "with an invalid debit" do
41
41
  before {
42
42
  entry.debit_amounts << FactoryGirl.build(:debit_amount, entry: entry, amount: nil)
43
43
  }
44
- it { should_not be_valid }
44
+ it { is_expected.not_to be_valid }
45
+ end
46
+ end
47
+
48
+ context "without a date" do
49
+ let(:entry) { FactoryGirl.build(:entry_with_credit_and_debit, date: nil) }
50
+
51
+ context "should assign a default date before being saved" do
52
+ before { entry.save! }
53
+ its(:date) { is_expected.to eq(Time.now.to_date) }
45
54
  end
46
55
  end
47
56
 
48
57
  it "should require the debit and credit amounts to cancel" do
49
58
  entry.credit_amounts << FactoryGirl.build(:credit_amount, :amount => 100, :entry => entry)
50
59
  entry.debit_amounts << FactoryGirl.build(:debit_amount, :amount => 200, :entry => entry)
51
- entry.should_not be_valid
52
- entry.errors['base'].should == ["The credit and debit amounts are not equal"]
60
+ expect(entry).not_to be_valid
61
+ expect(entry.errors['base']).to eq(["The credit and debit amounts are not equal"])
53
62
  end
54
63
 
55
64
  it "should require the debit and credit amounts to cancel even with fractions" do
56
65
  entry = FactoryGirl.build(:entry)
57
66
  entry.credit_amounts << FactoryGirl.build(:credit_amount, :amount => 100.1, :entry => entry)
58
67
  entry.debit_amounts << FactoryGirl.build(:debit_amount, :amount => 100.2, :entry => entry)
59
- entry.should_not be_valid
60
- entry.errors['base'].should == ["The credit and debit amounts are not equal"]
68
+ expect(entry).not_to be_valid
69
+ expect(entry.errors['base']).to eq(["The credit and debit amounts are not equal"])
70
+ end
71
+
72
+ it "should ignore debit and credit amounts marked for destruction to cancel" do
73
+ entry.credit_amounts << FactoryGirl.build(:credit_amount, :amount => 100, :entry => entry)
74
+ debit_amount = FactoryGirl.build(:debit_amount, :amount => 100, :entry => entry)
75
+ debit_amount.mark_for_destruction
76
+ entry.debit_amounts << debit_amount
77
+ expect(entry).not_to be_valid
78
+ expect(entry.errors['base']).to eq(["The credit and debit amounts are not equal"])
61
79
  end
62
80
 
63
81
  it "should have a polymorphic commercial document associations" do
@@ -65,7 +83,7 @@ module Plutus
65
83
  entry = FactoryGirl.build(:entry_with_credit_and_debit, commercial_document: mock_document)
66
84
  entry.save!
67
85
  saved_entry = Entry.find(entry.id)
68
- saved_entry.commercial_document.should == mock_document
86
+ expect(saved_entry.commercial_document).to eq(mock_document)
69
87
  end
70
88
 
71
89
  context "given a set of accounts" do
@@ -75,13 +93,13 @@ module Plutus
75
93
  let!(:sales_tax_payable) { FactoryGirl.create(:liability, name: "Sales Tax Payable") }
76
94
 
77
95
  shared_examples_for 'a built-from-hash Plutus::Entry' do
78
- its(:credit_amounts) { should_not be_empty }
79
- its(:debit_amounts) { should_not be_empty }
80
- it { should be_valid }
96
+ its(:credit_amounts) { is_expected.not_to be_empty }
97
+ its(:debit_amounts) { is_expected.not_to be_empty }
98
+ it { is_expected.to be_valid }
81
99
 
82
100
  context "when saved" do
83
101
  before { entry.save! }
84
- its(:id) { should_not be_nil }
102
+ its(:id) { is_expected.not_to be_nil }
85
103
 
86
104
  context "when reloaded" do
87
105
  let(:saved_transaction) { Entry.find(entry.id) }
@@ -126,31 +144,6 @@ module Plutus
126
144
  }
127
145
  include_examples 'a built-from-hash Plutus::Entry'
128
146
  end
129
-
130
- context "when given a credit/debits hash with :account => String" do
131
- let(:hash) {
132
- {
133
- description: "Sold some widgets",
134
- commercial_document: mock_document,
135
- debits: [{account: accounts_receivable.name, amount: 50}],
136
- credits: [
137
- {account: sales_revenue.name, amount: 45},
138
- {account: sales_tax_payable.name, amount: 5}
139
- ]
140
- }
141
- }
142
-
143
- before { ::ActiveSupport::Deprecation.silenced = true }
144
- after { ::ActiveSupport::Deprecation.silenced = false }
145
-
146
- it("should be deprecated") {
147
- # one deprecation per account looked up
148
- ::ActiveSupport::Deprecation.should_receive(:warn).exactly(3).times
149
- entry
150
- }
151
-
152
- include_examples 'a built-from-hash Plutus::Entry'
153
- end
154
147
  end
155
148
 
156
149
  describe ".build" do
@@ -165,32 +158,11 @@ module Plutus
165
158
 
166
159
  it("should be deprecated") {
167
160
  # .build is the only thing deprecated
168
- ::ActiveSupport::Deprecation.should_receive(:warn).once
161
+ expect(::ActiveSupport::Deprecation).to receive(:warn).once
169
162
  entry
170
163
  }
171
164
  end
172
165
 
173
- context "when given a credit/debits hash with :account => String" do
174
- let(:hash) {
175
- {
176
- description: "Sold some widgets",
177
- commercial_document: mock_document,
178
- debits: [{account: accounts_receivable.name, amount: 50}],
179
- credits: [
180
- {account: sales_revenue.name, amount: 45},
181
- {account: sales_tax_payable.name, amount: 5}
182
- ]
183
- }
184
- }
185
-
186
- it("should be deprecated") {
187
- # one deprecation for build, plus three for accounts as strings
188
- ::ActiveSupport::Deprecation.should_receive(:warn).exactly(4).times
189
- entry
190
- }
191
-
192
- include_examples 'a built-from-hash Plutus::Entry'
193
- end
194
166
  end
195
167
  end
196
168