xero_gateway 2.0.13 → 2.0.14

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.
@@ -2,10 +2,9 @@ module XeroGateway
2
2
  class Invoice
3
3
  include Dates
4
4
  include Money
5
+ include LineItemCalculations
5
6
 
6
- class Error < RuntimeError; end
7
7
  class NoGatewayError < Error; end
8
- class InvalidLineItemError < Error; end
9
8
 
10
9
  INVOICE_TYPE = {
11
10
  'ACCREC' => 'Accounts Receivable',
@@ -39,7 +38,7 @@ module XeroGateway
39
38
  attr_accessor :line_items_downloaded
40
39
 
41
40
  # All accessible fields
42
- attr_accessor :invoice_id, :invoice_number, :invoice_type, :invoice_status, :date, :due_date, :reference, :line_amount_types, :currency_code, :line_items, :contact, :payments, :fully_paid_on, :amount_due, :amount_paid, :amount_credited, :sent_to_contact
41
+ attr_accessor :invoice_id, :invoice_number, :invoice_type, :invoice_status, :date, :due_date, :reference, :line_amount_types, :currency_code, :line_items, :contact, :payments, :fully_paid_on, :amount_due, :amount_paid, :amount_credited, :sent_to_contact, :url
43
42
 
44
43
 
45
44
  def initialize(params = {})
@@ -107,58 +106,6 @@ module XeroGateway
107
106
  @contact ||= build_contact
108
107
  end
109
108
 
110
- # Helper method to create a new associated line_item.
111
- # Usage:
112
- # invoice.add_line_item({:description => "Bob's Widgets", :quantity => 1, :unit_amount => 120})
113
- def add_line_item(params = {})
114
- line_item = nil
115
- case params
116
- when Hash then line_item = LineItem.new(params)
117
- when LineItem then line_item = params
118
- else raise InvalidLineItemError
119
- end
120
-
121
- @line_items << line_item
122
-
123
- line_item
124
- end
125
-
126
- # Deprecated (but API for setter remains).
127
- #
128
- # As sub_total must equal SUM(line_item.line_amount) for the API call to pass, this is now
129
- # automatically calculated in the sub_total method.
130
- def sub_total=(value)
131
- end
132
-
133
- # Calculate the sub_total as the SUM(line_item.line_amount).
134
- def sub_total
135
- line_items.inject(BigDecimal.new('0')) { | sum, line_item | sum + BigDecimal.new(line_item.line_amount.to_s) }
136
- end
137
-
138
- # Deprecated (but API for setter remains).
139
- #
140
- # As total_tax must equal SUM(line_item.tax_amount) for the API call to pass, this is now
141
- # automatically calculated in the total_tax method.
142
- def total_tax=(value)
143
- end
144
-
145
- # Calculate the total_tax as the SUM(line_item.tax_amount).
146
- def total_tax
147
- line_items.inject(BigDecimal.new('0')) { | sum, line_item | sum + BigDecimal.new(line_item.tax_amount.to_s) }
148
- end
149
-
150
- # Deprecated (but API for setter remains).
151
- #
152
- # As total must equal sub_total + total_tax for the API call to pass, this is now
153
- # automatically calculated in the total method.
154
- def total=(value)
155
- end
156
-
157
- # Calculate the toal as sub_total + total_tax.
158
- def total
159
- sub_total + total_tax
160
- end
161
-
162
109
  # Helper method to check if the invoice is accounts payable.
163
110
  def accounts_payable?
164
111
  invoice_type == 'ACCPAY'
@@ -178,12 +125,10 @@ module XeroGateway
178
125
  def line_items
179
126
  if line_items_downloaded?
180
127
  @line_items
181
-
182
- # There is an invoice_is so we can assume this record was loaded from Xero.
183
- # attempt to download the line_item records.
184
- elsif invoice_id =~ GUID_REGEX
185
- raise NoGatewayError unless @gateway
186
-
128
+
129
+ elsif invoice_id =~ GUID_REGEX && @gateway
130
+ # There is an invoice_id so we can assume this record was loaded from Xero.
131
+ # Let's attempt to download the line_item records (if there is a gateway)
187
132
  response = @gateway.get_invoice(invoice_id)
188
133
  raise InvoiceNotFoundError, "Invoice with ID #{invoice_id} not found in Xero." unless response.success? && response.invoice.is_a?(XeroGateway::Invoice)
189
134
 
@@ -250,6 +195,7 @@ module XeroGateway
250
195
  line_item.to_xml(b)
251
196
  end
252
197
  }
