xeroizer 0.3.5 → 0.4.0

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 (54) hide show
  1. data/.bundle/config +2 -2
  2. data/Gemfile +5 -0
  3. data/Rakefile +17 -1
  4. data/VERSION +1 -1
  5. data/lib/xeroizer.rb +5 -1
  6. data/lib/xeroizer/configuration.rb +19 -0
  7. data/lib/xeroizer/generic_application.rb +2 -1
  8. data/lib/xeroizer/logging.rb +8 -0
  9. data/lib/xeroizer/models/account.rb +2 -1
  10. data/lib/xeroizer/models/bank_account.rb +12 -0
  11. data/lib/xeroizer/models/bank_transaction.rb +74 -0
  12. data/lib/xeroizer/models/invoice.rb +17 -12
  13. data/lib/xeroizer/models/item.rb +3 -3
  14. data/lib/xeroizer/models/item_purchase_details.rb +19 -0
  15. data/lib/xeroizer/models/{item_purchase_sale_details.rb → item_sales_details.rb} +5 -3
  16. data/lib/xeroizer/models/line_amount_type.rb +11 -0
  17. data/lib/xeroizer/models/line_item.rb +2 -12
  18. data/lib/xeroizer/models/line_item_sum.rb +21 -0
  19. data/lib/xeroizer/models/payment.rb +2 -6
  20. data/lib/xeroizer/oauth.rb +1 -1
  21. data/lib/xeroizer/record/base.rb +21 -2
  22. data/lib/xeroizer/record/validation_helper.rb +14 -2
  23. data/lib/xeroizer/record/validators/block_validator.rb +22 -0
  24. data/lib/xeroizer/record/validators/validator.rb +14 -5
  25. data/lib/xeroizer/record/xml_helper.rb +24 -7
  26. data/test/acceptance/about_creating_bank_transactions_test.rb +162 -0
  27. data/test/acceptance/about_fetching_bank_transactions_test.rb +56 -0
  28. data/test/acceptance/acceptance_test.rb +53 -0
  29. data/test/acceptance/bank_transaction_reference_data.rb +31 -0
  30. data/test/test_helper.rb +11 -1
  31. data/test/unit/models/bank_transaction_model_parsing_test.rb +131 -0
  32. data/test/unit/models/bank_transaction_test.rb +47 -0
  33. data/test/unit/models/bank_transaction_validation_test.rb +87 -0
  34. data/test/unit/models/contact_test.rb +2 -2
  35. data/test/unit/models/credit_note_test.rb +2 -2
  36. data/test/unit/models/invoice_test.rb +43 -17
  37. data/test/unit/models/line_item_sum_test.rb +24 -0
  38. data/test/unit/models/line_item_test.rb +54 -0
  39. data/test/unit/oauth_config_test.rb +20 -0
  40. data/test/unit/oauth_test.rb +1 -1
  41. data/test/unit/private_application_test.rb +2 -2
  42. data/test/unit/record/base_model_test.rb +2 -2
  43. data/test/unit/record/base_test.rb +38 -1
  44. data/test/unit/record/block_validator_test.rb +125 -0
  45. data/test/unit/record/model_definition_test.rb +2 -2
  46. data/test/unit/record/parse_where_hash_test.rb +2 -2
  47. data/test/unit/record/record_association_test.rb +1 -1
  48. data/test/unit/record/validators_test.rb +51 -3
  49. data/test/unit/record_definition_test.rb +2 -2
  50. data/test/unit/report_definition_test.rb +2 -2
  51. data/test/unit/report_test.rb +1 -1
  52. data/xeroizer.gemspec +60 -6
  53. metadata +124 -66
  54. data/lib/.DS_Store +0 -0
