uomi 0.2.13

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 (45) hide show
  1. data/.gitignore +6 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +10 -0
  5. data/README.md +35 -0
  6. data/Rakefile +8 -0
  7. data/config.ru +7 -0
  8. data/invoicing.gemspec +27 -0
  9. data/lib/generators/active_record/invoicing_generator.rb +30 -0
  10. data/lib/generators/active_record/templates/migration.rb +84 -0
  11. data/lib/invoicing/buyer.rb +10 -0
  12. data/lib/invoicing/credit_note.rb +70 -0
  13. data/lib/invoicing/credit_note_credit_transaction.rb +6 -0
  14. data/lib/invoicing/credit_note_invoice.rb +6 -0
  15. data/lib/invoicing/credit_transaction.rb +9 -0
  16. data/lib/invoicing/debit_transaction.rb +9 -0
  17. data/lib/invoicing/exception.rb +4 -0
  18. data/lib/invoicing/invoice.rb +233 -0
  19. data/lib/invoicing/invoice_adjustment.rb +59 -0
  20. data/lib/invoicing/invoice_decorator.rb +6 -0
  21. data/lib/invoicing/invoiceable.rb +22 -0
  22. data/lib/invoicing/late_payment.rb +11 -0
  23. data/lib/invoicing/line_item.rb +17 -0
  24. data/lib/invoicing/line_item_type.rb +6 -0
  25. data/lib/invoicing/overdue_invoice.rb +16 -0
  26. data/lib/invoicing/payment_reference.rb +10 -0
  27. data/lib/invoicing/seller.rb +10 -0
  28. data/lib/invoicing/transaction.rb +13 -0
  29. data/lib/invoicing/version.rb +3 -0
  30. data/lib/invoicing.rb +46 -0
  31. data/spec/README +0 -0
  32. data/spec/internal/config/database.yml.sample +3 -0
  33. data/spec/internal/config/routes.rb +3 -0
  34. data/spec/internal/db/schema.rb +81 -0
  35. data/spec/internal/log/.gitignore +1 -0
  36. data/spec/internal/public/favicon.ico +0 -0
  37. data/spec/lib/invoicing/credit_note_spec.rb +140 -0
  38. data/spec/lib/invoicing/invoice_adjustment_spec.rb +136 -0
  39. data/spec/lib/invoicing/invoice_late_payment_spec.rb +10 -0
  40. data/spec/lib/invoicing/invoice_spec.rb +419 -0
  41. data/spec/lib/invoicing/line_item_spec.rb +14 -0
  42. data/spec/lib/invoicing/overdue_invoice_spec.rb +67 -0
  43. data/spec/spec_helper.rb +20 -0
  44. data/spec/support/helpers.rb +20 -0
  45. metadata +185 -0
