plutus 0.11.0 → 0.16

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.
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