@@ -0,0 +1,131 @@
1
+ require "test_helper"
2
+
3
+ class BankTransactionModelParsingTest < Test::Unit::TestCase
4
+ def setup
5
+ # See lib/xeroizer/record/base_model.rb
6
+ @instance = Xeroizer::Record::BankTransactionModel.new(nil, "BankTransaction")
7
+ end
8
+
9
+ must "parse all the root elements except sub_total, total_tax and total" do
10
+ some_xml = "
11
+ <Response>
12
+ <BankTransactions>
13
+ <BankTransaction>
14
+ <Date>2010-07-30T00:00:00</Date>
15
+ <LineAmountTypes>Inclusive</LineAmountTypes>
16
+ <SubTotal>15.00</SubTotal>
17
+ <TotalTax>0.00</TotalTax>
18
+ <Total>15.00</Total>
19
+ <UpdatedDateUTC>2008-02-20T12:19:56.657</UpdatedDateUTC>
20
+ <FullyPaidOnDate>2010-07-30T00:00:00</FullyPaidOnDate>
21
+ <BankTransactionID>d20b6c54-7f5d-4ce6-ab83-55f609719126</BankTransactionID>
22
+ <Type>SPEND</Type>
23
+ <IsReconciled>true</IsReconciled>
24
+ </BankTransaction>
25
+ </BankTransactions>
26
+ </Response>"
27
+
28
+ result = @instance.parse_response(some_xml)
29
+
30
+ the_bank_transaction = result.response_items.first
31
+
32
+ assert_equal Date.parse("2010-07-30T00:00:00"), the_bank_transaction.date
33
+ assert_equal "Inclusive", the_bank_transaction.line_amount_types
34
+ assert_equal Date.parse("2008-02-20T12:19:56.657"), the_bank_transaction.updated_date_utc
35
+ assert_equal Date.parse("2010-07-30T00:00:00"), the_bank_transaction.fully_paid_on_date
36
+ assert_equal "d20b6c54-7f5d-4ce6-ab83-55f609719126", the_bank_transaction.bank_transaction_id
37
+ assert_equal "SPEND", the_bank_transaction.type
38
+ assert the_bank_transaction.reconciled?, "Expected reconciled to be true"
39
+ end
40
+
41
+ must "parse the contact" do
42
+ some_xml_with_a_contact = "
43
+ <Response>
44
+ <BankTransactions>
45
+ <BankTransaction>
46
+ <Contact>
47
+ <ContactID>c09661a2-a954-4e34-98df-f8b6d1dc9b19</ContactID>
48
+ <ContactStatus>ACTIVE</ContactStatus>
49
+ <Name>BNZ</Name>
50
+ <Addresses>
51
+ <Address>
52
+ <AddressType>POBOX</AddressType>
53
+ </Address>
54
+ <Address>
55
+ <AddressType>STREET</AddressType>
56
+ </Address>
57
+ </Addresses>
58
+ <Phones>
59
+ <Phone>
60
+ <PhoneType>MOBILE</PhoneType>
61
+ </Phone>
62
+ </Phones>
63
+ <UpdatedDateUTC>2010-09-17T19:26:39.157</UpdatedDateUTC>
64
+ </Contact>
65
+ </BankTransaction>
66
+ </BankTransactions>
67
+ </Response>"
68
+
69
+ result = @instance.parse_response(some_xml_with_a_contact)
70
+ the_bank_transaction = result.response_items.first
71
+ the_contact = the_bank_transaction.contact
72
+
73
+ assert_equal(
74
+ "c09661a2-a954-4e34-98df-f8b6d1dc9b19",
75
+ the_contact.contact_id,
76
+ "Mismatched contact id for contact: #{the_contact.inspect}"
77
+ )
78
+ end
79
+
80
+ must "parse the correct number of line items" do
81
+ some_xml_with_a_line_item = "
82
+ <Response>
83
+ <BankTransactions>
84
+ <BankTransaction>
85
+ <LineItems>
86
+ <LineItem>
87
+ <Description>Monthly account fee</Description>
88
+ <UnitAmount>15</UnitAmount>
89
+ <TaxType>NONE</TaxType>
90
+ <TaxAmount>0.00</TaxAmount>
91
+ <LineAmount>15.00</LineAmount>
92
+ <AccountCode>404</AccountCode>
93
+ <Quantity>1.0000</Quantity>
94
+ </LineItem>
95
+ </LineItems>
96
+ </BankTransaction>
97
+ </BankTransactions>
98
+ </Response>"
99
+
100
+ result = @instance.parse_response(some_xml_with_a_line_item)
101
+ the_bank_transaction = result.response_items.first
102
+
103
+ assert_equal(1, the_bank_transaction.line_items.size,
104
+ "Mismatched number of line items: #{the_bank_transaction.inspect}"
105
+ )
106
+ end
107
+
108
+ must "parse the bank account" do
109
+ some_xml_with_a_bank_account = "
110
+ <Response>
111
+ <BankTransactions>
112
+ <BankTransaction>
113
+ <BankAccount>
114
+ <AccountID>297c2dc5-cc47-4afd-8ec8-74990b8761e9</AccountID>
115
+ <Code>BANK</Code>
116
+ </BankAccount>
117
+ </BankTransaction>
118
+ </BankTransactions>
119
+ </Response>"
120
+
121
+ result = @instance.parse_response(some_xml_with_a_bank_account)
122
+ the_bank_transaction = result.response_items.first
123
+
124
+ assert_not_nil(the_bank_transaction.bank_account,
125
+ "Missing bank_account: #{the_bank_transaction.inspect}"
126
+ )
127
+
128
+ assert_equal "297c2dc5-cc47-4afd-8ec8-74990b8761e9", the_bank_transaction.bank_account.account_id,
129
+ "Unexpected bank account id: #{the_bank_transaction.inspect}"
130
+ end
131
+ end
@@ -0,0 +1,47 @@
1
+ require "test_helper"
2
+
3
+ class BankTransactionTest < Test::Unit::TestCase
4
+ include Xeroizer::Record
5
+
6
+ def setup
7
+ fake_parent = Class.new do
8
+ attr_accessor :application
9
+ end.new
10
+
11
+ the_line_items = [
12
+ LineItem.build({:quantity => 1, :tax_amount => 0.15, :unit_amount => 1.00, :tax_amount => 0.50}, nil),
13
+ LineItem.build({:quantity => 1, :tax_amount => 0.15, :unit_amount => 1.00, :tax_amount => 0.50}, nil)
14
+ ]
15
+
16
+ @the_bank_transaction = BankTransaction.new fake_parent
17
+ @the_bank_transaction.line_items = the_line_items
18
+ end
19
+
20
+ context "given a bank_transaction with line_amount_types set to \"Exclusive\"" do
21
+ setup do
22
+ @the_bank_transaction.line_amount_types = "Exclusive"
23
+ end
24
+
25
+ must "calculate the total as the sum of its line item line_amount and tax_amount" do
26
+ assert_equal "3.0", @the_bank_transaction.total.to_s
27
+ end
28
+
29
+ must "calculate the sub_total as the sum of the line_amounts" do
30
+ assert_equal "2.0", @the_bank_transaction.sub_total.to_s
31
+ end
32
+ end
33
+
34
+ context "given a bank_transaction with line_amount_types set to \"Inclusive\"" do
35
+ setup do
36
+ @the_bank_transaction.line_amount_types = "Inclusive"
37
+ end
38
+
39
+ must "calculate the total as the sum of its line item line_amount and tax_amount" do
40
+ assert_equal "2.0", @the_bank_transaction.total.to_s
41
+ end
42
+
43
+ must "calculate the sub_total as the sum of the line_amounts minus the total tax" do
44
+ assert_equal "1.0", @the_bank_transaction.sub_total.to_s
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,87 @@
1
+ require "test_helper"
2
+
3
+ class BankTransactionValidationTest < Test::Unit::TestCase
4
+ include Xeroizer::Record
5
+
6
+ must "supply either SPEND or RECEIVE as the type" do
7
+ instance = BankTransaction.build({:type => "xxx"}, nil)
8
+
9
+ assert false == instance.valid?, "Expected invalid because of invalid type"
10
+
11
+ expected_error = "Invalid type. Expected either SPEND or RECEIVE."
12
+
13
+ assert_equal expected_error, instance.errors_for(:type).first, "Expected an error about type"
14
+
15
+ instance = BankTransaction.build({:type => "SPEND"}, nil)
16
+
17
+ instance.valid?
18
+
19
+ assert_empty instance.errors_for(:type), "Expected no error about type"
20
+
21
+ instance = BankTransaction.build({:type => "RECEIVE"}, nil)
22
+
23
+ instance.valid?
24
+
25
+ assert_empty instance.errors_for(:type), "Expected no error about type"
26
+ end
27
+
28
+ must "supply a non-blank contact" do
29
+ instance = BankTransaction.build({}, nil)
30
+
31
+ assert false == instance.valid?, "Expected invalid because of missing contact"
32
+
33
+ assert_equal "can't be blank", instance.errors_for(:contact).first,
34
+ "Expected an error about blank contact"
35
+ end
36
+
37
+ must "supply at least one line item" do
38
+ zero_line_items = []
39
+
40
+ instance = BankTransaction.build({:line_items => zero_line_items}, nil)
41
+
42
+ assert false == instance.valid?, "Expected invalid because of empty line items"
43
+
44
+ assert_equal "Invalid line items. Must supply at least one.", instance.errors_for(:line_items).first,
45
+ "Expected an error about blank line items"
46
+
47
+ one_line_item = [LineItem.build({}, nil)]
48
+
49
+ instance.errors.clear
50
+ instance.line_items = one_line_item
51
+
52
+ assert_empty instance.errors_for(:line_items), "expected no errors for one line item, #{instance.errors_for(:line_items)}"
53
+
54
+ more_than_one_line_item = [LineItem.build({}, nil), LineItem.build({}, nil)]
55
+
56
+ instance.errors.clear
57
+ instance.line_items = more_than_one_line_item
58
+
59
+ assert_empty instance.errors_for(:line_items), "expected no errors for more than one line item, #{instance.errors_for(:line_items)}"
60
+ end
61
+
62
+ must "supply a non-blank bank account" do
63
+ instance = BankTransaction.build({}, nil)
64
+
65
+ assert false == instance.valid?, "Expected invalid because of missing bank account"
66
+
67
+ assert_equal "can't be blank", instance.errors_for(:bank_account).first,
68
+ "Expected an error about blank contact"
69
+ end
70
+
71
+ must "supply valid line_amount_types value" do
72
+ instance = BankTransaction.build({
73
+ :line_amount_types => "XXX_ANYTHING_INVALID_XXX"
74
+ }, nil)
75
+
76
+ assert false == instance.valid?, "Expected invalid because of missing bank account"
77
+
78
+ assert_equal "not one of Exclusive, Inclusive, NoTax", instance.errors_for(:line_amount_types).first,
79
+ "Expected an error about blank contact"
80
+ end
81
+
82
+ must "line_amount_type defaults to \"Exclusive\"" do
83
+ instance = BankTransaction.build({}, nil)
84
+
85
+ assert_equal "Exclusive", instance.line_amount_types
86
+ end
87
+ end
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), '../../test_helper.rb')
1
+ require 'test_helper'
2
2
 