198
+ b.Url url if url
253
199
  }
254
200
  end
255
201
 
@@ -279,6 +225,7 @@ module XeroGateway
279
225
  when "AmountPaid" then invoice.amount_paid = BigDecimal.new(element.text)
280
226
  when "AmountCredited" then invoice.amount_credited = BigDecimal.new(element.text)
281
227
  when "SentToContact" then invoice.sent_to_contact = (element.text.strip.downcase == "true")
228
+ when "Url" then invoice.url = element.text
282
229
  end
283
230
  end
284
231
  invoice
@@ -0,0 +1,55 @@
1
+ module XeroGateway
2
+ module LineItemCalculations
3
+
4
+ class Error < RuntimeError; end
5
+ class InvalidLineItemError < Error; end
6
+
7
+ def add_line_item(params = {})
8
+ line_item = nil
9
+ case params
10
+ when Hash then line_item = LineItem.new(params)
11
+ when LineItem then line_item = params
12
+ else raise InvalidLineItemError
13
+ end
14
+ @line_items << line_item
15
+ line_item
16
+ end
17
+
18
+ # Deprecated (but API for setter remains).
19
+ #
20
+ # As sub_total must equal SUM(line_item.line_amount) for the API call to pass, this is now
21
+ # automatically calculated in the sub_total method.
22
+ def sub_total=(value)
23
+ end
24
+
25
+ # Calculate the sub_total as the SUM(line_item.line_amount).
26
+ def sub_total
27
+ line_items.inject(BigDecimal.new('0')) { | sum, line_item | sum + BigDecimal.new(line_item.line_amount.to_s) }
28
+ end
29
+
30
+ # Deprecated (but API for setter remains).
31
+ #
32
+ # As total_tax must equal SUM(line_item.tax_amount) for the API call to pass, this is now
33
+ # automatically calculated in the total_tax method.
34
+ def total_tax=(value)
35
+ end
36
+
37
+ # Calculate the total_tax as the SUM(line_item.tax_amount).
38
+ def total_tax
39
+ line_items.inject(BigDecimal.new('0')) { | sum, line_item | sum + BigDecimal.new(line_item.tax_amount.to_s) }
40
+ end
41
+
42
+ # Deprecated (but API for setter remains).
43
+ #
44
+ # As total must equal sub_total + total_tax for the API call to pass, this is now
45
+ # automatically calculated in the total method.
46
+ def total=(value)
47
+ end
48
+
49
+ # Calculate the toal as sub_total + total_tax.
50
+ def total
51
+ sub_total + total_tax
52
+ end
53
+
54
+ end
55
+ end
@@ -6,17 +6,19 @@ module XeroGateway
6
6
  Array(response_item)
7
7
  end
8
8
 
9
- alias_method :invoice, :response_item
10
- alias_method :credit_note, :response_item
11
- alias_method :contact, :response_item
12
- alias_method :organisation, :response_item
13
- alias_method :invoices, :array_wrapped_response_item
14
- alias_method :credit_notes, :array_wrapped_response_item
15
- alias_method :contacts, :array_wrapped_response_item
16
- alias_method :accounts, :array_wrapped_response_item
17
- alias_method :tracking_categories, :array_wrapped_response_item
18
- alias_method :tax_rates, :array_wrapped_response_item
19
- alias_method :currencies, :array_wrapped_response_item
9
+ alias_method :invoice, :response_item
10
+ alias_method :credit_note, :response_item
11
+ alias_method :bank_transaction, :response_item
12
+ alias_method :contact, :response_item
13
+ alias_method :organisation, :response_item
14
+ alias_method :invoices, :array_wrapped_response_item
15
+ alias_method :credit_notes, :array_wrapped_response_item
16
+ alias_method :bank_transactions, :array_wrapped_response_item
17
+ alias_method :contacts, :array_wrapped_response_item
18
+ alias_method :accounts, :array_wrapped_response_item
19
+ alias_method :tracking_categories, :array_wrapped_response_item
20
+ alias_method :tax_rates, :array_wrapped_response_item
21
+ alias_method :currencies, :array_wrapped_response_item
20
22
 
21
23
  def initialize(params = {})
