xero_gateway-float 2.0.15
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +12 -0
- data/LICENSE +14 -0
- data/README.textile +357 -0
- data/Rakefile +14 -0
- data/examples/oauth.rb +25 -0
- data/examples/partner_app.rb +36 -0
- data/init.rb +1 -0
- data/lib/oauth/oauth_consumer.rb +14 -0
- data/lib/xero_gateway.rb +39 -0
- data/lib/xero_gateway/account.rb +95 -0
- data/lib/xero_gateway/accounts_list.rb +87 -0
- data/lib/xero_gateway/address.rb +96 -0
- data/lib/xero_gateway/bank_transaction.rb +178 -0
- data/lib/xero_gateway/ca-certificates.crt +2560 -0
- data/lib/xero_gateway/contact.rb +206 -0
- data/lib/xero_gateway/credit_note.rb +222 -0
- data/lib/xero_gateway/currency.rb +56 -0
- data/lib/xero_gateway/dates.rb +30 -0
- data/lib/xero_gateway/error.rb +18 -0
- data/lib/xero_gateway/exceptions.rb +46 -0
- data/lib/xero_gateway/gateway.rb +622 -0
- data/lib/xero_gateway/http.rb +138 -0
- data/lib/xero_gateway/http_encoding_helper.rb +49 -0
- data/lib/xero_gateway/invoice.rb +236 -0
- data/lib/xero_gateway/line_item.rb +125 -0
- data/lib/xero_gateway/line_item_calculations.rb +55 -0
- data/lib/xero_gateway/money.rb +16 -0
- data/lib/xero_gateway/oauth.rb +87 -0
- data/lib/xero_gateway/organisation.rb +75 -0
- data/lib/xero_gateway/partner_app.rb +30 -0
- data/lib/xero_gateway/payment.rb +40 -0
- data/lib/xero_gateway/phone.rb +77 -0
- data/lib/xero_gateway/private_app.rb +17 -0
- data/lib/xero_gateway/response.rb +41 -0
- data/lib/xero_gateway/tax_rate.rb +63 -0
- data/lib/xero_gateway/tracking_category.rb +87 -0
- data/test/integration/accounts_list_test.rb +109 -0
- data/test/integration/create_bank_transaction_test.rb +38 -0
- data/test/integration/create_contact_test.rb +66 -0
- data/test/integration/create_credit_note_test.rb +49 -0
- data/test/integration/create_invoice_test.rb +49 -0
- data/test/integration/get_accounts_test.rb +23 -0
- data/test/integration/get_bank_transaction_test.rb +51 -0
- data/test/integration/get_bank_transactions_test.rb +88 -0
- data/test/integration/get_contact_test.rb +28 -0
- data/test/integration/get_contacts_test.rb +40 -0
- data/test/integration/get_credit_note_test.rb +48 -0
- data/test/integration/get_credit_notes_test.rb +90 -0
- data/test/integration/get_currencies_test.rb +25 -0
- data/test/integration/get_invoice_test.rb +48 -0
- data/test/integration/get_invoices_test.rb +92 -0
- data/test/integration/get_organisation_test.rb +24 -0
- data/test/integration/get_tax_rates_test.rb +25 -0
- data/test/integration/get_tracking_categories_test.rb +27 -0
- data/test/integration/update_bank_transaction_test.rb +31 -0
- data/test/integration/update_contact_test.rb +31 -0
- data/test/integration/update_invoice_test.rb +31 -0
- data/test/test_helper.rb +179 -0
- data/test/unit/account_test.rb +47 -0
- data/test/unit/bank_transaction_test.rb +126 -0
- data/test/unit/contact_test.rb +97 -0
- data/test/unit/credit_note_test.rb +284 -0
- data/test/unit/currency_test.rb +31 -0
- data/test/unit/gateway_test.rb +119 -0
- data/test/unit/invoice_test.rb +326 -0
- data/test/unit/oauth_test.rb +116 -0
- data/test/unit/organisation_test.rb +38 -0
- data/test/unit/tax_rate_test.rb +38 -0
- data/test/unit/tracking_category_test.rb +52 -0
- data/xero_gateway.gemspec +15 -0
- metadata +164 -0
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'xero_gateway'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module OAuth
|
2
|
+
class Consumer
|
3
|
+
|
4
|
+
def http_with_ssl_client_certificates(*args)
|
5
|
+
@http ||= http_without_ssl_client_certificates(*args).tap do |http|
|
6
|
+
http.cert = options[:ssl_client_cert]
|
7
|
+
http.key = options[:ssl_client_key]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method_chain :http, :ssl_client_certificates
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
data/lib/xero_gateway.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "cgi"
|
2
|
+
require "uri"
|
3
|
+
require "net/https"
|
4
|
+
require "rexml/document"
|
5
|
+
require "builder"
|
6
|
+
require "bigdecimal"
|
7
|
+
require "oauth"
|
8
|
+
require 'oauth/signature/rsa/sha1'
|
9
|
+
require "forwardable"
|
10
|
+
require "active_support/all"
|
11
|
+
|
12
|
+
require File.join(File.dirname(__FILE__), 'oauth', 'oauth_consumer')
|
13
|
+
|
14
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'http_encoding_helper')
|
15
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'http')
|
16
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'dates')
|
17
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'money')
|
18
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'line_item_calculations')
|
19
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'response')
|
20
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'account')
|
21
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'accounts_list')
|
22
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'tracking_category')
|
23
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'contact')
|
24
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'line_item')
|
25
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'payment')
|
26
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'invoice')
|
27
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'bank_transaction')
|
28
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'credit_note')
|
29
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'address')
|
30
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'phone')
|
31
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'organisation')
|
32
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'tax_rate')
|
33
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'currency')
|
34
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'error')
|
35
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'oauth')
|
36
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'exceptions')
|
37
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'gateway')
|
38
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'private_app')
|
39
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'partner_app')
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class Account
|
3
|
+
|
4
|
+
TYPE = {
|
5
|
+
'CURRENT' => '',
|
6
|
+
'FIXED' => '',
|
7
|
+
'PREPAYMENT' => '',
|
8
|
+
'EQUITY' => '',
|
9
|
+
'DEPRECIATN' => '',
|
10
|
+
'DIRECTCOSTS' => '',
|
11
|
+
'EXPENSE' => '',
|
12
|
+
'OVERHEADS' => '',
|
13
|
+
'CURRLIAB' => '',
|
14
|
+
'LIABILITY' => '',
|
15
|
+
'TERMLIAB' => '',
|
16
|
+
'OTHERINCOME' => '',
|
17
|
+
'REVENUE' => '',
|
18
|
+
'SALES' => ''
|
19
|
+
} unless defined?(TYPE)
|
20
|
+
|
21
|
+
ACCOUNT_CLASS = {
|
22
|
+
'ASSET' => '',
|
23
|
+
'EQUITY' => '',
|
24
|
+
'EXPENSE' => '',
|
25
|
+
'LIABILITY' => '',
|
26
|
+
'REVENUE' => '',
|
27
|
+
} unless defined?(ACCOUNT_CLASS)
|
28
|
+
|
29
|
+
TAX_TYPE = {
|
30
|
+
'NONE' => 'No GST',
|
31
|
+
'EXEMPTINPUT' => 'VAT on expenses exempt from VAT (UK only)',
|
32
|
+
'INPUT' => 'GST on expenses',
|
33
|
+
'SRINPUT' => 'VAT on expenses',
|
34
|
+
'ZERORATEDINPUT' => 'Expense purchased from overseas (UK only)',
|
35
|
+
'RRINPUT' => 'Reduced rate VAT on expenses (UK Only)',
|
36
|
+
'EXEMPTOUTPUT' => 'VAT on sales exempt from VAT (UK only)',
|
37
|
+
'ECZROUTPUT' => 'EC Zero-rated output',
|
38
|
+
'OUTPUT' => 'OUTPUT (old rate)',
|
39
|
+
'OUTPUT2' => 'OUTPUT2',
|
40
|
+
'SROUTPUT' => 'SROUTPUT',
|
41
|
+
'ZERORATEDOUTPUT' => 'Sales made from overseas (UK only)',
|
42
|
+
'RROUTPUT' => 'Reduced rate VAT on sales (UK Only)',
|
43
|
+
'ZERORATED' => 'Zero-rated supplies/sales from overseas (NZ Only)'
|
44
|
+
} unless defined?(TAX_TYPE)
|
45
|
+
|
46
|
+
attr_accessor :account_id, :code, :name, :type, :account_class, :tax_type, :description, :system_account, :enable_payments_to_account, :currency_code
|
47
|
+
|
48
|
+
def initialize(params = {})
|
49
|
+
params.each do |k,v|
|
50
|
+
self.send("#{k}=", v)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def ==(other)
|
55
|
+
[:account_id, :code, :name, :type, :account_class, :tax_type, :description, :system_account, :enable_payments_to_account].each do |field|
|
56
|
+
return false if send(field) != other.send(field)
|
57
|
+
end
|
58
|
+
return true
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_xml(b = Builder::XmlMarkup.new, options={})
|
62
|
+
b.tag!(options[:name] ? options[:name] : 'Account') {
|
63
|
+
b.AccountID self.account_id
|
64
|
+
b.Code self.code
|
65
|
+
b.Name self.name
|
66
|
+
b.Type self.type
|
67
|
+
b.TaxType self.tax_type
|
68
|
+
b.Description self.description
|
69
|
+
b.SystemAccount self.system_account unless self.system_account.nil?
|
70
|
+
b.EnablePaymentsToAccount self.enable_payments_to_account
|
71
|
+
b.CurrencyCode currency_code if currency_code
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.from_xml(account_element)
|
76
|
+
account = Account.new
|
77
|
+
account_element.children.each do |element|
|
78
|
+
case(element.name)
|
79
|
+
when "AccountID" then account.account_id = element.text
|
80
|
+
when "Code" then account.code = element.text
|
81
|
+
when "Name" then account.name = element.text
|
82
|
+
when "Type" then account.type = element.text
|
83
|
+
when "Class" then account.account_class = element.text
|
84
|
+
when "TaxType" then account.tax_type = element.text
|
85
|
+
when "Description" then account.description = element.text
|
86
|
+
when "SystemAccount" then account.system_account = element.text
|
87
|
+
when "EnablePaymentsToAccount" then account.enable_payments_to_account = (element.text == 'true')
|
88
|
+
when "CurrencyCode" then account.currency_code = element.text
|
89
|
+
end
|
90
|
+
end
|
91
|
+
account
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class AccountsList
|
3
|
+
|
4
|
+
class Error < RuntimeError; end
|
5
|
+
class NoGatewayError < Error; end
|
6
|
+
class AccountsListNotLoadedError < Error; end
|
7
|
+
|
8
|
+
# Xero::Gateway associated with this invoice.
|
9
|
+
attr_accessor :gateway
|
10
|
+
|
11
|
+
# All accessible fields
|
12
|
+
attr_accessor :accounts
|
13
|
+
|
14
|
+
# Hash of accounts with the account code as the key.
|
15
|
+
attr :accounts_by_code
|
16
|
+
|
17
|
+
# Boolean representing whether the accounts list has been loaded.
|
18
|
+
attr :loaded
|
19
|
+
|
20
|
+
public
|
21
|
+
|
22
|
+
def initialize(gateway, initial_load = true)
|
23
|
+
raise NoGatewayError unless gateway && gateway.is_a?(XeroGateway::Gateway)
|
24
|
+
@gateway = gateway
|
25
|
+
@loaded = false
|
26
|
+
|
27
|
+
load if initial_load
|
28
|
+
end
|
29
|
+
|
30
|
+
def load
|
31
|
+
@loaded = false
|
32
|
+
response = gateway.get_accounts
|
33
|
+
@accounts = response.accounts
|
34
|
+
@loaded = true
|
35
|
+
|
36
|
+
# Cache accounts by code.
|
37
|
+
@accounts_by_code = {}
|
38
|
+
@accounts.each do | account |
|
39
|
+
@accounts_by_code[account.code.to_s] = account
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def loaded?
|
44
|
+
@loaded == true
|
45
|
+
end
|
46
|
+
|
47
|
+
# Lookup account by account_code.
|
48
|
+
def find_by_code(account_code)
|
49
|
+
raise AccountsListNotLoadedError unless loaded?
|
50
|
+
@accounts_by_code[account_code.to_s]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Alias [] method to find_by_code.
|
54
|
+
def [](account_code)
|
55
|
+
find_by_code(account_code)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return a list of all accounts matching account_type.
|
59
|
+
def find_all_by_type(account_type)
|
60
|
+
raise AccountsListNotLoadedError unless loaded?
|
61
|
+
@accounts.inject([]) do | list, account |
|
62
|
+
list << account if account.type == account_type
|
63
|
+
list
|
64
|
+
end
|
65
|
+
end
|
66
|
+
#
|
67
|
+
# Return a list of all accounts matching account_type.
|
68
|
+
def find_all_by_account_class(*account_classes)
|
69
|
+
account_classes.flatten!
|
70
|
+
raise AccountsListNotLoadedError unless loaded?
|
71
|
+
@accounts.inject([]) do | list, account |
|
72
|
+
list << account if account_classes.include?(account.account_class)
|
73
|
+
list
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return a list of all accounts matching tax_type.
|
78
|
+
def find_all_by_tax_type(tax_type)
|
79
|
+
raise AccountsListNotLoadedError unless loaded?
|
80
|
+
@accounts.inject([]) do | list, account |
|
81
|
+
list << account if account.tax_type == tax_type
|
82
|
+
list
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class Address
|
3
|
+
|
4
|
+
ADDRESS_TYPE = {
|
5
|
+
'STREET' => 'Street',
|
6
|
+
'POBOX' => 'PO Box'
|
7
|
+
} unless defined?(ADDRESS_TYPE)
|
8
|
+
|
9
|
+
# Any errors that occurred when the #valid? method called.
|
10
|
+
attr_reader :errors
|
11
|
+
|
12
|
+
attr_accessor :address_type, :line_1, :line_2, :line_3, :line_4, :city, :region, :post_code, :country
|
13
|
+
|
14
|
+
def initialize(params = {})
|
15
|
+
@errors ||= []
|
16
|
+
|
17
|
+
params = {
|
18
|
+
:address_type => "POBOX"
|
19
|
+
}.merge(params)
|
20
|
+
|
21
|
+
params.each do |k,v|
|
22
|
+
self.send("#{k}=", v)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validate the Address record according to what will be valid by the gateway.
|
27
|
+
#
|
28
|
+
# Usage:
|
29
|
+
# address.valid? # Returns true/false
|
30
|
+
#
|
31
|
+
# Additionally sets address.errors array to an array of field/error.
|
32
|
+
def valid?
|
33
|
+
@errors = []
|
34
|
+
|
35
|
+
if address_type && !ADDRESS_TYPE[address_type]
|
36
|
+
@errors << ['address_type', "must be one of #{ADDRESS_TYPE.keys.join('/')} and is currently #{address_type}"]
|
37
|
+
end
|
38
|
+
|
39
|
+
@errors.size == 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_xml(b = Builder::XmlMarkup.new)
|
43
|
+
b.Address {
|
44
|
+
b.AddressType address_type
|
45
|
+
b.AddressLine1 line_1 if line_1
|
46
|
+
b.AddressLine2 line_2 if line_2
|
47
|
+
b.AddressLine3 line_3 if line_3
|
48
|
+
b.AddressLine4 line_4 if line_4
|
49
|
+
b.City city if city
|
50
|
+
b.Region region if region
|
51
|
+
b.PostalCode post_code if post_code
|
52
|
+
b.Country country if country
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.from_xml(address_element)
|
57
|
+
address = Address.new
|
58
|
+
address_element.children.each do |element|
|
59
|
+
case(element.name)
|
60
|
+
when "AddressType" then address.address_type = element.text
|
61
|
+
when "AddressLine1" then address.line_1 = element.text
|
62
|
+
when "AddressLine2" then address.line_2 = element.text
|
63
|
+
when "AddressLine3" then address.line_3 = element.text
|
64
|
+
when "AddressLine4" then address.line_4 = element.text
|
65
|
+
when "City" then address.city = element.text
|
66
|
+
when "Region" then address.region = element.text
|
67
|
+
when "PostalCode" then address.post_code = element.text
|
68
|
+
when "Country" then address.country = element.text
|
69
|
+
end
|
70
|
+
end
|
71
|
+
address
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.parse(string)
|
75
|
+
address = Address.new
|
76
|
+
|
77
|
+
parts = string.split("\r\n")
|
78
|
+
|
79
|
+
if(parts.size > 3)
|
80
|
+
parts = [parts.shift, parts.shift, parts.shift, parts.join(", ")]
|
81
|
+
end
|
82
|
+
|
83
|
+
parts.each_with_index do |line, index|
|
84
|
+
address.send("line_#{index+1}=", line)
|
85
|
+
end
|
86
|
+
address
|
87
|
+
end
|
88
|
+
|
89
|
+
def ==(other)
|
90
|
+
[:address_type, :line_1, :line_2, :line_3, :line_4, :city, :region, :post_code, :country].each do |field|
|
91
|
+
return false if send(field) != other.send(field)
|
92
|
+
end
|
93
|
+
return true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class BankTransaction
|
3
|
+
include Dates
|
4
|
+
include LineItemCalculations
|
5
|
+
|
6
|
+
class NoGatewayError < Error; end
|
7
|
+
|
8
|
+
GUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/ unless defined?(GUID_REGEX)
|
9
|
+
|
10
|
+
TYPES = {
|
11
|
+
'RECEIVE' => 'Receive Bank Transaction',
|
12
|
+
'SPEND' => 'Spend Bank Transaction',
|
13
|
+
} unless defined?(TYPES)
|
14
|
+
|
15
|
+
STATUSES = {
|
16
|
+
'ACTIVE' => 'Bank Transaction is active',
|
17
|
+
'DELETED' => 'Bank Transaction is deleted',
|
18
|
+
} unless defined?(STATUSES)
|
19
|
+
|
20
|
+
# Xero::Gateway associated with this invoice.
|
21
|
+
attr_accessor :gateway
|
22
|
+
|
23
|
+
# Any errors that occurred when the #valid? method called.
|
24
|
+
attr_reader :errors
|
25
|
+
|
26
|
+
# Represents whether the line_items have been downloaded when getting from GET /API.XRO/2.0/BankTransactions
|
27
|
+
attr_accessor :line_items_downloaded
|
28
|
+
|
29
|
+
# accessible fields
|
30
|
+
attr_accessor :bank_transaction_id, :type, :date, :reference, :status, :contact, :line_items, :bank_account, :url, :is_reconciled, :updated_date_utc
|
31
|
+
|
32
|
+
def initialize(params = {})
|
33
|
+
@errors ||= []
|
34
|
+
@payments ||= []
|
35
|
+
|
36
|
+
# Check if the line items have been downloaded.
|
37
|
+
@line_items_downloaded = (params.delete(:line_items_downloaded) == true)
|
38
|
+
|
39
|
+
# params = {
|
40
|
+
# :line_amount_types => "Exclusive"
|
41
|
+
# }.merge(params)
|
42
|
+
params.each do |k,v|
|
43
|
+
self.send("#{k}=", v)
|
44
|
+
end
|
45
|
+
|
46
|
+
@line_items ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(other)
|
50
|
+
['type', 'reference', 'status', 'contact', 'line_items', 'bank_account'].each do |field|
|
51
|
+
return false if send(field) != other.send(field)
|
52
|
+
end
|
53
|
+
|
54
|
+
["date"].each do |field|
|
55
|
+
return false if send(field).to_s != other.send(field).to_s
|
56
|
+
end
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
# Validate the BankTransaction record according to what will be valid by the gateway.
|
61
|
+
#
|
62
|
+
# Usage:
|
63
|
+
# bank_transaction.valid? # Returns true/false
|
64
|
+
#
|
65
|
+
# Additionally sets bank_transaction.errors array to an array of field/error.
|
66
|
+
def valid?
|
67
|
+
@errors = []
|
68
|
+
|
69
|
+
if !bank_transaction_id.nil? && bank_transaction_id !~ GUID_REGEX
|
70
|
+
@errors << ['bank_transaction_id', 'must be blank or a valid Xero GUID']
|
71
|
+
end
|
72
|
+
|
73
|
+
if type && !TYPES[type]
|
74
|
+
@errors << ['type', "must be one of #{TYPES.keys.join('/')}"]
|
75
|
+
end
|
76
|
+
|
77
|
+
if status && !STATUSES[status]
|
78
|
+
@errors << ['status', "must be one of #{STATUSES.keys.join('/')}"]
|
79
|
+
end
|
80
|
+
|
81
|
+
unless date
|
82
|
+
@errors << ['date', "can't be blank"]
|
83
|
+
end
|
84
|
+
|
85
|
+
# Make sure contact is valid.
|
86
|
+
unless @contact && @contact.valid?
|
87
|
+
@errors << ['contact', 'is invalid']
|
88
|
+
end
|
89
|
+
|
90
|
+
# Make sure all line_items are valid.
|
91
|
+
unless line_items.all? { | line_item | line_item.valid? }
|
92
|
+
@errors << ['line_items', "at least one line item invalid"]
|
93
|
+
end
|
94
|
+
|
95
|
+
@errors.size == 0
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def line_items_downloaded?
|
100
|
+
@line_items_downloaded
|
101
|
+
end
|
102
|
+
|
103
|
+
# If line items are not downloaded, then attempt a download now (if this record was found to begin with).
|
104
|
+
def line_items
|
105
|
+
if line_items_downloaded?
|
106
|
+
@line_items
|
107
|
+
|
108
|
+
elsif bank_transaction_id =~ GUID_REGEX && @gateway
|
109
|
+
# There is a bank_transaction_id so we can assume this record was loaded from Xero.
|
110
|
+
# Let's attempt to download the line_item records (if there is a gateway)
|
111
|
+
|
112
|
+
response = @gateway.get_bank_transaction(bank_transaction_id)
|
113
|
+
raise BankTransactionNotFoundError, "Bank Transaction with ID #{bank_transaction_id} not found in Xero." unless response.success? && response.bank_transaction.is_a?(XeroGateway::BankTransaction)
|
114
|
+
|
115
|
+
@line_items = response.bank_transaction.line_items
|
116
|
+
@line_items_downloaded = true
|
117
|
+
|
118
|
+
@line_items
|
119
|
+
|
120
|
+
# Otherwise, this is a new bank transaction, so return the line_items reference.
|
121
|
+
else
|
122
|
+
@line_items
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_xml(b = Builder::XmlMarkup.new)
|
127
|
+
b.BankTransaction {
|
128
|
+
b.BankTransactionID bank_transaction_id if bank_transaction_id
|
129
|
+
b.Type type
|
130
|
+
# b.CurrencyCode self.currency_code if self.currency_code
|
131
|
+
contact.to_xml(b) if contact
|
132
|
+
bank_account.to_xml(b, :name => 'BankAccount') if bank_account
|
133
|
+
b.Date BankTransaction.format_date(date || Date.today)
|
134
|
+
b.Status status if status
|
135
|
+
b.Reference reference if reference
|
136
|
+
b.IsReconciled true if self.is_reconciled
|
137
|
+
b.LineItems {
|
138
|
+
self.line_items.each do |line_item|
|
139
|
+
line_item.to_xml(b)
|
140
|
+
end
|
141
|
+
}
|
142
|
+
b.Url url if url
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.from_xml(bank_transaction_element, gateway = nil, options = {})
|
147
|
+
bank_transaction = BankTransaction.new(options.merge({:gateway => gateway}))
|
148
|
+
bank_transaction_element.children.each do |element|
|
149
|
+
case(element.name)
|
150
|
+
when "BankTransactionID" then bank_transaction.bank_transaction_id = element.text
|
151
|
+
when "Type" then bank_transaction.type = element.text
|
152
|
+
# when "CurrencyCode" then invoice.currency_code = element.text
|
153
|
+
when "Contact" then bank_transaction.contact = Contact.from_xml(element)
|
154
|
+
when "BankAccount" then bank_transaction.bank_account = Account.from_xml(element)
|
155
|
+
when "Date" then bank_transaction.date = parse_date(element.text)
|
156
|
+
when "Status" then bank_transaction.status = element.text
|
157
|
+
when "Reference" then bank_transaction.reference = element.text
|
158
|
+
when "LineItems" then element.children.each {|line_item| bank_transaction.line_items_downloaded = true; bank_transaction.line_items << LineItem.from_xml(line_item) }
|
159
|
+
# when "SubTotal" then invoice.sub_total = BigDecimal.new(element.text)
|
160
|
+
# when "TotalTax" then invoice.total_tax = BigDecimal.new(element.text)
|
161
|
+
# when "Total" then invoice.total = BigDecimal.new(element.text)
|
162
|
+
# when "InvoiceID" then invoice.invoice_id = element.text
|
163
|
+
# when "InvoiceNumber" then invoice.invoice_number = element.text
|
164
|
+
# when "Payments" then element.children.each { | payment | invoice.payments << Payment.from_xml(payment) }
|
165
|
+
# when "AmountDue" then invoice.amount_due = BigDecimal.new(element.text)
|
166
|
+
# when "AmountPaid" then invoice.amount_paid = BigDecimal.new(element.text)
|
167
|
+
# when "AmountCredited" then invoice.amount_credited = BigDecimal.new(element.text)
|
168
|
+
# when "SentToContact" then invoice.sent_to_contact = (element.text.strip.downcase == "true")
|
169
|
+
when "UpdatedDateUTC" then bank_transaction.updated_date_utc = parse_utc_date_time(element.text)
|
170
|
+
when "IsReconciled" then bank_transaction.is_reconciled = (element.text.strip.downcase == "true")
|
171
|
+
when "Url" then bank_transaction.url = element.text
|
172
|
+
end
|
173
|
+
end
|
174
|
+
bank_transaction
|
175
|
+
end # from_xml
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|