3
3
  class ContactTest < Test::Unit::TestCase
4
4
  include TestHelper
@@ -23,4 +23,4 @@ class ContactTest < Test::Unit::TestCase
23
23
 
24
24
  end
25
25
 
26
- end
26
+ end
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), '../../test_helper.rb')
1
+ require 'test_helper'
2
2
 
3
3
  class CreditNoteTest < Test::Unit::TestCase
4
4
  include TestHelper
@@ -34,4 +34,4 @@ class CreditNoteTest < Test::Unit::TestCase
34
34
 
35
35
  end
36
36
 
37
- end
37
+ end
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), '../../test_helper.rb')
1
+ require 'test_helper'
2
2
 
3
3
  class InvoiceTest < Test::Unit::TestCase
4
4
  include TestHelper
@@ -8,7 +8,23 @@ class InvoiceTest < Test::Unit::TestCase
8
8
  mock_api('Invoices')
9
9
  @invoice = @client.Invoice.first
10
10
  end
11
-
11
+
12
+ def build_valid_authorised_invoice
13
+ @client.Invoice.build({
14
+ :type => "ACCREC",
15
+ :contact => { :name => "ABC Limited" },
16
+ :status => "AUTHORISED",
17
+ :date => Date.today,
18
+ :due_date => Date.today,
19
+ :line_items => [{
20
+ :description => "Consulting services as agreed",
21
+ :quantity => 0.005,
22
+ :unit_amount => 1,
23
+ :account_code => 200
24
+ }]
25
+ })
26
+ end
27
+
12
28
  context "invoice types" do
