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,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
+