uomi 0.2.13

Sign up to get free protection for your applications and to get access to all the features.
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,419 @@
1
+ require 'spec_helper'
2
+
3
+ describe Invoicing::Invoice do
4
+ include Helpers
5
+
6
+ before(:each) do
7
+ tear_it_down
8
+
9
+ @invoice = Invoicing::generate do
10
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
11
+ line_item description: "Line Item 2", amount: 5097, line_item_type_id: 1
12
+ line_item description: "Line Item 3", amount: 1714, line_item_type_id: 1
13
+ line_item description: "Line Item 4", amount: 20300, line_item_type_id: 1
14
+
15
+ payment_reference "REF123"
16
+ decorate_with tenant_name: "Peter"
17
+ end
18
+ end
19
+
20
+ it "should be able to add a line item" do
21
+ invoice = Invoicing::generate do
22
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
23
+ end
24
+
25
+ line_items = invoice.line_items
26
+ line_items.count.should == 1
27
+ line_items.first.amount.should == 1101
28
+ line_items.first.description.should == "Line Item 1"
29
+ end
30
+
31
+ it "should have a total matching the sum of its line item amounts" do
32
+ @invoice.total.should == 28212
33
+ end
34
+
35
+ context "an issued invoice" do
36
+
37
+ before(:each) do
38
+ @invoice.issue!
39
+ end
40
+
41
+ it "should be able to calculate its balance as the sum of its credit and debit transactions" do
42
+ @invoice.balance.should == -28212
43
+ end
44
+
45
+ it "should be considered as settled if its balance is zero" do
46
+ @invoice.balance.should_not be_zero
47
+ @invoice.settled?.should be_false
48
+
49
+ @invoice.add_credit_transaction amount: 28212
50
+ @invoice.save!
51
+
52
+ @invoice.balance.should be_zero
53
+ @invoice.settled?.should be_true
54
+ end
55
+
56
+ it "should report as overdue if it is not settled and the due date has past" do
57
+ @invoice.due_date = Date.today - 1.days
58
+ @invoice.save!
59
+
60
+ @invoice.overdue?.should be_true
61
+ end
62
+
63
+ it "should not report as overdue if the invoice has been settled" do
64
+ @invoice.add_credit_transaction amount: 28212
65
+ @invoice.due_date = Date.today - 1.days
66
+ @invoice.save!
67
+
68
+ @invoice.overdue?.should be_false
69
+ end
70
+
71
+ it "should record the issued time" do
72
+ @invoice.issued_at.to_date.should == Date.today
73
+ end
74
+
75
+ end
76
+
77
+ context "searching for sets of invoices" do
78
+
79
+ before(:each) do
80
+ tear_it_down
81
+ end
82
+
83
+ it "should find draft invoices" do
84
+ issued_invoice = Invoicing::generate do
85
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
86
+ end
87
+
88
+ issued_invoice.issue!
89
+
90
+ draft_invoice = Invoicing::generate do
91
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1, line_item_type_id: 1
92
+ end
93
+
94
+ Invoicing::Invoice.draft.count.should == 1
95
+ Invoicing::Invoice.draft.first.should == draft_invoice
96
+ end
97
+
98
+ it "should find issued invoices" do
99
+ issued_invoice = Invoicing::generate do
100
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
101
+ end
102
+
103
+ issued_invoice.issue!
104
+
105
+ other_invoice = Invoicing::generate do
106
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
107
+ end
108
+
109
+ Invoicing::Invoice.issued.count.should == 1
110
+ Invoicing::Invoice.issued.first.should == issued_invoice
111
+ end
112
+
113
+ it "should find owing invoices" do
114
+ owing_invoice = Invoicing::generate do
115
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
116
+ end
117
+
118
+ owing_invoice.issue!
119
+
120
+ other_invoice = Invoicing::generate do
121
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
122
+ end
123
+
124
+ Invoicing::Invoice.owing.count.should == 1
125
+ Invoicing::Invoice.owing.first.should == owing_invoice
126
+ end
127
+
128
+ it "should find settled invoices" do
129
+ settled_invoice = Invoicing::generate do
130
+ end
131
+
132
+ settled_invoice.issue!
133
+
134
+ other_invoice = Invoicing::generate do
135
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
136
+ end
137
+
138
+ Invoicing::Invoice.settled.count.should == 1
139
+ Invoicing::Invoice.settled.first.should == settled_invoice
140
+ end
141
+
142
+ it "should find voided invoices" do
143
+ voided_invoice = Invoicing::generate do
144
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
145
+ end
146
+
147
+ voided_invoice.issue!
148
+ voided_invoice.void!
149
+
150
+ other_invoice = Invoicing::generate do
151
+ line_item description: "Line Item 1", amount: 1101, line_item_type_id: 1
152
+ end
153
+
154
+ Invoicing::Invoice.voided.count.should == 1
155
+ Invoicing::Invoice.voided.first.should == voided_invoice
156
+ end
157
+
158
+ end
159
+
160
+ context "specifying an invoice number" do
161
+
162
+ it "should default the invoice number to a format of INV{invoice id}" do
163
+ @invoice.invoice_number.should == "INV#{@invoice.id}"
164
+ end
165
+
166
+ it "should allow a specific invoice number to be specified" do
167
+ invoice = Invoicing::generate do
168
+ numbered "CUSTOMREF123"
169
+ end
170
+
171
+ invoice.invoice_number.should == "CUSTOMREF123"
172
+ end
173
+
174
+ it "should allow a custom invoice number format to be specified containing the invoice id" do
175
+ invoice = Invoicing::generate do
176
+ numbered "CUSTOMREF{id}"
177
+ end
178
+
179
+ invoice.invoice_number.should == "CUSTOMREF#{invoice.id}"
180
+ end
181
+
182
+ it "should validate that the invoice number is unique per seller" do
183
+ seller = Invoicing::DebitTransaction.create!
184
+
185
+ invoice = Invoicing::generate do
186
+ numbered "CUSTOMREF123"
187
+ from seller
188
+ end
189
+
190
+ expect {
191
+ invoice = Invoicing::generate do
192
+ numbered "CUSTOMREF123"
193
+ from seller
194
+ end
195
+ }.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Invoice number has already been taken")
196
+ end
197
+
198
+ it "should allow two invoices with the same invoice number, but from different sellers" do
199
+ first_seller = Invoicing::DebitTransaction.create!
200
+
201
+ invoice = Invoicing::generate do
202
+ numbered "CUSTOMREF123"
203
+ from first_seller
204
+ end
205
+
206
+ second_seller = Invoicing::DebitTransaction.create!
207
+
208
+ expect {
209
+ invoice = Invoicing::generate do
210
+ numbered "CUSTOMREF123"
211
+ from second_seller
212
+ end
213
+ }.to_not raise_error
214
+ end
215
+
216
+ end
217
+
218
+ it "should be able to register multiple payment references to look up invoices by" do
219
+ invoice = Invoicing::generate do
220
+ payment_reference "Mr. Anderson"
221
+ payment_reference "REF23934"
222
+ end
223
+
224
+ invoice.payment_references.map(&:reference).should == ["Mr. Anderson", "REF23934"]
225
+ end
226
+
227
+ it "should be able to specify the seller" do
228
+ seller = Invoicing::DebitTransaction.create!
229
+
230
+ invoice = Invoicing::generate do
231
+ from seller
232
+ end
233
+
234
+ invoice.seller.sellerable.should == seller
235
+ end
236
+
237
+ it "should be able to find invoices for a given reference" do
238
+ invoice1 = Invoicing::generate do
239
+ payment_reference "Mr. Anderson"
240
+ payment_reference "REF23934"
241
+ end
242
+
243
+ invoice2 = Invoicing::generate do
244
+ payment_reference "Mr. Anderson"
245
+ end
246
+
247
+ Invoicing::Invoice.for_payment_reference("Mr. Anderson").should == [invoice1, invoice2]
248
+ Invoicing::Invoice.for_payment_reference("REF23934").should == [invoice1]
249
+ Invoicing::Invoice.for_payment_reference("My Payment").should == []
250
+ end
251
+
252
+ context "when adding a line item" do
253
+
254
+ it "should be able to attach an invoiceable item to the line item" do
255
+ item_to_invoice = @invoice.extend(Invoicing::Invoiceable)
256
+
257
+ invoice = Invoicing::generate do
258
+ line_item item_to_invoice
259
+ end
260
+
261
+ invoice.line_items.first.invoiceable.should == @invoice
262
+ end
263
+ end
264
+
265
+ it "should be able to add decoration data to the invoice" do
266
+ item_to_invoice = @invoice.extend(Invoicing::Invoiceable)
267
+
268
+ decoration = {invoicee: "Bob Jones"}
269
+
270
+ invoice = Invoicing::generate do
271
+ line_item item_to_invoice
272
+ decorate_with decoration
273
+ end
274
+
275
+ invoice.decorator.data.should == {invoicee: "Bob Jones"}
276
+ end
277
+
278
+ context "destroying an invoice" do
279
+
280
+ it "should destroy its associated line items" do
281
+ Invoicing::LineItem.create! amount: 0
282
+ Invoicing::LineItem.count.should == 5
283
+ @invoice.destroy
284
+ Invoicing::LineItem.count.should == 1
285
+ end
286
+
287
+ it "should destroy its associated transactions" do
288
+ @invoice.issue!
289
+ Invoicing::Transaction.create!
290
+ Invoicing::Transaction.count.should == 2
291
+ @invoice.destroy
292
+ Invoicing::Transaction.count.should == 1
293
+ end
294
+
295
+ it "should destroy its associated payment_references" do
296
+ Invoicing::PaymentReference.create!
297
+ Invoicing::PaymentReference.count.should == 2
298
+ @invoice.destroy
299
+ Invoicing::PaymentReference.count.should == 1
300
+ end
301
+
302
+ it "should destroy its associated late payments" do
303
+ Invoicing::LatePayment.create!
304
+ Invoicing::LatePayment.create! invoice_id: @invoice.id
305
+ Invoicing::LatePayment.count.should == 2
306
+ @invoice.destroy
307
+ Invoicing::LatePayment.count.should == 1
308
+ end
309
+
310
+ it "should destroy its associated decorator" do
311
+ Invoicing::InvoiceDecorator.create!
312
+ Invoicing::InvoiceDecorator.count.should == 2
313
+ @invoice.destroy
314
+ Invoicing::InvoiceDecorator.count.should == 1
315
+ end
316
+
317
+ end
318
+
319
+ context "giving the invoice state" do
320
+
321
+ context "new invoice" do
322
+ it "should default to the state draft" do
323
+ @invoice.should be_draft
324
+ end
325
+
326
+ context "voiding a drafted invoice" do
327
+ before(:each) do
328
+ @invoice.void!
329
+ end
330
+
331
+ it "should be marked as a voided invoice" do
332
+ @invoice.should be_voided
333
+ end
334
+
335
+ it "should not create a credit transaction against the draft invoice" do
336
+ @invoice.transactions.should be_blank
337
+ end
338
+ end
339
+ end
340
+
341
+ context "issuing the invoice" do
342
+ before(:each) do
343
+ @invoice.issue!
344
+ end
345
+
346
+ it "should be marked as an issued invoice" do
347
+ @invoice.should be_issued
348
+ end
349
+
350
+ it "should create a debit transaction with an amount matching the sum of its line item amounts" do
351
+ @invoice.transactions.length.should == 1
352
+ @invoice.transactions.first.debit?
353
+ @invoice.transactions.first.amount.should == 28212
354
+ end
355
+
356
+ context "voiding an issued invoice" do
357
+ before(:each) do
358
+ @invoice.void!
359
+ end
360
+
361
+ it "should mark the invoice as voided" do
362
+ @invoice.should be_voided
363
+ end
364
+
365
+ it "should create a credit transaction against the invoice" do
366
+ @invoice.credit_transactions.count.should == 1
367
+ end
368
+
369
+ it "should set the balance on the invoice to zero" do
370
+ @invoice.balance.should == 0
371
+ end
372
+
373
+ end
374
+
375
+ context "voiding an issued invoice with a payment recorded against it" do
376
+ before(:each) do
377
+ @invoice.add_credit_transaction amount: 1
378
+ end
379
+
380
+ it "should raise an error when voiding the invoice" do
381
+ lambda { @invoice.void! }.should raise_error(Invoicing::CannotVoidDocumentException, "Cannot void a document that has a transaction recorded against it!")
382
+ end
383
+ end
384
+ end
385
+ end
386
+
387
+ context "Hooks" do
388
+ before(:each) do
389
+ item_to_invoice = @invoice.extend(Invoicing::Invoiceable)
390
+ item_to_invoice.amount = 10
391
+
392
+ @invoice2 = Invoicing::generate do
393
+ line_item item_to_invoice
394
+ end
395
+
396
+ @item_to_invoice = item_to_invoice
397
+ end
398
+
399
+ context "issuing the invoice" do
400
+ it "the items to invoice should receive a mark invoice call" do
401
+ @item_to_invoice.should_receive(:mark_invoiced).with(@invoice2)
402
+ @invoice2.issue!
403
+ end
404
+ end
405
+
406
+ context "voiding the issued invoice" do
407
+ before(:each) do
408
+ @invoice2.issue!
409
+ end
410
+
411
+ it "the items to invoice should receive a mark uninvoiced call" do
412
+ @item_to_invoice.should_receive(:mark_uninvoiced).with(@invoice2)
413
+ @invoice2.void!
414
+ end
415
+
416
+ end
417
+ end
418
+
419
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Invoicing::LineItem do
4
+ include Helpers
5
+
6
+ it "should validate that the amount is positive" do
7
+ expect {Invoicing::LineItem.create! amount: -0.01}.to raise_error ActiveRecord::RecordInvalid, "Validation failed: Amount must be greater than or equal to 0"
8
+ end
9
+
10
+ it "should validate that the amount is specified" do
11
+ expect {Invoicing::LineItem.create! amount: nil}.to raise_error ActiveRecord::RecordInvalid, "Validation failed: Amount is not a number, Amount can't be blank"
12
+ end
13
+
14
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Invoicing::OverdueInvoice do
4
+ include Helpers
5
+
6
+ before(:each) do
7
+ tear_it_down
8
+ end
9
+
10
+ it "should find all invoices which are overdue" do
11
+ Invoicing::generate do
12
+ line_item description: "Line Item 1", amount: 1101
13
+ due Time.now
14
+ end
15
+
16
+ Invoicing::generate do
17
+ due Time.now - 1.days
18
+ line_item description: "Line Item 1", amount: 1101
19
+ end
20
+
21
+ Invoicing::generate do
22
+ due Time.now - 1.days
23
+ line_item description: "Line Item 1", amount: 0
24
+ end
25
+
26
+ Invoicing::Invoice.all.each(&:issue!)
27
+ Invoicing::OverdueInvoice.all.count.should == 1
28
+ end
29
+
30
+ it "should record a late payment against an invoice which is overdue" do
31
+ Invoicing::generate do
32
+ due Time.now
33
+ line_item description: "Line Item 1", amount: 1101
34
+ end
35
+
36
+ invoice2 = Invoicing::generate do
37
+ due Time.now - 1.days
38
+ line_item description: "Line Item 1", amount: 1101
39
+ end
40
+
41
+ Invoicing::generate do
42
+ due Time.now - 1.days
43
+ line_item description: "Line Item 1", amount: 0
44
+ end
45
+
46
+ Invoicing::Invoice.all.each(&:issue!)
47
+ Invoicing::OverdueInvoice.record_late_payments!
48
+
49
+ Invoicing::LatePayment.count.should == 1
50
+ Invoicing::LatePayment.first.invoice.should == invoice2
51
+ Invoicing::LatePayment.first.amount.should == 1101
52
+ invoice2.late_payment.should == Invoicing::LatePayment.first
53
+ end
54
+
55
+ it "should default the late payment penalty date to 7 days from the date the late payment was recorded" do
56
+ invoice = Invoicing::generate do
57
+ due Time.now - 1.days
58
+ line_item description: "Line Item 1", amount: 1101
59
+ end
60
+
61
+ Invoicing::Invoice.all.each(&:issue!)
62
+
63
+ Invoicing::OverdueInvoice.record_late_payments!
64
+ invoice.late_payment.penalty_date.should == Date.today.to_time + 7.days
65
+ end
66
+
67
+ end
@@ -0,0 +1,20 @@
1
+
2
+ require 'rubygems'
3
+ require 'bundler'
4
+
5
+ Bundler.require :default, :development
6
+
7
+ Combustion.initialize! :active_record
8
+
9
+ require 'rspec/rails'
10
+
11
+ require './spec/support/helpers.rb'
12
+
13
+ RSpec.configure do |config|
14
+ config.use_transactional_fixtures = true
15
+ config.color_enabled = true
16
+ config.formatter = :documentation # :progress, :html, :textmate
17
+ #config.include FactoryGirl::Syntax::Methods
18
+ end
19
+
20
+ #FactoryGirl.find_definitions
@@ -0,0 +1,20 @@
1
+ module Helpers
2
+ def tear_it_down
3
+ Invoicing::Invoice.delete_all
4
+ Invoicing::LineItem.delete_all
5
+ Invoicing::Transaction.delete_all
6
+ Invoicing::LatePayment.delete_all
7
+ Invoicing::PaymentReference.delete_all
8
+ Invoicing::Seller.delete_all
9
+ end
10
+ end
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+