tlconnor-xero_gateway 1.0.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.
@@ -0,0 +1,73 @@
1
+ # Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ module XeroGateway
16
+ module Http
17
+ OPEN_TIMEOUT = 10
18
+ READ_TIMEOUT = 60
19
+
20
+ def http_get(url, extra_params = {})
21
+ params = {:apiKey => @api_key, :xeroKey => @customer_key}
22
+ params = params.merge(extra_params).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
23
+
24
+ uri = URI.parse(url + "?" + params)
25
+
26
+ http = Net::HTTP.new(uri.host, uri.port)
27
+ http.open_timeout = OPEN_TIMEOUT
28
+ http.read_timeout = READ_TIMEOUT
29
+ http.use_ssl = true
30
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
31
+
32
+ http.get(uri.request_uri).body
33
+ end
34
+
35
+ def http_post(url, body, extra_params = {})
36
+ headers = {}
37
+ headers['Content-Type'] ||= "application/x-www-form-urlencoded"
38
+
39
+ params = {:apiKey => @api_key, :xeroKey => @customer_key}
40
+ params = params.merge(extra_params).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
41
+
42
+ uri = URI.parse(url + "?" + params)
43
+
44
+ http = Net::HTTP.new(uri.host, uri.port)
45
+ http.open_timeout = OPEN_TIMEOUT
46
+ http.read_timeout = READ_TIMEOUT
47
+ http.use_ssl = true
48
+
49
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
50
+
51
+ http.post(uri.request_uri, body, headers).body
52
+ end
53
+
54
+ def http_put(url, body, extra_params = {})
55
+ headers = {}
56
+ headers['Content-Type'] ||= "application/x-www-form-urlencoded"
57
+
58
+ params = {:apiKey => @api_key, :xeroKey => @customer_key}
59
+ params = params.merge(extra_params).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
60
+
61
+ uri = URI.parse(url + "?" + params)
62
+
63
+ http = Net::HTTP.new(uri.host, uri.port)
64
+ http.open_timeout = OPEN_TIMEOUT
65
+ http.read_timeout = READ_TIMEOUT
66
+ http.use_ssl = true
67
+
68
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
69
+
70
+ http.put(uri.request_uri, body, headers).body
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,48 @@
1
+ # Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ module XeroGateway
16
+ class Invoice
17
+ # All accessible fields
18
+ attr_accessor :id, :invoice_number, :invoice_type, :invoice_status, :date, :due_date, :reference, :tax_inclusive, :includes_tax, :sub_total, :total_tax, :total, :line_items, :contact
19
+
20
+ def initialize(params = {})
21
+ params = {
22
+ :contact => Contact.new,
23
+ :date => Time.now,
24
+ :includes_tax => true,
25
+ :tax_inclusive => true
26
+ }.merge(params)
27
+
28
+ params.each do |k,v|
29
+ self.instance_variable_set("@#{k}", v) ## create and initialize an instance variable for this key/value pair
30
+ self.send("#{k}=", v)
31
+ end
32
+
33
+ @line_items ||= []
34
+ end
35
+
36
+ def ==(other)
37
+ equal = true
38
+ ["invoice_number", "invoice_type", "invoice_status", "reference", "tax_inclusive", "includes_tax", "sub_total", "total_tax", "total", "contact", "line_items"].each do |field|
39
+ equal &&= (send(field) == other.send(field))
40
+ end
41
+ ["date", "due_date"].each do |field|
42
+ equal &&= (send(field).to_s == other.send(field).to_s)
43
+ end
44
+
45
+ return equal
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ module XeroGateway
16
+ class LineItem
17
+ # All accessible fields
18
+ attr_accessor :id, :description, :quantity, :unit_amount, :tax_type, :tax_amount, :line_amount, :account_code, :tracking_category, :tracking_option
19
+
20
+ def initialize(params = {})
21
+ params = {
22
+ :quantity => 1
23
+ }.merge(params)
24
+
25
+ params.each do |k,v|
26
+ self.instance_variable_set("@#{k}", v) ## create and initialize an instance variable for this key/value pair
27
+ self.send("#{k}=", v)
28
+ end
29
+ end
30
+
31
+ def ==(other)
32
+ return true
33
+ equal = true
34
+ [:description, :quantity, :unit_amount, :tax_type, :tax_amount, :line_amount, :account_code, :tracking_category, :tracking_option].each do |field|
35
+ equal &&= (send(field) == other.send(field))
36
+ end
37
+ return equal
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,59 @@
1
+ # Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ module XeroGateway
16
+ module Messages
17
+ class AccountMessage
18
+
19
+ # Take an Account element and convert it into an Account object
20
+ def self.from_xml(account_element)
21
+ account = Account.new
22
+ account_element.children.each do |element|
23
+ case(element.name)
24
+ when "Code" then account.code = element.text
25
+ when "Name" then account.name = element.text
26
+ when "Type" then account.type = element.text
27
+ when "TaxType" then account.tax_type = element.text
28
+ when "Description" then account.description = element.text
29
+ end
30
+ end
31
+ account
32
+ end
33
+
34
+ private
35
+
36
+ def self.parse_line_item(line_item_element)
37
+ line_item = LineItem.new
38
+ line_item_element.children.each do |element|
39
+ case(element.name)
40
+ when "LineItemID" then line_item.id = element.text
41
+ when "Description" then line_item.description = element.text
42
+ when "Quantity" then line_item.quantity = element.text.to_i
43
+ when "UnitAmount" then line_item.unit_amount = BigDecimal.new(element.text)
44
+ when "TaxType" then line_item.tax_type = element.text
45
+ when "TaxAmount" then line_item.tax_amount = BigDecimal.new(element.text)
46
+ when "LineAmount" then line_item.line_amount = BigDecimal.new(element.text)
47
+ when "AccountCode" then line_item.account_code = element.text
48
+ when "Tracking" then
49
+ if element.elements['TrackingCategory']
50
+ line_item.tracking_category = element.elements['TrackingCategory/Name'].text
51
+ line_item.tracking_option = element.elements['TrackingCategory/Option'].text
52
+ end
53
+ end
54
+ end
55
+ line_item
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,107 @@
1
+ # Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ module XeroGateway
16
+ module Messages
17
+ class ContactMessage
18
+ include Dates
19
+
20
+ def self.build_xml(contact)
21
+ b = Builder::XmlMarkup.new
22
+
23
+ b.Contact {
24
+ b.ContactID contact.id if contact.id
25
+ b.ContactNumber contact.contact_number if contact.contact_number
26
+ b.Name contact.name
27
+ b.EmailAddress contact.email if contact.email
28
+ b.Addresses {
29
+ contact.addresses.each do |address|
30
+ b.Address {
31
+ b.AddressType address.address_type
32
+ b.AddressLine1 address.line_1 if address.line_1
33
+ b.AddressLine2 address.line_2 if address.line_2
34
+ b.AddressLine3 address.line_3 if address.line_3
35
+ b.AddressLine4 address.line_4 if address.line_4
36
+ b.City address.city if address.city
37
+ b.Region address.region if address.region
38
+ b.PostalCode address.post_code if address.post_code
39
+ b.Country address.country if address.country
40
+ }
41
+ end
42
+ }
43
+ b.Phones {
44
+ contact.phones.each do |phone|
45
+ b.Phone {
46
+ b.PhoneType phone.phone_type
47
+ b.PhoneNumber phone.number
48
+ b.PhoneAreaCode phone.area_code if phone.area_code
49
+ b.PhoneCountryCode phone.country_code if phone.country_code
50
+ }
51
+ end
52
+ }
53
+ }
54
+ end
55
+
56
+ # Take a Contact element and convert it into an Contact object
57
+ def self.from_xml(contact_element)
58
+ contact = Contact.new
59
+ contact_element.children.each do |element|
60
+ case(element.name)
61
+ when "ContactID" then contact.id = element.text
62
+ when "ContactNumber" then contact.contact_number = element.text
63
+ when "ContactStatus" then contact.status = element.text
64
+ when "Name" then contact.name = element.text
65
+ when "EmailAddress" then contact.email = element.text
66
+ when "Addresses" then element.children.each {|address| contact.addresses << parse_address(address)}
67
+ when "Phones" then element.children.each {|phone| contact.phones << parse_phone(phone)}
68
+ end
69
+ end
70
+ contact
71
+ end
72
+
73
+ private
74
+
75
+ def self.parse_address(address_element)
76
+ address = Address.new
77
+ address_element.children.each do |element|
78
+ case(element.name)
79
+ when "AddressType" then address.address_type = element.text
80
+ when "AddressLine1" then address.line_1 = element.text
81
+ when "AddressLine2" then address.line_2 = element.text
82
+ when "AddressLine3" then address.line_3 = element.text
83
+ when "AddressLine4" then address.line_4 = element.text
84
+ when "City" then address.city = element.text
85
+ when "Region" then address.region = element.text
86
+ when "PostalCode" then address.post_code = element.text
87
+ when "Country" then address.country = element.text
88
+ end
89
+ end
90
+ address
91
+ end
92
+
93
+ def self.parse_phone(phone_element)
94
+ phone = Phone.new
95
+ phone_element.children.each do |element|
96
+ case(element.name)
97
+ when "PhoneType" then phone.phone_type = element.text
98
+ when "PhoneNumber" then phone.number = element.text
99
+ when "PhoneAreaCode" then phone.area_code = element.text
100
+ when "PhoneCountryCode" then phone.country_code = element.text
101
+ end
102
+ end
103
+ phone
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,136 @@
1
+ # Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ module XeroGateway
16
+ module Messages
17
+ class InvoiceMessage
18
+ include Dates
19
+ include Money
20
+
21
+ def self.build_xml(invoice)
22
+ b = Builder::XmlMarkup.new
23
+
24
+ b.Invoice {
25
+ b.InvoiceType invoice.invoice_type
26
+ b.Contact {
27
+ b.ContactID invoice.contact.id if invoice.contact.id
28
+ b.Name invoice.contact.name
29
+ b.EmailAddress invoice.contact.email if invoice.contact.email
30
+ b.Addresses {
31
+ invoice.contact.addresses.each do |address|
32
+ b.Address {
33
+ b.AddressType address.address_type
34
+ b.AddressLine1 address.line_1 if address.line_1
35
+ b.AddressLine2 address.line_2 if address.line_2
36
+ b.AddressLine3 address.line_3 if address.line_3
37
+ b.AddressLine4 address.line_4 if address.line_4
38
+ b.City address.city if address.city
39
+ b.Region address.region if address.region
40
+ b.PostalCode address.post_code if address.post_code
41
+ b.Country address.country if address.country
42
+ }
43
+ end
44
+ }
45
+ b.Phones {
46
+ invoice.contact.phones.each do |phone|
47
+ b.Phone {
48
+ b.PhoneType phone.phone_type
49
+ b.PhoneNumber phone.number
50
+ b.PhoneAreaCode phone.area_code if phone.area_code
51
+ b.PhoneCountryCode phone.country_code if phone.country_code
52
+ }
53
+ end
54
+ }
55
+ }
56
+ b.InvoiceDate format_date_time(invoice.date)
57
+ b.DueDate format_date_time(invoice.due_date) if invoice.due_date
58
+ b.InvoiceNumber invoice.invoice_number
59
+ b.Reference invoice.reference if invoice.reference
60
+ b.TaxInclusive invoice.tax_inclusive if invoice.tax_inclusive
61
+ b.IncludesTax invoice.includes_tax
62
+ b.SubTotal format_money(invoice.sub_total) if invoice.sub_total
63
+ b.TotalTax format_money(invoice.total_tax) if invoice.total_tax
64
+ b.Total format_money(invoice.total) if invoice.total
65
+ b.LineItems {
66
+ invoice.line_items.each do |line_item|
67
+ b.LineItem {
68
+ b.Description line_item.description
69
+ b.Quantity line_item.quantity if line_item.quantity
70
+ b.UnitAmount format_money(line_item.unit_amount)
71
+ b.TaxType line_item.tax_type if line_item.tax_type
72
+ b.TaxAmount format_money(line_item.tax_amount) if line_item.tax_amount
73
+ b.LineAmount format_money(line_item.line_amount)
74
+ b.AccountCode line_item.account_code || 200
75
+ b.Tracking {
76
+ b.TrackingCategory {
77
+ b.Name line_item.tracking_category
78
+ b.Option line_item.tracking_option
79
+ }
80
+ }
81
+ }
82
+ end
83
+ }
84
+ }
85
+ end
86
+
87
+ # Take an Invoice element and convert it into an Invoice object
88
+ def self.from_xml(invoice_element)
89
+ invoice = Invoice.new
90
+ invoice_element.children.each do |element|
91
+ case(element.name)
92
+ when "InvoiceStatus" then invoice.invoice_status = element.text
93
+ when "InvoiceID" then invoice.id = element.text
94
+ when "InvoiceNumber" then invoice.invoice_number = element.text
95
+ when "InvoiceType" then invoice.invoice_type = element.text
96
+ when "InvoiceDate" then invoice.date = parse_date_time(element.text)
97
+ when "DueDate" then invoice.due_date = parse_date_time(element.text)
98
+ when "Reference" then invoice.reference = element.text
99
+ when "TaxInclusive" then invoice.tax_inclusive = (element.text == "true")
100
+ when "IncludesTax" then invoice.includes_tax = (element.text == "true")
101
+ when "SubTotal" then invoice.sub_total = BigDecimal.new(element.text)
102
+ when "TotalTax" then invoice.total_tax = BigDecimal.new(element.text)
103
+ when "Total" then invoice.total = BigDecimal.new(element.text)
104
+ when "Contact" then invoice.contact = ContactMessage.from_xml(element)
105
+ when "LineItems" then element.children.each {|line_item| invoice.line_items << parse_line_item(line_item)}
106
+ end
107
+ end
108
+ invoice
109
+ end
110
+
111
+ private
112
+
113
+ def self.parse_line_item(line_item_element)
114
+ line_item = LineItem.new
115
+ line_item_element.children.each do |element|
116
+ case(element.name)
117
+ when "LineItemID" then line_item.id = element.text
118
+ when "Description" then line_item.description = element.text
119
+ when "Quantity" then line_item.quantity = element.text.to_i
120
+ when "UnitAmount" then line_item.unit_amount = BigDecimal.new(element.text)
121
+ when "TaxType" then line_item.tax_type = element.text
122
+ when "TaxAmount" then line_item.tax_amount = BigDecimal.new(element.text)
123
+ when "LineAmount" then line_item.line_amount = BigDecimal.new(element.text)
124
+ when "AccountCode" then line_item.account_code = element.text
125
+ when "Tracking" then
126
+ if element.elements['TrackingCategory']
127
+ line_item.tracking_category = element.elements['TrackingCategory/Name'].text
128
+ line_item.tracking_option = element.elements['TrackingCategory/Option'].text
129
+ end
130
+ end
131
+ end
132
+ line_item
133
+ end
134
+ end
135
+ end
136
+ end