13
29
 
14
30
  should "have helpers to determine invoice type" do
@@ -45,7 +61,7 @@ class InvoiceTest < Test::Unit::TestCase
45
61
  assert_equal(invoice.attributes[:total], invoice.total)
46
62
  end
47
63
  end
48
-
64
+
49
65
  end
50
66
 
51
67
  context "invoice validations" do
@@ -65,22 +81,32 @@ class InvoiceTest < Test::Unit::TestCase
65
81
  end
66
82
 
67
83
  should "build a valid AUTHORISED invoice with complete attributes" do
68
- invoice = @client.Invoice.build({
69
- :type => "ACCREC",
70
- :contact => { :name => "ABC Limited" },
71
- :status => "AUTHORISED",
72
- :date => Date.today,
73
- :due_date => Date.today,
74
- :line_items => [{
75
- :description => "Consulting services as agreed",
76
- :quantity => 5,
77
- :unit_amount => 120,
78
- :account_code => 200
79
- }]
80
- })
84
+ invoice = build_valid_authorised_invoice
81
85
  assert_equal(true, invoice.valid?)
82
86
  end
83
87
 
84
88
  end
85
89
 
86
- end
90
+ context "line items" do
91
+
92
+ should "round line item amounts to two decimal places" do
93
+ invoice = build_valid_authorised_invoice
94
+ assert_equal(0.01, invoice.line_items.first.line_amount)
95
+ end
96
+
97
+ end
98
+
99
+ context "contact shortcuts" do
100
+
101
+ should "have valid #contact_name and #contact_id without downloading full invoice" do
102
+ invoices = @client.Invoice.all
103
+ invoices.each do |invoice|
104
+ assert_not_equal("", invoice.contact_name)
105
+ assert_not_equal("", invoice.contact_id)
106
+ assert_equal(false, invoice.complete_record_downloaded?)
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -0,0 +1,24 @@
1
+ require "test_helper"
2
+
3
+ class LineItemSumTest < Test::Unit::TestCase
4
+ include Xeroizer::Record
5
+
6
+ def setup
7
+ @the_line_items = [
8
+ LineItem.build({:quantity => 1, :unit_amount => 1.00, :tax_amount => 0.15}, nil),
9
+ LineItem.build({:quantity => 1, :unit_amount => 1.00, :tax_amount => 0.30}, nil)
10
+ ]
11
+ end
12
+
13
+ it "sub_total is the sum of the line_amounts" do
14
+ assert_equal BigDecimal("2.00"), LineItemSum.sub_total(@the_line_items)
15
+ end
16
+
17
+ it "total_tax is the sum of the tax_amounts" do
18
+ assert_equal BigDecimal("0.45"), LineItemSum.total_tax(@the_line_items)
19
+ end
20
+
21
+ it "total is the sum of sub_total and total_tax" do
22
+ assert_equal BigDecimal("2.45"), LineItemSum.total(@the_line_items)
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ require "test_helper"
2
+
3
+ class LineItemTest < Test::Unit::TestCase
4
+ include Xeroizer::Record
5
+
6
+ it "line_amount equals unit_price times quantity" do
7
+ line_item = LineItem.new(nil)
8
+
9
+ line_item.quantity = 1
10
+ line_item.unit_amount = BigDecimal("1337.00")
11
+ line_item.tax_amount = BigDecimal("0.15")
12
+
13
+ expected = BigDecimal((line_item.quantity * (line_item.unit_amount)).to_s).round(2)
14
+
15
+ assert_equal expected.to_s, line_item.line_amount.to_s
16
+ end
17
+
18
+ it "line_amount is zero when quantity is nil or zero" do
19
+ line_item = LineItem.new(nil)
20
+
21
+ line_item.quantity = nil
22
+ line_item.unit_amount = BigDecimal("1.00")
23
+ line_item.tax_amount = BigDecimal("0.15")
24
+
25
+ assert_equal "0.0", line_item.line_amount.to_s, "expected line amount zero when quantity is nil"
26
+
27
+ line_item.quantity = 0
28
+ assert_equal "0.0", line_item.line_amount.to_s, "expected line amount zero when quantity is zero"
29
+ end
30
+
31
+ it "is not possible to set unit_amount to zero" do
32
+ line_item = LineItem.new(nil)
33
+
34
+ line_item.quantity = 1
35
+ line_item.unit_amount = nil
36
+ line_item.tax_amount = BigDecimal("0.15")
37
+
38
+ assert_equal 0.0, line_item.unit_amount,
39
+ "Expected setting unit_amount to nil to be ignored, i.e., it should remain zero"
40
+ end
41
+
42
+ it "line_amount is zero when unit_amount is nil or zero" do
43
+ line_item = LineItem.new(nil)
44
+
45
+ line_item.quantity = 1
46
+ line_item.unit_amount = nil
47
+ line_item.tax_amount = BigDecimal("0.15")
48
+
49
+ assert_equal "0.0", line_item.line_amount.to_s, "expected line amount zero when unit_amount is nil"
50
+
51
+ line_item.unit_amount = BigDecimal("0.00")
52
+ assert_equal "0.0", line_item.line_amount.to_s, "expected line amount zero when unit_amount is zero"
53
+ end
54
+ end