22
24
  params.each do |k,v|
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class CreateBankTransactionTest < Test::Unit::TestCase
4
+ include TestHelper
5
+
6
+ def setup
7
+ @gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
8
+
9
+ if STUB_XERO_CALLS
10
+ @gateway.xero_url = "DUMMY_URL"
11
+
12
+ @gateway.stubs(:http_put).with {|client, url, body, params| url =~ /BankTransactions$/ }.returns(get_file_as_string("create_bank_transaction.xml"))
13
+ @gateway.stubs(:http_post).with {|client, url, body, params| url =~ /BankTransactions$/ }.returns(get_file_as_string("bank_transaction.xml"))
14
+ end
15
+ end
16
+
17
+ def test_create_bank_transaction
18
+ example_bank_transaction = create_test_bank_transaction.dup
19
+
20
+ result = @gateway.create_bank_transaction(example_bank_transaction)
21
+ assert_kind_of XeroGateway::Response, result
22
+ assert result.success?
23
+ assert !result.request_xml.nil?
24
+ assert !result.response_xml.nil?
25
+ assert !result.bank_transaction.bank_transaction_id.nil?
26
+ assert example_bank_transaction.bank_transaction_id =~ GUID_REGEX
27
+ end
28
+
29
+ def test_create_bank_transaction_valid
30
+ example_bank_transaction = create_test_bank_transaction.dup
31
+ assert_equal true, example_bank_transaction.valid?,
32
+ "bank_transaction is invalid - errors:\n\t#{example_bank_transaction.errors.map { | error | "#{error[0]} #{error[1]}"}.join("\n\t")}"
33
+ end
34
+
35
+ private
36
+
37
+
38
+ end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class GetBankTransactionTest < Test::Unit::TestCase
4
+ include TestHelper
5
+
6
+ def setup
7
+ @gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
8
+
9
+ if STUB_XERO_CALLS
10
+ @gateway.xero_url = "DUMMY_URL"
11
+
12
+ @gateway.stubs(:http_get).with {|client, url, params| url =~ /BankTransactions(\/[0-9a-z\-]+)?$/i }.returns(get_file_as_string("bank_transaction.xml"))
13
+ @gateway.stubs(:http_put).with {|client, url, body, params| url =~ /BankTransactions$/ }.returns(get_file_as_string("create_bank_transaction.xml"))
14
+ end
15
+ end
16
+
17
+ def test_get_bank_transaction
18
+ # Make sure there is a bank transaction in Xero to retrieve
19
+ response = @gateway.create_bank_transaction(create_test_bank_transaction)
20
+ bank_transaction = response.bank_transaction
21
+
22
+ result = @gateway.get_bank_transaction(bank_transaction.bank_transaction_id)
23
+ assert result.success?
24
+ assert !result.request_params.nil?
25
+ assert !result.response_xml.nil?
26
+ assert_equal result.bank_transaction.bank_transaction_id, bank_transaction.bank_transaction_id
27
+ assert_equal result.bank_transaction.reference, bank_transaction.reference
28
+
29
+ result = @gateway.get_bank_transaction(bank_transaction.bank_transaction_id)
30
+ assert result.success?
31
+ assert !result.request_params.nil?
32
+ assert !result.response_xml.nil?
33
+ assert_equal result.bank_transaction.bank_transaction_id, bank_transaction.bank_transaction_id
34
+ end
35
+
36
+ def test_line_items_downloaded_set_correctly
37
+ # Make sure there is a bank transaction in Xero to retrieve.
38
+ example_bank_transaction = @gateway.create_bank_transaction(create_test_bank_transaction).bank_transaction
39
+
40
+ # No line items.
41
+ response = @gateway.get_bank_transaction(example_bank_transaction.bank_transaction_id)
42
+ assert_equal(true, response.success?)
43
+
44
+ bank_transaction = response.bank_transaction
45
+ assert_kind_of(XeroGateway::LineItem, bank_transaction.line_items.first)
46
+ assert_kind_of(XeroGateway::BankTransaction, bank_transaction)
47
+ assert_equal(true, bank_transaction.line_items_downloaded?)
48
+ end
49
+
50
+ end
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class GetBankTransactionsTest < Test::Unit::TestCase
4
+ include TestHelper
5
+
6
+ INVALID_BANK_TRANSACTION_ID = "99999999-9999-9999-9999-999999999999" unless defined?(INVALID_BANK_TRANSACTION_ID)
7
+
8
+ def setup
9
+ @gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
10
+
11
+ if STUB_XERO_CALLS
12
+ @gateway.xero_url = "DUMMY_URL"
13
+
14
+ @gateway.stubs(:http_get).with {|client, url, params| url =~ /BankTransactions(\/[0-9a-z\-]+)?$/i }.returns(get_file_as_string("bank_transactions.xml"))
15
+ @gateway.stubs(:http_put).with {|client, url, body, params| url =~ /BankTransactions$/ }.returns(get_file_as_string("create_bank_transaction.xml"))
16
+
17
+ # Get a bank transaction with an invalid ID.
18
+ @gateway.stubs(:http_get).with {|client, url, params| url =~ Regexp.new("BankTransactions/#{INVALID_BANK_TRANSACTION_ID}") }.returns(get_file_as_string("bank_transaction_not_found_error.xml"))
19
+ end
20
+ end
21
+
22
+ def test_get_bank_transactions
23
+ # Make sure there is a bank transaction in Xero to retrieve
24
+ bank_transaction = @gateway.create_bank_transaction(create_test_bank_transaction).bank_transaction
25
+
26
+ result = @gateway.get_bank_transactions
27
+ assert result.success?
28
+ assert !result.request_params.nil?
29
+ assert !result.response_xml.nil?
30
+ assert result.bank_transactions.collect {|i| i.reference}.include?(bank_transaction.reference)
31
+ assert result.bank_transactions.collect {|i| i.bank_transaction_id}.include?(bank_transaction.bank_transaction_id)
32
+ end
33
+
34
+ def test_get_bank_transactions_with_modified_since_date
35
+ # Create a test bank transaction
36
+ @gateway.create_bank_transaction(create_test_bank_transaction)
37
+
38
+ # Check that it is returned
39
+ result = @gateway.get_bank_transactions(:modified_since => Date.today - 1)
40
+ assert result.success?
41
+ assert !result.request_params.nil?
42
+ assert !result.response_xml.nil?
43
+ assert result.request_params.keys.include?(:ModifiedAfter) # make sure the flag was sent
44
+ end
45
+
46
+ def test_line_items_downloaded_set_correctly
47
+ # No line items.
48
+ response = @gateway.get_bank_transactions
49
+ assert_equal(true, response.success?)
50
+
51
+ bank_transaction = response.bank_transactions.first
52
+ assert_kind_of(XeroGateway::BankTransaction, bank_transaction)
53
+ assert_equal(false, bank_transaction.line_items_downloaded?)
54
+ end
55
+
56
+ # Make sure that a reference to gateway is passed when the get_bank_transactions response is parsed.
57
+ def test_get_bank_transactions_gateway_reference
58
+ result = @gateway.get_bank_transactions
59
+ assert(result.success?)
60
+ assert_not_equal(0, result.bank_transactions.size)
61
+
62
+ result.bank_transactions.each do |bank_transaction|
63
+ assert(bank_transaction.gateway === @gateway)
64
+ end
65
+ end
66
+
67
+ # Test to make sure that we correctly error when a bank transaction doesn't have an ID.
68
+ # This should usually never be ecountered.
69
+ def test_to_ensure_that_a_bank_transaction_with_invalid_id_errors
70
+ # Make sure there is a bank transaction to retrieve, even though we will mangle it later.
71
+ bank_transaction = @gateway.create_bank_transaction(create_test_bank_transaction).bank_transaction
72
+
73
+ result = @gateway.get_bank_transactions
74
+ assert_equal(true, result.success?)
75
+
76
+ bank_transaction = result.bank_transactions.first
77
+ assert_equal(false, bank_transaction.line_items_downloaded?)
78
+
79
+ # Mangle invoice_id to invalid one.
80
+ bank_transaction.bank_transaction_id = INVALID_BANK_TRANSACTION_ID
81
+
82
+ # Make sure we fail here.
83
+ line_items = nil
84
+ assert_raise(XeroGateway::BankTransactionNotFoundError) { line_items = bank_transaction.line_items }
85
+ assert_nil(line_items)
86
+ end
87
+
88
+ end
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class UpdateBankTransactionTest < Test::Unit::TestCase
4
+ include TestHelper
5
+
6
+ def setup
7
+ @gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
8
+
9
+ if STUB_XERO_CALLS
10
+ @gateway.xero_url = "DUMMY_URL"
11
+
12
+ @gateway.stubs(:http_put).with {|client, url, body, params| url =~ /BankTransactions$/ }.returns(get_file_as_string("create_bank_transaction.xml"))
13
+ @gateway.stubs(:http_post).with {|client, url, body, params| url =~ /BankTransactions$/ }.returns(get_file_as_string("bank_transaction.xml"))
14
+ end
15
+ end
16
+
17
+ def test_update_bank_transaction
18
+ bank_transaction = @gateway.create_bank_transaction(create_test_bank_transaction).bank_transaction
19
+
20
+ today = Date.today
21
+ bank_transaction.date = today
22
+
23
+ result = @gateway.update_bank_transaction(bank_transaction)
24
+
25
+ assert result.success?
26
+ assert !result.request_xml.nil?
27
+ assert !result.response_xml.nil?
28
+ assert_equal bank_transaction.bank_transaction_id, result.bank_transaction.bank_transaction_id
29
+ assert_equal today, result.bank_transaction.date if !STUB_XERO_CALLS
30
+ end
31
+ end
data/test/test_helper.rb CHANGED
@@ -100,5 +100,80 @@ module TestHelper
100
100
  f.close