@@ -0,0 +1,17 @@
1
+ module Invoicing
2
+ class LineItem < ActiveRecord::Base
3
+ belongs_to :invoice
4
+ belongs_to :invoiceable, polymorphic: true
5
+ belongs_to :line_item_type
6
+
7
+ validates :amount, numericality: {greater_than_or_equal_to: 0}, presence: true
8
+
9
+ def net_amount
10
+ amount - tax
11
+ end
12
+
13
+ def self.for(item)
14
+ where(id: item.id).first
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ module Invoicing
2
+ class LineItemType < ActiveRecord::Base
3
+ has_many :line_items
4
+
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ module Invoicing
2
+ class OverdueInvoice
3
+
4
+ def self.all
5
+ Invoice.owing.select{|invoice| invoice.overdue?}
6
+ end
7
+
8
+ def self.record_late_payments!
9
+ OverdueInvoice.all.each do |invoice|
10
+ invoice.late_payment = LatePayment.new amount: invoice.balance.abs
11
+ invoice.save!
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ module Invoicing
2
+
3
+ class PaymentReference < ActiveRecord::Base
4
+
5
+ belongs_to :invoice
6
+
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,10 @@
1
+ module Invoicing
2
+ class Seller < ActiveRecord::Base
3
+ has_many :invoices
4
+ belongs_to :sellerable, polymorphic: true
5
+
6
+ def self.for(sellerable)
7
+ Seller.where(sellerable_type: sellerable.class.name, sellerable_id: sellerable.id).first || Seller.create!(sellerable_type: sellerable.class.name, sellerable_id: sellerable.id)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module Invoicing
2
+ class Transaction < ActiveRecord::Base
3
+ belongs_to :invoice
4
+
5
+ def debit?
6
+ false
7
+ end
8
+
9
+ def credit?
10
+ false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Invoicing
2
+ VERSION = "0.2.13"
3
+ end
data/lib/invoicing.rb ADDED
@@ -0,0 +1,46 @@
1
+ require "active_record"
2
+ require "workflow"
3
+ require "invoicing/version"
4
+
5
+ module Invoicing
6
+
7
+ def self.table_name_prefix
8
+ 'invoicing_'
9
+ end
10
+
11
+ def self.generate(&block)
12
+ invoice = Invoice.new
13
+ invoice.instance_eval(&block)
14
+ invoice.save!
15
+ invoice
16
+ end
17
+
18
+ def self.generate_credit_note(&block)
19
+ credit_note = CreditNote.new
20
+ credit_note.instance_eval(&block)
21
+ credit_note.save!
22
+ credit_note.issue!
23
+ credit_note
24
+ end
25
+
26
+ end
27
+
28
+ require "invoicing/exception"
29
+ require "invoicing/invoiceable"
30
+ require "invoicing/transaction"
31
+ require "invoicing/credit_transaction"
32
+ require "invoicing/debit_transaction"
33
+ require "invoicing/late_payment"
34
+ require "invoicing/line_item"
35
+ require "invoicing/payment_reference"
36
+ require "invoicing/seller"
37
+ require "invoicing/buyer"
38
+ require "invoicing/invoice_decorator"
39
+ require "invoicing/credit_note_invoice"
40
+ require "invoicing/credit_note_credit_transaction"
41
+
42
+ require "invoicing/invoice"
43
+ require "invoicing/credit_note"
44
+ require "invoicing/overdue_invoice"
45
+ require "invoicing/invoice_adjustment"
46
+ require "invoicing/line_item_type"
data/spec/README ADDED
File without changes
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/invoicing_test.sqlite
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ #
3
+ end
@@ -0,0 +1,81 @@
1
+ ActiveRecord::Schema.define do
2
+
3
+ create_table "invoicing_late_payments", :force => true do |t|
4
+ t.integer "invoice_id"
5
+ t.integer "amount"
6
+ t.datetime "penalty_date"
7
+ t.boolean "processed"
8
+ t.timestamps
9
+ end
10
+
11
+ create_table "invoicing_line_items", :force => true do |t|
12
+ t.integer "invoice_id"
13
+ t.string "description"
14
+ t.integer "amount"
15
+ t.integer "tax"
16
+ t.integer "invoiceable_id"
17
+ t.string "invoiceable_type"
18
+ t.integer "line_item_type_id"
19
+ t.timestamps
20
+ end
21
+
22
+ create_table "invoicing_transactions", :force => true do |t|
23
+ t.integer "invoice_id"
24
+ t.string "type"
25
+ t.integer "amount"
26
+ t.timestamps
27
+ end
28
+
29
+ create_table "invoicing_invoices", :force => true do |t|
30
+ t.integer "seller_id"
31
+ t.integer "buyer_id"
32
+ t.string "invoice_number"
33
+ t.datetime "due_date"
34
+ t.datetime "issued_at"
35
+ t.integer "total"
36
+ t.integer "tax"
37
+ t.integer "balance"
38
+ t.string "type"
39
+ t.string "workflow_state"
40
+ t.timestamps
41
+ end
42
+
43
+ create_table "invoicing_payment_references", :force => true do |t|
44
+ t.integer "invoice_id"
45
+ t.string "reference"
46
+ t.timestamps
47
+ end
48
+
49
+ create_table "invoicing_sellers", :force => true do |t|
50
+ t.integer "sellerable_id"
51
+ t.string "sellerable_type"
52
+ t.timestamps
53
+ end
54
+
55
+ create_table "invoicing_buyers", :force => true do |t|
56
+ t.integer "buyerable_id"
57
+ t.string "buyerable_type"
58
+ t.timestamps
59
+ end
60
+
61
+ create_table "invoicing_invoice_decorators", :force => true do |t|
62
+ t.integer "invoice_id"
63
+ t.text "data"
64
+ t.timestamps
65
+ end
66
+
67
+ create_table "invoicing_credit_note_invoices", :force => true do |t|
68
+ t.integer "invoice_id"
69
+ t.integer "credit_note_id"
70
+ end
71
+
72
+ create_table "invoicing_credit_note_credit_transactions", :force => true do |t|
73
+ t.integer "credit_note_id"
74
+ t.integer "transaction_id"
75
+ end
76
+
77
+ create_table "invoicing_line_item_types", :force => true do |t|
78
+ t.string "name"
79
+ end
80
+
81
+ end
@@ -0,0 +1 @@
1
+ *.log
File without changes
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+
3
+ describe Invoicing::CreditNote do
4
+ include Helpers
5
+
6
+ before(:each) do
7
+ tear_it_down
8
+
9
+ invoice_buyer = Invoicing::DebitTransaction.create!
10
+ invoice_seller = Invoicing::DebitTransaction.create!
11
+
12
+ @invoice = Invoicing::generate do
13
+ to invoice_buyer
14
+ from invoice_seller
15
+
16
+ line_item description: "Line Item 1", amount: 1101
17
+ line_item description: "Line Item 2", amount: 5097
18
+ line_item description: "Line Item 3", amount: 1714
19
+ line_item description: "Line Item 4", amount: 20300
20
+
21
+ payment_reference "REF123"
22
+ decorate_with tenant_name: "Peter"
23
+ end
24
+
25
+ @invoice_buyer = invoice_buyer
26
+ @invoice_seller = invoice_seller
27
+
28
+ @invoice.issue!
29
+ end
30
+
31
+ context "when recording a credit note" do
32
+ before(:each) do
33
+ @invoice.line_items.each do |li|
34
+ li.invoiceable = li
35
+ li.invoiceable.extend(Invoicing::Invoiceable)
36
+ li.amount = 0
37
+ li.tax = 0
38
+ li.save!
39
+ end
40
+
41
+ @invoice.reload
42
+ end
43
+
44
+ it "should raise an exception if the invoice is not in an issued state" do
45
+ lambda {
46
+ @credit_note = Invoicing::generate_credit_note do
47
+ against_invoice @invoice
48
+ end
49
+ }.should raise_error(RuntimeError, "You must allocate a credit note against an invoice")
50
+
51
+ end
52
+
53
+ context "against an invoice" do
54
+ before(:each) do
55
+ invoice = @invoice
56
+ line_items = invoice.line_items
57
+
58
+ @credit_note = Invoicing::generate_credit_note do
59
+ line_items.each do |line_item|
60
+ credit amount: line_item.amount, line_item: line_item, description: "Credit note for Line Item #{line_item.id}", tax: 0
61
+ end
62
+
63
+ against_invoice invoice
64
+ end
65
+
66
+ @invoice.reload
67
+ end
68
+
69
+ it "should be issued" do
70
+ @credit_note.should be_issued
71
+ end
72
+
73
+ it "should have a receipt number" do
74
+ @credit_note.receipt_number.should == "CN#{@credit_note.id}"
75
+ end
76
+
77
+ it "should be able to record a line item for the credit note" do
78
+ @credit_note.line_items.count.should == @invoice.line_items.count
79
+ @credit_note.line_items.first.amount.should == @invoice.line_items.first.amount
80
+ end
81
+
82
+ it "should create a credit transaction against the invoice" do
83
+ @invoice.settled?.should be_true
84
+ end
85
+
86
+ it "should know if it is linked to an invoice" do
87
+ @credit_note.invoice.should == @invoice
88
+ end
89
+
90
+ it "should be linked to the credit transaction paid against the invoice" do
91
+ @credit_note.credit_note_credit_transactions.count.should == 1
92
+ @credit_note.credit_note_credit_transactions.first.transaction.amount.should == @invoice.total
93
+ end
94
+
95
+ it "should be recorded against the invoice" do
96
+ @invoice.reload
97
+ @invoice.credit_notes.count.should == 1
98
+ @invoice.credit_notes.first.receipt_number.should == "CN#{@credit_note.id}"
99
+ end
100
+
101
+ it "should set the buyer to the invoice's buyer" do
102
+ @credit_note.buyer.buyerable.should == @invoice_buyer
103
+ end
104
+
105
+ it "should set the seller to the invoice's seller" do
106
+ @credit_note.seller.sellerable.should == @invoice_seller
107
+ end
108
+
109
+ end
110
+
111
+ context "When applying credits to the invoiceables" do
112
+
113
+ it "should receive handle credit for each line item being credited" do
114
+ credit_note = Invoicing::CreditNote.new
115
+ credit_note.line_items = @invoice.line_items
116
+
117
+ credit_note.line_items.map(&:invoiceable).compact.each do |item|
118
+ item.should_receive(:handle_credit)
119
+ end
120
+
121
+ credit_note.record_credit_notes!
122
+ end
123
+
124
+ end
125
+
126
+ context "trying to create a standalone credit note" do
127
+
128
+ it "should raise an error if no invoice is specified" do
129
+ expect {Invoicing::generate_credit_note do
130
+ line_items.each do |line_item|
131
+ credit amount: 500, description: "Credit note for Line Item #{line_item.id}", tax: 0
132
+ end
133
+ decorate_with tenant_name: "Peter"
134
+ end}.to raise_error(RuntimeError, "You must allocate a credit note against an invoice")
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ end
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+
3
+ describe Invoicing::InvoiceAdjustment do
4
+ include Helpers
5
+
6
+ before(:each) do
7
+ tear_it_down
8
+
9
+ buyer = Invoicing::DebitTransaction.create!
10
+
11
+ @invoice = Invoicing::generate do
12
+ to buyer
13
+ numbered "INV123"
14
+
15
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
16
+ line_item description: "Line Item 2", amount: 5097, line_item_type_id: 1
17
+ line_item description: "Line Item 3", amount: 1714, line_item_type_id: 1
18
+
19
+ payment_reference "REF123"
20
+ decorate_with tenant_name: "Peter"
21
+ end
22
+ end
23
+
24
+ context "adjusting a draft invoice" do
25
+
26
+ it "should allow the due date to be updated" do
27
+ @invoice.adjust do
28
+ due Date.tomorrow
29
+ end
30
+ end
31
+
32
+ it "should allow the buyer to be changed" do
33
+ new_buyer = Invoicing::DebitTransaction.create!
34
+
35
+ @invoice.adjust do
36
+ to new_buyer
37
+ end
38
+
39
+ @invoice.buyer.buyerable.should == new_buyer
40
+ end
41
+
42
+ it "should allow a payment references to be added" do
43
+ @invoice.adjust do
44
+ add_payment_reference "NEWREF123"
45
+ end
46
+
47
+ @invoice.payment_references.count.should == 2
48
+ end
49
+
50
+ it "should allow a payment references to be removed" do
51
+ @invoice.adjust do
52
+ remove_payment_reference @invoice.payment_references.last
53
+ end
54
+
55
+ @invoice.payment_references.count.should == 0
56
+ end
57
+
58
+ it "should allow the invoice decorator to be updated" do
59
+ decorations = {tenant_name: "Bob"}
60
+
61
+ @invoice.adjust do
62
+ decorate_with decorations
63
+ end
64
+
65
+ # @invoice.reload
66
+
67
+ @invoice.decorator.data.should == decorations
68
+ end
69
+
70
+ it "should allow line items to be added" do
71
+ item_to_invoice = @invoice.extend(Invoicing::Invoiceable)
72
+ @invoice.line_items.count.should == 3
73
+
74
+ @invoice.adjust do
75
+ add_line_item(item_to_invoice)
76
+ end
77
+
78
+ @invoice.line_items.count.should == 4
79
+ @invoice.total.should == 7912
80
+ end
81
+
82
+ it "should allow line items to be edited" do
83
+ line_item = @invoice.line_items.first
84
+ item_to_invoice = @invoice.extend(Invoicing::Invoiceable)
85
+
86
+ @invoice.adjust do
87
+ edit_line_item(line_item, {description: "Modified Line Item", amount: 500})
88
+ end
89
+
90
+ @invoice.line_items.count.should == 3
91
+ @invoice.total.should == 7311
92
+
93
+ line_item.reload
94
+
95
+ line_item.description.should == "Modified Line Item"
96
+ line_item.amount.should == 500
97
+ end
98
+
99
+ it "should allow line items to be removed" do
100
+ @invoice.line_items.count.should == 3
101
+
102
+ @invoice.adjust do
103
+ remove_line_item(@invoice.line_items.first)
104
+ end
105
+
106
+ @invoice.line_items.count.should == 2
107
+ @invoice.total.should == 6811
108
+ end
109
+
110
+ end
111
+
112
+ context "Adjusting an Invoice Number" do
113
+ it "should allow the invoice number to be updated" do
114
+ @invoice.adjust do
115
+ numbered "DRAFT-INV123"
116
+ end
117
+
118
+ @invoice.invoice_number.should == "DRAFT-INV123"
119
+ end
120
+ end
121
+
122
+ context "adjusting an issued invoice" do
123
+
124
+ it "should raise an exception" do
125
+ @invoice.issue!
126
+
127
+ expect {
128
+ @invoice.adjust do
129
+ due Date.tomorrow
130
+ end
131
+ }.to raise_error(Invoicing::CannotAdjustIssuedDocument)
132
+ end
133
+
134
+ end
135
+
136
+ end
@@ -0,0 +1,10 @@
1
+ # require 'spec_helper'
2
+ #
3
+ # describe Invoicing::LatePayment do
4
+ # include Helpers
5
+ #
6
+ # before(:each) do
7
+ # tear_it_down
8
+ # end
9
+ #
10
+ # end