xero_gateway 2.0.13 → 2.0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -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