101
101
  return data
102
102
  end
103
-
103
+
104
+ def create_test_bank_transaction(params={}, contact_params={}, line_item_params={})
105
+ params = {
106
+ :type => 'RECEIVE',
107
+ :date => Date.today,
108
+ :reference => '12345',
109
+ :status => 'ACTIVE',
110
+ }.merge(params)
111
+ bank_transaction = XeroGateway::BankTransaction.new(params)
112
+
113
+ bank_transaction.contact = create_test_contact(contact_params)
114
+ add_test_line_items(bank_transaction, line_item_params)
115
+ bank_transaction.bank_account = create_test_account
116
+
117
+ bank_transaction
118
+ end
119
+
120
+ def add_test_line_items(bank_transaction, line_item_params={})
121
+ if line_item_params
122
+ line_item_params = [line_item_params].flatten # always use an array, even if only a single hash passed in
123
+
124
+ # At least one line item, make first have some defaults.
125
+ line_item_params << {} if line_item_params.size == 0
126
+ line_item_params[0] = {
127
+ :description => "A LINE ITEM",
128
+ :account_code => "200",
129
+ :unit_amount => BigDecimal.new("100"),
130
+ :tax_amount => BigDecimal.new("12.5"),
131
+ :tracking => XeroGateway::TrackingCategory.new(:name => "blah", :options => "hello")
132
+ }.merge(line_item_params[0])
133
+
134
+ # Create line_items from line_item_params
135
+ line_item_params.each do |line_item|
136
+ bank_transaction.add_line_item(line_item)
137
+ end
138
+ end
139
+ bank_transaction
140
+ end
141
+
142
+ def create_test_account
143
+ account = XeroGateway::Account.new(:account_id => "57cedda9")
144
+ account.code = "200"
145
+ account.name = "Sales"
146
+ account.type = "REVENUE"
147
+ account.tax_type = "OUTPUT"
148
+ account.description = "Income from any normal business activity"
149
+ account.enable_payments_to_account = false
150
+ account
151
+ end
152
+
153
+ def create_test_contact(contact_params={})
154
+ # Strip out :address key from contact_params to use as the default address.
155
+ stripped_address = {
156
+ :address_type => 'STREET',
157
+ :line_1 => 'LINE 1 OF THE ADDRESS'
158
+ }.merge(contact_params.delete(:address) || {})
159
+
160
+ # Strip out :phone key from contact_params to use at the default phone.
161
+ stripped_phone = {
162
+ :phone_type => 'DEFAULT',
163
+ :number => '12345678'
164
+ }.merge(contact_params.delete(:phone) || {})
165
+
166
+ contact_params = {
167
+ :contact_id => '00000000-0000-0000-0000-000000000000', # Just any valid GUID
168
+ :name => "CONTACT NAME",
169
+ :first_name => "Bob",
170
+ :last_name => "Builder"
171
+ }.merge(contact_params)
172
+
173
+ contact = XeroGateway::Contact.new(contact_params)
174
+ contact.address = XeroGateway::Address.new(stripped_address)
175
+ contact.phone = XeroGateway::Phone.new(stripped_phone)
176
+ contact
177
+ end
178
+
104
179
  end