xeroizer 0.3.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -5
- data/VERSION +1 -1
- data/lib/xeroizer/models/invoice.rb +11 -5
- data/lib/xeroizer/models/manual_journal.rb +1 -1
- data/lib/xeroizer/models/manual_journal_line.rb +3 -0
- data/lib/xeroizer/partner_application.rb +9 -7
- data/lib/xeroizer/record/base.rb +1 -0
- data/lib/xeroizer/record/record_association_helper.rb +6 -0
- data/lib/xeroizer/record/xml_helper.rb +3 -4
- data/test/stub_responses/invoices.xml +46 -0
- data/test/stub_responses/records/invoice-762aa45d-4632-45b5-8087-b4f47690665e.xml +54 -0
- data/test/unit/models/invoice_test.rb +35 -0
- data/test/unit/record/record_association_test.rb +26 -2
- data/xeroizer.gemspec +3 -2
- metadata +5 -4
data/README.md
CHANGED
@@ -58,12 +58,12 @@ Authentication occcurs in 3 steps:
|
|
58
58
|
|
59
59
|
client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
|
60
60
|
|
61
|
-
# 1. Get a RequestToken from Xero.
|
61
|
+
# 1. Get a RequestToken from Xero. :oauth_callback is the URL the user will be redirected to
|
62
62
|
# after they have authenticated your application.
|
63
63
|
#
|
64
64
|
# Note: The callback URL's domain must match that listed for your application in http://api.xero.com
|
65
65
|
# otherwise the user will not be redirected and only be shown the authentication code.
|
66
|
-
request_token = client.request_token(:
|
66
|
+
request_token = client.request_token(:oauth_callback => 'http://yourapp.com/oauth/callback')
|
67
67
|
|
68
68
|
# 2. Redirect the user to the URL specified by the RequestToken.
|
69
69
|
#
|
@@ -90,7 +90,7 @@ You can now use the client to access the Xero API methods, e.g.
|
|
90
90
|
public
|
91
91
|
|
92
92
|
def new
|
93
|
-
request_token = @xero_client.request_token(:
|
93
|
+
request_token = @xero_client.request_token(:oauth_callback => 'http://yourapp.com/xero_session/create')
|
94
94
|
session[:request_token] = request_token.token
|
95
95
|
session[:request_secret] = request_token.secret
|
96
96
|
|
@@ -194,12 +194,12 @@ Authentication occcurs in 3 steps:
|
|
194
194
|
"/path/to/entrust-private-nopass.pem"
|
195
195
|
)
|
196
196
|
|
197
|
-
# 1. Get a RequestToken from Xero.
|
197
|
+
# 1. Get a RequestToken from Xero. :oauth_callback is the URL the user will be redirected to
|
198
198
|
# after they have authenticated your application.
|
199
199
|
#
|
200
200
|
# Note: The callback URL's domain must match that listed for your application in http://api.xero.com
|
201
201
|
# otherwise the user will not be redirected and only be shown the authentication code.
|
202
|
-
request_token = client.request_token(:
|
202
|
+
request_token = client.request_token(:oauth_callback => 'http://yourapp.com/oauth/callback')
|
203
203
|
|
204
204
|
# 2. Redirect the user to the URL specified by the RequestToken.
|
205
205
|
#
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.5
|
@@ -77,15 +77,21 @@ module Xeroizer
|
|
77
77
|
has_many :payments
|
78
78
|
has_many :credit_notes
|
79
79
|
|
80
|
-
validates_presence_of :date, :due_date
|
80
|
+
validates_presence_of :date, :due_date, :unless => proc { |invoice| invoice.new_record? }
|
81
81
|
validates_inclusion_of :type, :in => INVOICE_TYPES
|
82
|
-
validates_inclusion_of :status, :in => INVOICE_STATUSES
|
83
|
-
validates_inclusion_of :line_amount_types, :in => LINE_AMOUNT_TYPES
|
82
|
+
validates_inclusion_of :status, :in => INVOICE_STATUSES, :unless => proc { |invoice| invoice.new_record? }
|
83
|
+
validates_inclusion_of :line_amount_types, :in => LINE_AMOUNT_TYPES, :unless => proc { |invoice| invoice.new_record? }
|
84
84
|
validates_associated :contact
|
85
|
-
validates_associated :line_items
|
85
|
+
validates_associated :line_items, :allow_blanks => true, :unless => proc { |invoice| invoice.approved? }
|
86
|
+
validates_associated :line_items, :if => proc { |invoice| invoice.approved? }
|
86
87
|
|
87
88
|
public
|
88
|
-
|
89
|
+
|
90
|
+
# Helper method to check if the invoice has been approved.
|
91
|
+
def approved?
|
92
|
+
[ 'AUTHORISED', 'PAID', 'VOIDED' ].include? status
|
93
|
+
end
|
94
|
+
|
89
95
|
# Helper method to check if the invoice is accounts payable.
|
90
96
|
def accounts_payable?
|
91
97
|
type == 'ACCPAY'
|
@@ -18,14 +18,16 @@ module Xeroizer
|
|
18
18
|
# @param [Hash] options other options to pass to the GenericApplication constructor
|
19
19
|
# @return [PartnerApplication] instance of PrivateApplication
|
20
20
|
def initialize(consumer_key, consumer_secret, path_to_private_key, path_to_ssl_client_cert, path_to_ssl_client_key, options = {})
|
21
|
-
|
22
|
-
:xero_url
|
23
|
-
:site
|
24
|
-
:authorize_url
|
25
|
-
:signature_method => 'RSA-SHA1'
|
21
|
+
default_options = {
|
22
|
+
:xero_url => 'https://api-partner.network.xero.com/api.xro/2.0',
|
23
|
+
:site => 'https://api-partner.network.xero.com',
|
24
|
+
:authorize_url => 'https://api.xero.com/oauth/Authorize',
|
25
|
+
:signature_method => 'RSA-SHA1'
|
26
|
+
}
|
27
|
+
options = default_options.merge(options).merge(
|
26
28
|
:private_key_file => path_to_private_key,
|
27
|
-
:ssl_client_cert
|
28
|
-
:ssl_client_key
|
29
|
+
:ssl_client_cert => OpenSSL::X509::Certificate.new(File.read(path_to_ssl_client_cert)),
|
30
|
+
:ssl_client_key => OpenSSL::PKey::RSA.new(File.read(path_to_ssl_client_key))
|
29
31
|
)
|
30
32
|
super(consumer_key, consumer_secret, options)
|
31
33
|
|
data/lib/xeroizer/record/base.rb
CHANGED
@@ -61,6 +61,9 @@ module Xeroizer
|
|
61
61
|
raise StandardError.new("Invalid arguments for #{self.class.name}#add_#{internal_singular_field_name}(#{args.inspect}).")
|
62
62
|
end
|
63
63
|
|
64
|
+
# Ensure that complete record is downloaded before adding new records
|
65
|
+
self.send(field_name)
|
66
|
+
|
64
67
|
# Add each record.
|
65
68
|
record = nil
|
66
69
|
records.each do | record |
|
@@ -109,6 +112,9 @@ module Xeroizer
|
|
109
112
|
when record_class
|
110
113
|
self.attributes[field_name] = ((association_type == :has_many) ? [value] : value)
|
111
114
|
|
115
|
+
when NilClass
|
116
|
+
self.attributes[field_name] = []
|
117
|
+
|
112
118
|
else
|
113
119
|
raise AssociationTypeMismatch.new(record_class, value.class)
|
114
120
|
end
|
@@ -58,10 +58,9 @@ module Xeroizer
|
|
58
58
|
def to_xml(b = Builder::XmlMarkup.new(:indent => 2))
|
59
59
|
b.tag!(parent.class.xml_node_name || parent.model_name) {
|
60
60
|
attributes.each do | key, value |
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
61
|
+
field = self.class.fields[key]
|
62
|
+
value = self.send(key) if field[:calculated]
|
63
|
+
xml_value_from_field(b, field, value) unless value.nil?
|
65
64
|
end
|
66
65
|
}
|
67
66
|
end
|
@@ -1849,5 +1849,51 @@
|
|
1849
1849
|
<AmountDue>0.00</AmountDue>
|
1850
1850
|
<AmountPaid>1500.00</AmountPaid>
|
1851
1851
|
</Invoice>
|
1852
|
+
<Invoice>
|
1853
|
+
<Contact>
|
1854
|
+
<ContactID>9601cac0-db41-4255-8cc6-f38b555ce53f</ContactID>
|
1855
|
+
<ContactStatus>ACTIVE</ContactStatus>
|
1856
|
+
<Name>ABC</Name>
|
1857
|
+
<Addresses>
|
1858
|
+
<Address>
|
1859
|
+
<AddressType>STREET</AddressType>
|
1860
|
+
</Address>
|
1861
|
+
<Address>
|
1862
|
+
<AddressType>POBOX</AddressType>
|
1863
|
+
</Address>
|
1864
|
+
</Addresses>
|
1865
|
+
<Phones>
|
1866
|
+
<Phone>
|
1867
|
+
<PhoneType>MOBILE</PhoneType>
|
1868
|
+
</Phone>
|
1869
|
+
<Phone>
|
1870
|
+
<PhoneType>DEFAULT</PhoneType>
|
1871
|
+
</Phone>
|
1872
|
+
<Phone>
|
1873
|
+
<PhoneType>DDI</PhoneType>
|
1874
|
+
</Phone>
|
1875
|
+
<Phone>
|
1876
|
+
<PhoneType>FAX</PhoneType>
|
1877
|
+
</Phone>
|
1878
|
+
</Phones>
|
1879
|
+
<UpdatedDateUTC>2011-06-21T09:22:54.097</UpdatedDateUTC>
|
1880
|
+
<IsSupplier>false</IsSupplier>
|
1881
|
+
<IsCustomer>true</IsCustomer>
|
1882
|
+
</Contact>
|
1883
|
+
<Date>2011-06-21T00:00:00</Date>
|
1884
|
+
<Status>DRAFT</Status>
|
1885
|
+
<LineAmountTypes>Exclusive</LineAmountTypes>
|
1886
|
+
<SubTotal>0.00</SubTotal>
|
1887
|
+
<TotalTax>0.00</TotalTax>
|
1888
|
+
<Total>0.00</Total>
|
1889
|
+
<UpdatedDateUTC>2011-06-21T09:22:54.143</UpdatedDateUTC>
|
1890
|
+
<CurrencyCode>NZD</CurrencyCode>
|
1891
|
+
<Type>ACCREC</Type>
|
1892
|
+
<InvoiceID>762aa45d-4632-45b5-8087-b4f47690665e</InvoiceID>
|
1893
|
+
<InvoiceNumber>INV-0039</InvoiceNumber>
|
1894
|
+
<AmountDue>0.00</AmountDue>
|
1895
|
+
<AmountPaid>0.00</AmountPaid>
|
1896
|
+
<SentToContact>false</SentToContact>
|
1897
|
+
</Invoice>
|
1852
1898
|
</Invoices>
|
1853
1899
|
</Response>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<Response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
2
|
+
<Id>8837eff8-5b9d-4788-a735-d61dd5d56a56</Id>
|
3
|
+
<Status>OK</Status>
|
4
|
+
<ProviderName>Xero API Previewer</ProviderName>
|
5
|
+
<DateTimeUTC>2011-06-21T09:22:54.1911951Z</DateTimeUTC>
|
6
|
+
<Invoices>
|
7
|
+
<Invoice>
|
8
|
+
<Contact>
|
9
|
+
<ContactID>9601cac0-db41-4255-8cc6-f38b555ce53f</ContactID>
|
10
|
+
<ContactStatus>ACTIVE</ContactStatus>
|
11
|
+
<Name>ABC</Name>
|
12
|
+
<Addresses>
|
13
|
+
<Address>
|
14
|
+
<AddressType>STREET</AddressType>
|
15
|
+
</Address>
|
16
|
+
<Address>
|
17
|
+
<AddressType>POBOX</AddressType>
|
18
|
+
</Address>
|
19
|
+
</Addresses>
|
20
|
+
<Phones>
|
21
|
+
<Phone>
|
22
|
+
<PhoneType>MOBILE</PhoneType>
|
23
|
+
</Phone>
|
24
|
+
<Phone>
|
25
|
+
<PhoneType>DEFAULT</PhoneType>
|
26
|
+
</Phone>
|
27
|
+
<Phone>
|
28
|
+
<PhoneType>DDI</PhoneType>
|
29
|
+
</Phone>
|
30
|
+
<Phone>
|
31
|
+
<PhoneType>FAX</PhoneType>
|
32
|
+
</Phone>
|
33
|
+
</Phones>
|
34
|
+
<UpdatedDateUTC>2011-06-21T09:22:54.097</UpdatedDateUTC>
|
35
|
+
<IsSupplier>false</IsSupplier>
|
36
|
+
<IsCustomer>true</IsCustomer>
|
37
|
+
</Contact>
|
38
|
+
<Date>2011-06-21T00:00:00</Date>
|
39
|
+
<Status>DRAFT</Status>
|
40
|
+
<LineAmountTypes>Exclusive</LineAmountTypes>
|
41
|
+
<SubTotal>0.00</SubTotal>
|
42
|
+
<TotalTax>0.00</TotalTax>
|
43
|
+
<Total>0.00</Total>
|
44
|
+
<UpdatedDateUTC>2011-06-21T09:22:54.143</UpdatedDateUTC>
|
45
|
+
<CurrencyCode>NZD</CurrencyCode>
|
46
|
+
<Type>ACCREC</Type>
|
47
|
+
<InvoiceID>762aa45d-4632-45b5-8087-b4f47690665e</InvoiceID>
|
48
|
+
<InvoiceNumber>INV-0039</InvoiceNumber>
|
49
|
+
<AmountDue>0.00</AmountDue>
|
50
|
+
<AmountPaid>0.00</AmountPaid>
|
51
|
+
<SentToContact>false</SentToContact>
|
52
|
+
</Invoice>
|
53
|
+
</Invoices>
|
54
|
+
</Response>
|
@@ -48,4 +48,39 @@ class InvoiceTest < Test::Unit::TestCase
|
|
48
48
|
|
49
49
|
end
|
50
50
|
|
51
|
+
context "invoice validations" do
|
52
|
+
|
53
|
+
should "build an invalid invoice if there are no attributes" do
|
54
|
+
assert_equal(false, @client.Invoice.build.valid?)
|
55
|
+
end
|
56
|
+
|
57
|
+
should "build a valid DRAFT invoice with minimal attributes" do
|
58
|
+
invoice = @client.Invoice.build :type => "ACCREC", :contact => { :name => "ABC Limited" }
|
59
|
+
assert_equal(true, invoice.valid?)
|
60
|
+
end
|
61
|
+
|
62
|
+
should "build a invalid AUTHORISED invoice with minimal attributes" do
|
63
|
+
invoice = @client.Invoice.build :type => "ACCREC", :contact => { :name => "ABC Limited" }, :status => "AUTHORISED"
|
64
|
+
assert_equal(false, invoice.valid?)
|
65
|
+
end
|
66
|
+
|
67
|
+
should "build a valid AUTHORISED invoice with complete attributes" do
|
68
|
+
invoice = @client.Invoice.build({
|
69
|
+
:type => "ACCREC",
|
70
|
+
:contact => { :name => "ABC Limited" },
|
71
|
+
:status => "AUTHORISED",
|
72
|
+
:date => Date.today,
|
73
|
+
:due_date => Date.today,
|
74
|
+
:line_items => [{
|
75
|
+
:description => "Consulting services as agreed",
|
76
|
+
:quantity => 5,
|
77
|
+
:unit_amount => 120,
|
78
|
+
:account_code => 200
|
79
|
+
}]
|
80
|
+
})
|
81
|
+
assert_equal(true, invoice.valid?)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
51
86
|
end
|
@@ -5,8 +5,8 @@ class RecordAssociationTest < Test::Unit::TestCase
|
|
5
5
|
|
6
6
|
def setup
|
7
7
|
@client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET)
|
8
|
-
|
9
|
-
@client.stubs(:
|
8
|
+
mock_api('Invoices')
|
9
|
+
@client.stubs(:http_put).returns(get_record_xml(:invoice, "762aa45d-4632-45b5-8087-b4f47690665e"))
|
10
10
|
end
|
11
11
|
|
12
12
|
context "belongs_to association" do
|
@@ -64,6 +64,30 @@ class RecordAssociationTest < Test::Unit::TestCase
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
should "retain unsaved items after create" do
|
68
|
+
invoice = @client.Invoice.build :type => "ACCREC", :contact => { :name => "A" }
|
69
|
+
invoice.save
|
70
|
+
invoice.add_line_item :description => "1"
|
71
|
+
assert_equal(1, invoice.line_items.size, "There should be one line item.")
|
72
|
+
end
|
73
|
+
|
74
|
+
should "retain unsaved items after find" do
|
75
|
+
invoice = @client.Invoice.find "762aa45d-4632-45b5-8087-b4f47690665e"
|
76
|
+
invoice.add_line_item :description => "1"
|
77
|
+
assert_equal(1, invoice.line_items.size, "There should be one line item.")
|
78
|
+
end
|
79
|
+
|
80
|
+
should "retain unsaved items after summary find" do
|
81
|
+
invoice = @client.Invoice.all.last
|
82
|
+
invoice.add_line_item :description => "1"
|
83
|
+
assert_equal(1, invoice.line_items.size, "There should be one line item.")
|
84
|
+
end
|
85
|
+
|
86
|
+
should "retain unsaved items when set explicitly" do
|
87
|
+
invoice = @client.Invoice.all.last
|
88
|
+
invoice.line_items = [{ :description => "1" }]
|
89
|
+
assert_equal(1, invoice.line_items.size, "There should be one line item.")
|
90
|
+
end
|
67
91
|
end
|
68
92
|
|
69
93
|
end
|
data/xeroizer.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{xeroizer}
|
8
|
-
s.version = "0.3.
|
8
|
+
s.version = "0.3.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Wayne Robinson"]
|
12
|
-
s.date = %q{2011-06-
|
12
|
+
s.date = %q{2011-06-27}
|
13
13
|
s.description = %q{Ruby library for the Xero accounting system API.}
|
14
14
|
s.email = %q{wayne.robinson@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -257,6 +257,7 @@ Gem::Specification.new do |s|
|
|
257
257
|
"test/stub_responses/records/invoice-673dd7cc-beb7-4697-83d4-0c47cb400cc2.xml",
|
258
258
|
"test/stub_responses/records/invoice-69fc971e-9b37-41c5-9c87-174330f22343.xml",
|
259
259
|
"test/stub_responses/records/invoice-70e6db69-e5a4-42c7-a397-aa3212c2945f.xml",
|
260
|
+
"test/stub_responses/records/invoice-762aa45d-4632-45b5-8087-b4f47690665e.xml",
|
260
261
|
"test/stub_responses/records/invoice-766d1289-b440-4675-a656-1a0612ecac77.xml",
|
261
262
|
"test/stub_responses/records/invoice-76bcb361-f93b-4513-b312-5a4af306d276.xml",
|
262
263
|
"test/stub_responses/records/invoice-76e3f056-479f-417c-a72b-f3d767899b87.xml",
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xeroizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 5
|
10
|
+
version: 0.3.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Wayne Robinson
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
18
|
+
date: 2011-06-27 00:00:00 +10:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -416,6 +416,7 @@ files:
|
|
416
416
|
- test/stub_responses/records/invoice-673dd7cc-beb7-4697-83d4-0c47cb400cc2.xml
|
417
417
|
- test/stub_responses/records/invoice-69fc971e-9b37-41c5-9c87-174330f22343.xml
|
418
418
|
- test/stub_responses/records/invoice-70e6db69-e5a4-42c7-a397-aa3212c2945f.xml
|
419
|
+
- test/stub_responses/records/invoice-762aa45d-4632-45b5-8087-b4f47690665e.xml
|
419
420
|
- test/stub_responses/records/invoice-766d1289-b440-4675-a656-1a0612ecac77.xml
|
420
421
|
- test/stub_responses/records/invoice-76bcb361-f93b-4513-b312-5a4af306d276.xml
|
421
422
|
- test/stub_responses/records/invoice-76e3f056-479f-417c-a72b-f3d767899b87.xml
|