xero_gateway-float 2.0.15
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.
- 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
|