xero_gateway 2.0.14 → 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/Rakefile +1 -1
- data/lib/xero_gateway/accounts_list.rb +0 -4
- data/lib/xero_gateway/bank_transaction.rb +5 -5
- data/lib/xero_gateway/contact.rb +3 -6
- data/lib/xero_gateway/credit_note.rb +1 -3
- data/lib/xero_gateway/exceptions.rb +5 -0
- data/lib/xero_gateway/gateway.rb +83 -0
- data/lib/xero_gateway/http.rb +1 -0
- data/lib/xero_gateway/invoice.rb +2 -4
- data/lib/xero_gateway/journal_line.rb +102 -0
- data/lib/xero_gateway/line_item_calculations.rb +0 -4
- data/lib/xero_gateway/manual_journal.rb +163 -0
- data/lib/xero_gateway/response.rb +2 -0
- data/lib/xero_gateway.rb +2 -0
- data/test/integration/accounts_list_test.rb +4 -4
- data/test/integration/create_manual_journal_test.rb +35 -0
- data/test/integration/get_bank_transaction_test.rb +1 -0
- data/test/integration/get_manual_journal_test.rb +50 -0
- data/test/integration/get_manual_journals_test.rb +88 -0
- data/test/integration/update_manual_journal_test.rb +31 -0
- data/test/test_helper.rb +38 -0
- data/test/unit/bank_transaction_test.rb +13 -1
- data/test/unit/credit_note_test.rb +1 -1
- data/test/unit/gateway_test.rb +26 -2
- data/test/unit/invoice_test.rb +1 -1
- data/test/unit/manual_journal_test.rb +93 -0
- data/xero_gateway.gemspec +2 -2
- metadata +82 -41
data/Rakefile
CHANGED
@@ -3,8 +3,6 @@ module XeroGateway
|
|
3
3
|
include Dates
|
4
4
|
include LineItemCalculations
|
5
5
|
|
6
|
-
class NoGatewayError < Error; end
|
7
|
-
|
8
6
|
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
7
|
|
10
8
|
TYPES = {
|
@@ -27,7 +25,7 @@ module XeroGateway
|
|
27
25
|
attr_accessor :line_items_downloaded
|
28
26
|
|
29
27
|
# accessible fields
|
30
|
-
attr_accessor :bank_transaction_id, :type, :date, :reference, :status, :contact, :line_items, :bank_account, :url
|
28
|
+
attr_accessor :bank_transaction_id, :type, :date, :reference, :status, :contact, :line_items, :bank_account, :url, :is_reconciled
|
31
29
|
|
32
30
|
def initialize(params = {})
|
33
31
|
@errors ||= []
|
@@ -128,11 +126,12 @@ module XeroGateway
|
|
128
126
|
b.BankTransactionID bank_transaction_id if bank_transaction_id
|
129
127
|
b.Type type
|
130
128
|
# b.CurrencyCode self.currency_code if self.currency_code
|
131
|
-
contact.to_xml(b)
|
132
|
-
bank_account.to_xml(b, :name => 'BankAccount')
|
129
|
+
contact.to_xml(b) if contact
|
130
|
+
bank_account.to_xml(b, :name => 'BankAccount') if bank_account
|
133
131
|
b.Date BankTransaction.format_date(date || Date.today)
|
134
132
|
b.Status status if status
|
135
133
|
b.Reference reference if reference
|
134
|
+
b.IsReconciled true if self.is_reconciled
|
136
135
|
b.LineItems {
|
137
136
|
self.line_items.each do |line_item|
|
138
137
|
line_item.to_xml(b)
|
@@ -165,6 +164,7 @@ module XeroGateway
|
|
165
164
|
# when "AmountPaid" then invoice.amount_paid = BigDecimal.new(element.text)
|
166
165
|
# when "AmountCredited" then invoice.amount_credited = BigDecimal.new(element.text)
|
167
166
|
# when "SentToContact" then invoice.sent_to_contact = (element.text.strip.downcase == "true")
|
167
|
+
when "IsReconciled" then bank_transaction.is_reconciled = (element.text.strip.downcase == "true")
|
168
168
|
when "Url" then bank_transaction.url = element.text
|
169
169
|
end
|
170
170
|
end
|
data/lib/xero_gateway/contact.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
module XeroGateway
|
2
2
|
class Contact
|
3
3
|
include Dates
|
4
|
-
|
5
|
-
class Error < RuntimeError; end
|
6
|
-
class NoGatewayError < Error; end
|
7
|
-
|
4
|
+
|
8
5
|
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
6
|
|
10
7
|
CONTACT_STATUS = {
|
@@ -126,14 +123,14 @@ module XeroGateway
|
|
126
123
|
end
|
127
124
|
|
128
125
|
# Creates this contact record (using gateway.create_contact) with the associated gateway.
|
129
|
-
# If no gateway set, raise a
|
126
|
+
# If no gateway set, raise a NoGatewayError exception.
|
130
127
|
def create
|
131
128
|
raise NoGatewayError unless gateway
|
132
129
|
gateway.create_contact(self)
|
133
130
|
end
|
134
131
|
|
135
132
|
# Creates this contact record (using gateway.update_contact) with the associated gateway.
|
136
|
-
# If no gateway set, raise a
|
133
|
+
# If no gateway set, raise a NoGatewayError exception.
|
137
134
|
def update
|
138
135
|
raise NoGatewayError unless gateway
|
139
136
|
gateway.update_contact(self)
|
@@ -4,8 +4,6 @@ module XeroGateway
|
|
4
4
|
include Money
|
5
5
|
include LineItemCalculations
|
6
6
|
|
7
|
-
class NoGatewayError < Error; end
|
8
|
-
|
9
7
|
CREDIT_NOTE_TYPE = {
|
10
8
|
'ACCRECCREDIT' => 'Accounts Receivable',
|
11
9
|
'ACCPAYCREDIT' => 'Accounts Payable'
|
@@ -163,7 +161,7 @@ module XeroGateway
|
|
163
161
|
end
|
164
162
|
|
165
163
|
# Creates this credit_note record (using gateway.create_credit_note) with the associated gateway.
|
166
|
-
# If no gateway set, raise a
|
164
|
+
# If no gateway set, raise a NoGatewayError exception.
|
167
165
|
def create
|
168
166
|
raise NoGatewayError unless gateway
|
169
167
|
gateway.create_credit_note(self)
|
@@ -1,4 +1,8 @@
|
|
1
1
|
module XeroGateway
|
2
|
+
class NoGatewayError < StandardError; end
|
3
|
+
class AccountsListNotLoadedError < StandardError; end
|
4
|
+
class InvalidLineItemError < StandardError; end
|
5
|
+
|
2
6
|
class ApiException < StandardError
|
3
7
|
|
4
8
|
def initialize(type, message, request_xml, response_xml)
|
@@ -43,4 +47,5 @@ module XeroGateway
|
|
43
47
|
class InvoiceNotFoundError < StandardError; end
|
44
48
|
class BankTransactionNotFoundError < StandardError; end
|
45
49
|
class CreditNoteNotFoundError < StandardError; end
|
50
|
+
class ManualJournalNotFoundError < StandardError; end
|
46
51
|
end
|
data/lib/xero_gateway/gateway.rb
CHANGED
@@ -418,6 +418,56 @@ module XeroGateway
|
|
418
418
|
parse_response(response_xml, {:request_params => request_params}, {:request_signature => 'GET/BankTransaction'})
|
419
419
|
end
|
420
420
|
|
421
|
+
# Creates a manual journal in Xero based on a manual journal object.
|
422
|
+
#
|
423
|
+
# Manual journal and line item totals are calculated automatically.
|
424
|
+
#
|
425
|
+
# Usage : # TODO
|
426
|
+
|
427
|
+
def create_manual_journal(manual_journal)
|
428
|
+
save_manual_journal(manual_journal)
|
429
|
+
end
|
430
|
+
|
431
|
+
#
|
432
|
+
# Updates an existing Xero manual journal
|
433
|
+
#
|
434
|
+
# Usage :
|
435
|
+
#
|
436
|
+
# manual_journal = xero_gateway.get_manual_journal(some_manual_journal_id)
|
437
|
+
#
|
438
|
+
# xero_gateway.update_manual_journal(manual_journal)
|
439
|
+
def update_manual_journal(manual_journal)
|
440
|
+
raise "manual_journal_id is required for updating manual journals" if manual_journal.manual_journal_id.nil?
|
441
|
+
save_manual_journal(manual_journal)
|
442
|
+
end
|
443
|
+
|
444
|
+
# Retrieves all manual journals from Xero
|
445
|
+
#
|
446
|
+
# Usage : get_manual_journal
|
447
|
+
# getmanual_journal(:manual_journal_id => " 297c2dc5-cc47-4afd-8ec8-74990b8761e9")
|
448
|
+
#
|
449
|
+
# Note : modified_since is in UTC format (i.e. Brisbane is UTC+10)
|
450
|
+
def get_manual_journals(options = {})
|
451
|
+
request_params = {}
|
452
|
+
request_params[:ManualJournalID] = options[:manual_journal_id] if options[:manual_journal_id]
|
453
|
+
request_params[:ModifiedAfter] = options[:modified_since] if options[:modified_since]
|
454
|
+
|
455
|
+
response_xml = http_get(@client, "#{@xero_url}/ManualJournals", request_params)
|
456
|
+
|
457
|
+
parse_response(response_xml, {:request_params => request_params}, {:request_signature => 'GET/ManualJournals'})
|
458
|
+
end
|
459
|
+
|
460
|
+
# Retrieves a single manual journal
|
461
|
+
#
|
462
|
+
# Usage : get_manual_journal("297c2dc5-cc47-4afd-8ec8-74990b8761e9") # By ID
|
463
|
+
# get_manual_journal("OIT-12345") # By number
|
464
|
+
def get_manual_journal(manual_journal_id)
|
465
|
+
request_params = {}
|
466
|
+
url = "#{@xero_url}/ManualJournals/#{URI.escape(manual_journal_id)}"
|
467
|
+
response_xml = http_get(@client, url, request_params)
|
468
|
+
parse_response(response_xml, {:request_params => request_params}, {:request_signature => 'GET/ManualJournal'})
|
469
|
+
end
|
470
|
+
|
421
471
|
#
|
422
472
|
# Gets all accounts for a specific organization in Xero.
|
423
473
|
#
|
@@ -555,6 +605,33 @@ module XeroGateway
|
|
555
605
|
response
|
556
606
|
end
|
557
607
|
|
608
|
+
# Create or update a manual journal record based on if it has an manual_journal_id.
|
609
|
+
def save_manual_journal(manual_journal)
|
610
|
+
request_xml = manual_journal.to_xml
|
611
|
+
response_xml = nil
|
612
|
+
create_or_save = nil
|
613
|
+
|
614
|
+
if manual_journal.manual_journal_id.nil?
|
615
|
+
# Create new manual journal record.
|
616
|
+
response_xml = http_put(@client, "#{@xero_url}/ManualJournals", request_xml, {})
|
617
|
+
create_or_save = :create
|
618
|
+
else
|
619
|
+
# Update existing manual journal record.
|
620
|
+
response_xml = http_post(@client, "#{@xero_url}/ManualJournals", request_xml, {})
|
621
|
+
create_or_save = :save
|
622
|
+
end
|
623
|
+
|
624
|
+
response = parse_response(response_xml, {:request_xml => request_xml}, {:request_signature => "#{create_or_save == :create ? 'PUT' : 'POST'}/ManualJournals"})
|
625
|
+
|
626
|
+
# Xero returns manual journals inside an <ManualJournals> tag, even though there's only ever
|
627
|
+
# one for this request
|
628
|
+
response.response_item = response.manual_journals.first
|
629
|
+
|
630
|
+
manual_journal.manual_journal_id = response.manual_journal.manual_journal_id if response.success? && response.manual_journal && response.manual_journal.manual_journal_id
|
631
|
+
|
632
|
+
response
|
633
|
+
end
|
634
|
+
|
558
635
|
def parse_response(raw_response, request = {}, options = {})
|
559
636
|
|
560
637
|
response = XeroGateway::Response.new
|
@@ -576,12 +653,18 @@ module XeroGateway
|
|
576
653
|
when "Invoice" then response.response_item = Invoice.from_xml(element, self, {:line_items_downloaded => options[:request_signature] != "GET/Invoices"})
|
577
654
|
when "BankTransaction"
|
578
655
|
response.response_item = BankTransaction.from_xml(element, self, {:line_items_downloaded => options[:request_signature] != "GET/BankTransactions"})
|
656
|
+
when "ManualJournal"
|
657
|
+
response.response_item = ManualJournal.from_xml(element, self, {:journal_lines_downloaded => options[:request_signature] != "GET/ManualJournals"})
|
579
658
|
when "Contacts" then element.children.each {|child| response.response_item << Contact.from_xml(child, self) }
|
580
659
|
when "Invoices" then element.children.each {|child| response.response_item << Invoice.from_xml(child, self, {:line_items_downloaded => options[:request_signature] != "GET/Invoices"}) }
|
581
660
|
when "BankTransactions"
|
582
661
|
element.children.each do |child|
|
583
662
|
response.response_item << BankTransaction.from_xml(child, self, {:line_items_downloaded => options[:request_signature] != "GET/BankTransactions"})
|
584
663
|
end
|
664
|
+
when "ManualJournals"
|
665
|
+
element.children.each do |child|
|
666
|
+
response.response_item << ManualJournal.from_xml(child, self, {:journal_lines_downloaded => options[:request_signature] != "GET/ManualJournals"})
|
667
|
+
end
|
585
668
|
when "CreditNotes" then element.children.each {|child| response.response_item << CreditNote.from_xml(child, self, {:line_items_downloaded => options[:request_signature] != "GET/CreditNotes"}) }
|
586
669
|
when "Accounts" then element.children.each {|child| response.response_item << Account.from_xml(child) }
|
587
670
|
when "TaxRates" then element.children.each {|child| response.response_item << TaxRate.from_xml(child) }
|
data/lib/xero_gateway/http.rb
CHANGED
@@ -125,6 +125,7 @@ module XeroGateway
|
|
125
125
|
def handle_object_not_found!(response, request_url)
|
126
126
|
case(request_url)
|
127
127
|
when /Invoices/ then raise InvoiceNotFoundError.new("Invoice not found in Xero.")
|
128
|
+
when /BankTransactions/ then raise BankTransactionNotFoundError.new("Bank Transaction not found in Xero.")
|
128
129
|
when /CreditNotes/ then raise CreditNoteNotFoundError.new("Credit Note not found in Xero.")
|
129
130
|
else raise ObjectNotFound.new(request_url)
|
130
131
|
end
|
data/lib/xero_gateway/invoice.rb
CHANGED
@@ -4,8 +4,6 @@ module XeroGateway
|
|
4
4
|
include Money
|
5
5
|
include LineItemCalculations
|
6
6
|
|
7
|
-
class NoGatewayError < Error; end
|
8
|
-
|
9
7
|
INVOICE_TYPE = {
|
10
8
|
'ACCREC' => 'Accounts Receivable',
|
11
9
|
'ACCPAY' => 'Accounts Payable'
|
@@ -165,14 +163,14 @@ module XeroGateway
|
|
165
163
|
end
|
166
164
|
|
167
165
|
# Creates this invoice record (using gateway.create_invoice) with the associated gateway.
|
168
|
-
# If no gateway set, raise a
|
166
|
+
# If no gateway set, raise a NoGatewayError exception.
|
169
167
|
def create
|
170
168
|
raise NoGatewayError unless gateway
|
171
169
|
gateway.create_invoice(self)
|
172
170
|
end
|
173
171
|
|
174
172
|
# Updates this invoice record (using gateway.update_invoice) with the associated gateway.
|
175
|
-
# If no gateway set, raise a
|
173
|
+
# If no gateway set, raise a NoGatewayError exception.
|
176
174
|
def update
|
177
175
|
raise NoGatewayError unless gateway
|
178
176
|
gateway.update_invoice(self)
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'account')
|
2
|
+
|
3
|
+
module XeroGateway
|
4
|
+
class JournalLine
|
5
|
+
include Money
|
6
|
+
|
7
|
+
TAX_TYPE = Account::TAX_TYPE unless defined?(TAX_TYPE)
|
8
|
+
|
9
|
+
# Any errors that occurred when the #valid? method called.
|
10
|
+
attr_reader :errors
|
11
|
+
|
12
|
+
# All accessible fields
|
13
|
+
attr_accessor :journal_line_id, :line_amount, :account_code, :description, :tax_type, :tracking
|
14
|
+
|
15
|
+
def initialize(params = {})
|
16
|
+
@errors ||= []
|
17
|
+
@tracking ||= []
|
18
|
+
|
19
|
+
params.each do |k,v|
|
20
|
+
self.send("#{k}=", v)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Validate the JournalLineItem record according to what will be valid by the gateway.
|
25
|
+
#
|
26
|
+
# Usage:
|
27
|
+
# journal_line_item.valid? # Returns true/false
|
28
|
+
#
|
29
|
+
# Additionally sets journal_line_item.errors array to an array of field/error.
|
30
|
+
def valid?
|
31
|
+
@errors = []
|
32
|
+
|
33
|
+
if !journal_line_id.nil? && journal_line_id !~ GUID_REGEX
|
34
|
+
@errors << ['journal_line_id', 'must be blank or a valid Xero GUID']
|
35
|
+
end
|
36
|
+
|
37
|
+
unless line_amount
|
38
|
+
@errors << ['line_amount', "can't be blank"]
|
39
|
+
end
|
40
|
+
|
41
|
+
unless account_code
|
42
|
+
@errors << ['account_code', "can't be blank"]
|
43
|
+
end
|
44
|
+
|
45
|
+
@errors.size == 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_tracking?
|
49
|
+
return false if tracking.nil?
|
50
|
+
|
51
|
+
if tracking.is_a?(Array)
|
52
|
+
return tracking.any?
|
53
|
+
else
|
54
|
+
return tracking.is_a?(TrackingCategory)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_xml(b = Builder::XmlMarkup.new)
|
59
|
+
b.JournalLine {
|
60
|
+
b.LineAmount line_amount # mandatory
|
61
|
+
b.AccountCode account_code # mandatory
|
62
|
+
b.Description description if description # optional
|
63
|
+
b.TaxType tax_type if tax_type # optional
|
64
|
+
if has_tracking?
|
65
|
+
b.Tracking { # optional
|
66
|
+
# Due to strange retardness in the Xero API, the XML structure for a tracking category within
|
67
|
+
# an invoice is different to a standalone tracking category.
|
68
|
+
# This means rather than going category.to_xml we need to call the special category.to_xml_for_invoice_messages
|
69
|
+
(tracking.is_a?(TrackingCategory) ? [tracking] : tracking).each do |category|
|
70
|
+
category.to_xml_for_invoice_messages(b)
|
71
|
+
end
|
72
|
+
}
|
73
|
+
end
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.from_xml(journal_line_element)
|
78
|
+
journal_line = JournalLine.new
|
79
|
+
journal_line_element.children.each do |element|
|
80
|
+
case(element.name)
|
81
|
+
when "LineAmount" then journal_line.line_amount = BigDecimal.new(element.text)
|
82
|
+
when "AccountCode" then journal_line.account_code = element.text
|
83
|
+
when "JournalLineID" then journal_line.journal_line_id = element.text
|
84
|
+
when "Description" then journal_line.description = element.text
|
85
|
+
when "TaxType" then journal_line.tax_type = element.text
|
86
|
+
when "Tracking" then
|
87
|
+
element.children.each do | tracking_element |
|
88
|
+
journal_line.tracking << TrackingCategory.from_xml(tracking_element)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
journal_line
|
93
|
+
end
|
94
|
+
|
95
|
+
def ==(other)
|
96
|
+
[:description, :line_amount, :account_code, :tax_type].each do |field|
|
97
|
+
return false if send(field) != other.send(field)
|
98
|
+
end
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class ManualJournal
|
3
|
+
include Dates
|
4
|
+
|
5
|
+
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)
|
6
|
+
|
7
|
+
STATUSES = {
|
8
|
+
'DRAFT' => 'Draft Manual Journal',
|
9
|
+
'POSTED' => 'Posted Manual Journal',
|
10
|
+
'DELETED' => 'Deleted Draft Manual Journal',
|
11
|
+
'VOIDED' => 'Voided Posted Manual Journal'
|
12
|
+
} unless defined?(STATUSES)
|
13
|
+
|
14
|
+
# Xero::Gateway associated with this invoice.
|
15
|
+
attr_accessor :gateway
|
16
|
+
|
17
|
+
# Any errors that occurred when the #valid? method called.
|
18
|
+
attr_reader :errors
|
19
|
+
|
20
|
+
# Represents whether the journal lines have been downloaded when getting from GET /API.XRO/2.0/ManualJournals
|
21
|
+
attr_accessor :journal_lines_downloaded
|
22
|
+
|
23
|
+
# accessible fields
|
24
|
+
attr_accessor :manual_journal_id, :narration, :date, :status, :journal_lines, :url, :show_on_cash_basis_reports
|
25
|
+
|
26
|
+
def initialize(params = {})
|
27
|
+
@errors ||= []
|
28
|
+
@payments ||= []
|
29
|
+
|
30
|
+
# Check if the line items have been downloaded.
|
31
|
+
@journal_lines_downloaded = (params.delete(:journal_lines_downloaded) == true)
|
32
|
+
|
33
|
+
params.each do |k,v|
|
34
|
+
self.send("#{k}=", v)
|
35
|
+
end
|
36
|
+
|
37
|
+
@journal_lines ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
['narration', 'status', 'journal_lines', 'show_on_cash_basis_reports'].each do |field|
|
42
|
+
return false if send(field) != other.send(field)
|
43
|
+
end
|
44
|
+
|
45
|
+
["date"].each do |field|
|
46
|
+
return false if send(field).to_s != other.send(field).to_s
|
47
|
+
end
|
48
|
+
return true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Validate the ManualJournal record according to what will be valid by the gateway.
|
52
|
+
#
|
53
|
+
# Usage:
|
54
|
+
# manual_journal.valid? # Returns true/false
|
55
|
+
#
|
56
|
+
# Additionally sets manual_journal.errors array to an array of field/error.
|
57
|
+
def valid?
|
58
|
+
@errors = []
|
59
|
+
|
60
|
+
if !manual_journal_id.nil? && manual_journal_id !~ GUID_REGEX
|
61
|
+
@errors << ['manual_journal_id', 'must be blank or a valid Xero GUID']
|
62
|
+
end
|
63
|
+
|
64
|
+
if narration.blank?
|
65
|
+
@errors << ['narration', "can't be blank"]
|
66
|
+
end
|
67
|
+
|
68
|
+
unless date
|
69
|
+
@errors << ['date', "can't be blank"]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Make sure all journal_items are valid.
|
73
|
+
unless journal_lines.all? { | journal_line | journal_line.valid? }
|
74
|
+
@errors << ['journal_lines', "at least one journal line invalid"]
|
75
|
+
end
|
76
|
+
|
77
|
+
# make sure there are at least 2 journal lines
|
78
|
+
unless journal_lines.length > 1
|
79
|
+
@errors << ['journal_lines', "journal must contain at least two individual journal lines"]
|
80
|
+
end
|
81
|
+
|
82
|
+
if journal_lines.length > 100
|
83
|
+
@errors << ['journal_lines', "journal must contain less than one hundred journal lines"]
|
84
|
+
end
|
85
|
+
|
86
|
+
unless journal_lines.sum(&:line_amount).to_f == 0.0
|
87
|
+
@errors << ['journal_lines', "the total debits must be equal to total credits"]
|
88
|
+
end
|
89
|
+
|
90
|
+
@errors.size == 0
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def journal_lines_downloaded?
|
95
|
+
@journal_lines_downloaded
|
96
|
+
end
|
97
|
+
|
98
|
+
# If line items are not downloaded, then attempt a download now (if this record was found to begin with).
|
99
|
+
def journal_lines
|
100
|
+
if journal_lines_downloaded?
|
101
|
+
@journal_lines
|
102
|
+
|
103
|
+
elsif manual_journal_id =~ GUID_REGEX && @gateway
|
104
|
+
# There is a manual_journal_id so we can assume this record was loaded from Xero.
|
105
|
+
# Let's attempt to download the journal_line records (if there is a gateway)
|
106
|
+
|
107
|
+
response = @gateway.get_manual_journal(manual_journal_id)
|
108
|
+
raise ManualJournalNotFoundError, "Manual Journal with ID #{manual_journal_id} not found in Xero." unless response.success? && response.manual_journal.is_a?(XeroGateway::ManualJournal)
|
109
|
+
|
110
|
+
@journal_lines = response.manual_journal.journal_lines
|
111
|
+
@journal_lines_downloaded = true
|
112
|
+
|
113
|
+
@journal_lines
|
114
|
+
|
115
|
+
# Otherwise, this is a new manual journal, so return the journal_lines reference.
|
116
|
+
else
|
117
|
+
@journal_lines
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def to_xml(b = Builder::XmlMarkup.new)
|
122
|
+
b.ManualJournal {
|
123
|
+
b.ManualJournalID manual_journal_id if manual_journal_id
|
124
|
+
b.Narration narration
|
125
|
+
b.JournalLines {
|
126
|
+
self.journal_lines.each do |journal_line|
|
127
|
+
journal_line.to_xml(b)
|
128
|
+
end
|
129
|
+
}
|
130
|
+
b.Date ManualJournal.format_date(date || Date.today)
|
131
|
+
b.Status status if status
|
132
|
+
b.Url url if url
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.from_xml(manual_journal_element, gateway = nil, options = {})
|
137
|
+
manual_journal = ManualJournal.new(options.merge({:gateway => gateway}))
|
138
|
+
manual_journal_element.children.each do |element|
|
139
|
+
case(element.name)
|
140
|
+
when "ManualJournalID" then manual_journal.manual_journal_id = element.text
|
141
|
+
when "Date" then manual_journal.date = parse_date(element.text)
|
142
|
+
when "Status" then manual_journal.status = element.text
|
143
|
+
when "Narration" then manual_journal.narration = element.text
|
144
|
+
when "JournalLines" then element.children.each {|journal_line| manual_journal.journal_lines_downloaded = true; manual_journal.journal_lines << JournalLine.from_xml(journal_line) }
|
145
|
+
when "Url" then manual_journal.url = element.text
|
146
|
+
end
|
147
|
+
end
|
148
|
+
manual_journal
|
149
|
+
end # from_xml
|
150
|
+
|
151
|
+
def add_journal_line(params = {})
|
152
|
+
journal_line = nil
|
153
|
+
case params
|
154
|
+
when Hash then journal_line = JournalLine.new(params)
|
155
|
+
when JournalLine then journal_line = params
|
156
|
+
else raise InvalidLineItemError
|
157
|
+
end
|
158
|
+
@journal_lines << journal_line
|
159
|
+
journal_line
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
@@ -9,11 +9,13 @@ module XeroGateway
|
|
9
9
|
alias_method :invoice, :response_item
|
10
10
|
alias_method :credit_note, :response_item
|
11
11
|
alias_method :bank_transaction, :response_item
|
12
|
+
alias_method :manual_journal, :response_item
|
12
13
|
alias_method :contact, :response_item
|
13
14
|
alias_method :organisation, :response_item
|
14
15
|
alias_method :invoices, :array_wrapped_response_item
|
15
16
|
alias_method :credit_notes, :array_wrapped_response_item
|
16
17
|
alias_method :bank_transactions, :array_wrapped_response_item
|
18
|
+
alias_method :manual_journals, :array_wrapped_response_item
|
17
19
|
alias_method :contacts, :array_wrapped_response_item
|
18
20
|
alias_method :accounts, :array_wrapped_response_item
|
19
21
|
alias_method :tracking_categories, :array_wrapped_response_item
|
data/lib/xero_gateway.rb
CHANGED
@@ -26,6 +26,8 @@ require File.join(File.dirname(__FILE__), 'xero_gateway', 'payment')
|
|
26
26
|
require File.join(File.dirname(__FILE__), 'xero_gateway', 'invoice')
|
27
27
|
require File.join(File.dirname(__FILE__), 'xero_gateway', 'bank_transaction')
|
28
28
|
require File.join(File.dirname(__FILE__), 'xero_gateway', 'credit_note')
|
29
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'journal_line')
|
30
|
+
require File.join(File.dirname(__FILE__), 'xero_gateway', 'manual_journal')
|
29
31
|
require File.join(File.dirname(__FILE__), 'xero_gateway', 'address')
|
30
32
|
require File.join(File.dirname(__FILE__), 'xero_gateway', 'phone')
|
31
33
|
require File.join(File.dirname(__FILE__), 'xero_gateway', 'organisation')
|
@@ -21,10 +21,10 @@ class AccountsListTest < Test::Unit::TestCase
|
|
21
21
|
def test_raise_error_on_not_loaded
|
22
22
|
accounts_list = @gateway.get_accounts_list(false)
|
23
23
|
assert_equal(false, accounts_list.loaded?)
|
24
|
-
assert_raise(XeroGateway::
|
25
|
-
assert_raise(XeroGateway::
|
26
|
-
assert_raise(XeroGateway::
|
27
|
-
assert_raise(XeroGateway::
|
24
|
+
assert_raise(XeroGateway::AccountsListNotLoadedError) { accounts_list[200] }
|
25
|
+
assert_raise(XeroGateway::AccountsListNotLoadedError) { accounts_list.find_by_code(200) }
|
26
|
+
assert_raise(XeroGateway::AccountsListNotLoadedError) { accounts_list.find_all_by_type('EXPENSE') }
|
27
|
+
assert_raise(XeroGateway::AccountsListNotLoadedError) { accounts_list.find_all_by_tax_type('OUTPUT') }
|
28
28
|
end
|
29
29
|
|
30
30
|
# Test simple lookup by account code (from cache).
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class CreateManualJournalTest < 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 =~ /ManualJournals$/ }.returns(get_file_as_string("create_manual_journal.xml"))
|
13
|
+
@gateway.stubs(:http_post).with {|client, url, body, params| url =~ /ManualJournals$/ }.returns(get_file_as_string("manual_journal.xml"))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_create_manual_journal
|
18
|
+
example_manual_journal = create_test_manual_journal.dup
|
19
|
+
|
20
|
+
result = @gateway.create_manual_journal(example_manual_journal)
|
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.manual_journal.manual_journal_id.nil?
|
26
|
+
assert example_manual_journal.manual_journal_id =~ GUID_REGEX
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_create_manual_journal_valid
|
30
|
+
example_manual_journal = create_test_manual_journal.dup
|
31
|
+
assert_equal true, example_manual_journal.valid?,
|
32
|
+
"manual_journal is invalid - errors:\n\t#{example_manual_journal.errors.map { | error | "#{error[0]} #{error[1]}"}.join("\n\t")}"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -25,6 +25,7 @@ class GetBankTransactionTest < Test::Unit::TestCase
|
|
25
25
|
assert !result.response_xml.nil?
|
26
26
|
assert_equal result.bank_transaction.bank_transaction_id, bank_transaction.bank_transaction_id
|
27
27
|
assert_equal result.bank_transaction.reference, bank_transaction.reference
|
28
|
+
assert result.bank_transaction.is_reconciled
|
28
29
|
|
29
30
|
result = @gateway.get_bank_transaction(bank_transaction.bank_transaction_id)
|
30
31
|
assert result.success?
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class GetManualJournalTest < 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 =~ /ManualJournals(\/[0-9a-z\-]+)?$/i }.returns(get_file_as_string("manual_journal.xml"))
|
13
|
+
@gateway.stubs(:http_put).with {|client, url, body, params| url =~ /ManualJournals$/ }.returns(get_file_as_string("create_manual_journal.xml"))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_get_manual_journal
|
18
|
+
# Make sure there is a manual journal in Xero to retrieve
|
19
|
+
response = @gateway.create_manual_journal(create_test_manual_journal)
|
20
|
+
manual_journal = response.manual_journal
|
21
|
+
|
22
|
+
result = @gateway.get_manual_journal(manual_journal.manual_journal_id)
|
23
|
+
assert result.success?
|
24
|
+
assert !result.request_params.nil?
|
25
|
+
assert !result.response_xml.nil?
|
26
|
+
assert_equal result.manual_journal.manual_journal_id, manual_journal.manual_journal_id
|
27
|
+
assert_equal result.manual_journal.narration, manual_journal.narration
|
28
|
+
|
29
|
+
result = @gateway.get_manual_journal(manual_journal.manual_journal_id)
|
30
|
+
assert result.success?
|
31
|
+
assert !result.request_params.nil?
|
32
|
+
assert !result.response_xml.nil?
|
33
|
+
assert_equal result.manual_journal.manual_journal_id, manual_journal.manual_journal_id
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_journal_lines_downloaded_set_correctly
|
37
|
+
# Make sure there is a manual journal in Xero to retrieve.
|
38
|
+
example_manual_journal = @gateway.create_manual_journal(create_test_manual_journal).manual_journal
|
39
|
+
|
40
|
+
# No line items.
|
41
|
+
response = @gateway.get_manual_journal(example_manual_journal.manual_journal_id)
|
42
|
+
assert response.success?
|
43
|
+
|
44
|
+
manual_journal = response.manual_journal
|
45
|
+
assert_kind_of(XeroGateway::JournalLine, manual_journal.journal_lines.first)
|
46
|
+
assert_kind_of(XeroGateway::ManualJournal, manual_journal)
|
47
|
+
assert manual_journal.journal_lines_downloaded?
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class GetManualJournalsTest < Test::Unit::TestCase
|
4
|
+
include TestHelper
|
5
|
+
|
6
|
+
INVALID_MANUAL_JOURNAL_ID = "99999999-9999-9999-9999-999999999999" unless defined?(INVALID_MANUAL_JOURNAL_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 =~ /ManualJournals(\/[0-9a-z\-]+)?$/i }.returns(get_file_as_string("manual_journals.xml"))
|
15
|
+
@gateway.stubs(:http_put).with {|client, url, body, params| url =~ /ManualJournals$/ }.returns(get_file_as_string("create_manual_journal.xml"))
|
16
|
+
|
17
|
+
# Get a manual journal with an invalid ID.
|
18
|
+
@gateway.stubs(:http_get).with {|client, url, params| url =~ Regexp.new("ManualJournals/#{INVALID_MANUAL_JOURNAL_ID}") }.returns(get_file_as_string("manual_journal_not_found_error.xml"))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_get_manual_journals
|
23
|
+
# Make sure there is a manual journal in Xero to retrieve
|
24
|
+
manual_journal = @gateway.create_manual_journal(create_test_manual_journal).manual_journal
|
25
|
+
|
26
|
+
result = @gateway.get_manual_journals
|
27
|
+
assert result.success?
|
28
|
+
assert !result.request_params.nil?
|
29
|
+
assert !result.response_xml.nil?
|
30
|
+
assert result.manual_journals.collect {|i| i.narration}.include?(manual_journal.narration)
|
31
|
+
assert result.manual_journals.collect {|i| i.manual_journal_id}.include?(manual_journal.manual_journal_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_get_manual_journals_with_modified_since_date
|
35
|
+
# Create a test manual journal
|
36
|
+
@gateway.create_manual_journal(create_test_manual_journal)
|
37
|
+
|
38
|
+
# Check that it is returned
|
39
|
+
result = @gateway.get_manual_journals(: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_journal_lines_downloaded_set_correctly
|
47
|
+
# No line items.
|
48
|
+
response = @gateway.get_manual_journals
|
49
|
+
assert_equal(true, response.success?)
|
50
|
+
|
51
|
+
manual_journal = response.manual_journals.first
|
52
|
+
assert_kind_of(XeroGateway::ManualJournal, manual_journal)
|
53
|
+
assert !manual_journal.journal_lines_downloaded?
|
54
|
+
end
|
55
|
+
|
56
|
+
# Make sure that a reference to gateway is passed when the get_manual_journals response is parsed.
|
57
|
+
def test_get_manual_journals_gateway_reference
|
58
|
+
result = @gateway.get_manual_journals
|
59
|
+
assert(result.success?)
|
60
|
+
assert_not_equal(0, result.manual_journals.size)
|
61
|
+
|
62
|
+
result.manual_journals.each do |manual_journal|
|
63
|
+
assert(manual_journal.gateway === @gateway)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Test to make sure that we correctly error when a manual journal doesn't have an ID.
|
68
|
+
# This should usually never be ecountered.
|
69
|
+
def test_to_ensure_that_a_manual_journal_with_invalid_id_errors
|
70
|
+
# Make sure there is a manual journal to retrieve, even though we will mangle it later.
|
71
|
+
manual_journal = @gateway.create_manual_journal(create_test_manual_journal).manual_journal
|
72
|
+
|
73
|
+
result = @gateway.get_manual_journals
|
74
|
+
assert_equal(true, result.success?)
|
75
|
+
|
76
|
+
manual_journal = result.manual_journals.first
|
77
|
+
assert !manual_journal.journal_lines_downloaded?
|
78
|
+
|
79
|
+
# Mangle invoice_id to invalid one.
|
80
|
+
manual_journal.manual_journal_id = INVALID_MANUAL_JOURNAL_ID
|
81
|
+
|
82
|
+
# Make sure we fail here.
|
83
|
+
journal_lines = nil
|
84
|
+
assert_raise(XeroGateway::ManualJournalNotFoundError) { journal_lines = manual_journal.journal_lines }
|
85
|
+
assert_nil(journal_lines)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class UpdateManualJournalTest < 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 =~ /ManualJournals$/ }.returns(get_file_as_string("create_manual_journal.xml"))
|
13
|
+
@gateway.stubs(:http_post).with {|client, url, body, params| url =~ /ManualJournals$/ }.returns(get_file_as_string("manual_journal.xml"))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_update_manual_journal
|
18
|
+
manual_journal = @gateway.create_manual_journal(create_test_manual_journal).manual_journal
|
19
|
+
|
20
|
+
today = Date.today
|
21
|
+
manual_journal.date = today
|
22
|
+
|
23
|
+
result = @gateway.update_manual_journal(manual_journal)
|
24
|
+
|
25
|
+
assert result.success?
|
26
|
+
assert !result.request_xml.nil?
|
27
|
+
assert !result.response_xml.nil?
|
28
|
+
assert_equal manual_journal.manual_journal_id, result.manual_journal.manual_journal_id
|
29
|
+
assert_equal today, result.manual_journal.date if !STUB_XERO_CALLS
|
30
|
+
end
|
31
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -176,4 +176,42 @@ module TestHelper
|
|
176
176
|
contact
|
177
177
|
end
|
178
178
|
|
179
|
+
def create_test_manual_journal(params={}, journal_line_params={})
|
180
|
+
params = {
|
181
|
+
:date => Date.today,
|
182
|
+
:narration => 'test narration',
|
183
|
+
:status => 'POSTED'
|
184
|
+
}.merge(params)
|
185
|
+
manual_journal = XeroGateway::ManualJournal.new(params)
|
186
|
+
add_test_journal_lines(manual_journal, journal_line_params)
|
187
|
+
end
|
188
|
+
|
189
|
+
def add_test_journal_lines(manual_journal, journal_line_params)
|
190
|
+
if journal_line_params
|
191
|
+
journal_line_params = [journal_line_params].flatten # always use an array, even if only a single hash passed in
|
192
|
+
|
193
|
+
# At least one line item, make first have some defaults.
|
194
|
+
journal_line_params << {} if journal_line_params.size == 0
|
195
|
+
journal_line_params[0] = {
|
196
|
+
:description => "FIRST LINE",
|
197
|
+
:account_code => "200",
|
198
|
+
:line_amount => BigDecimal.new("100"),
|
199
|
+
:tracking => XeroGateway::TrackingCategory.new(:name => "blah", :options => "hello")
|
200
|
+
}.merge(journal_line_params[0])
|
201
|
+
params_line_1 = journal_line_params[1] || {}
|
202
|
+
journal_line_params[1] = {
|
203
|
+
:description => "SECOND LINE",
|
204
|
+
:account_code => "200",
|
205
|
+
:line_amount => BigDecimal.new("-100"),
|
206
|
+
:tracking => XeroGateway::TrackingCategory.new(:name => "blah2", :options => "hello2")
|
207
|
+
}.merge(params_line_1)
|
208
|
+
|
209
|
+
# Create line_items from line_item_params
|
210
|
+
journal_line_params.each do |journal_line|
|
211
|
+
manual_journal.add_journal_line(journal_line)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
manual_journal
|
215
|
+
end
|
216
|
+
|
179
217
|
end
|
@@ -65,7 +65,7 @@ class BankTransactionTest < Test::Unit::TestCase
|
|
65
65
|
|
66
66
|
# Test that pushing anything else into add_line_item fails.
|
67
67
|
["invalid", 100, nil, []].each do | invalid_object |
|
68
|
-
assert_raise(XeroGateway::
|
68
|
+
assert_raise(XeroGateway::InvalidLineItemError) { @bank_transaction.add_line_item(invalid_object) }
|
69
69
|
assert_equal(2, @bank_transaction.line_items.size)
|
70
70
|
end
|
71
71
|
end
|
@@ -100,6 +100,18 @@ class BankTransactionTest < Test::Unit::TestCase
|
|
100
100
|
parsed_bank_transaction = XeroGateway::BankTransaction.from_xml(bank_transaction_element)
|
101
101
|
assert_equal 'http://example.com?with=params&and=more', parsed_bank_transaction.url
|
102
102
|
end
|
103
|
+
|
104
|
+
should "ignore missing contact" do
|
105
|
+
bank_transaction = create_test_bank_transaction
|
106
|
+
bank_transaction.contact = nil
|
107
|
+
bank_transaction.to_xml
|
108
|
+
end
|
109
|
+
|
110
|
+
should "ignore missing bank account" do
|
111
|
+
bank_transaction = create_test_bank_transaction
|
112
|
+
bank_transaction.bank_account = nil
|
113
|
+
bank_transaction.to_xml
|
114
|
+
end
|
103
115
|
end
|
104
116
|
|
105
117
|
private
|
@@ -215,7 +215,7 @@ class CreditNoteTest < Test::Unit::TestCase
|
|
215
215
|
|
216
216
|
# Test that pushing anything else into add_line_item fails.
|
217
217
|
["invalid", 100, nil, []].each do | invalid_object |
|
218
|
-
assert_raise(XeroGateway::
|
218
|
+
assert_raise(XeroGateway::InvalidLineItemError) { credit_note.add_line_item(invalid_object) }
|
219
219
|
assert_equal(2, credit_note.line_items.size)
|
220
220
|
end
|
221
221
|
end
|
data/test/unit/gateway_test.rb
CHANGED
@@ -7,7 +7,7 @@ class GatewayTest < Test::Unit::TestCase
|
|
7
7
|
@gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
|
8
8
|
end
|
9
9
|
|
10
|
-
context "with
|
10
|
+
context "with error handling" do
|
11
11
|
|
12
12
|
should "handle token expired" do
|
13
13
|
XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("token_expired"), :code => "401"))
|
@@ -56,7 +56,31 @@ class GatewayTest < Test::Unit::TestCase
|
|
56
56
|
@gateway.create_invoice(XeroGateway::Invoice.new)
|
57
57
|
end
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
|
+
should "handle invoices not found" do
|
61
|
+
XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("api_exception.xml"), :code => "404"))
|
62
|
+
|
63
|
+
assert_raises XeroGateway::InvoiceNotFoundError do
|
64
|
+
@gateway.get_invoice('unknown-invoice-id')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
should "handle bank transactions not found" do
|
69
|
+
XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("api_exception.xml"), :code => "404"))
|
70
|
+
|
71
|
+
assert_raises XeroGateway::BankTransactionNotFoundError do
|
72
|
+
@gateway.get_bank_transaction('unknown-bank-transaction-id')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
should "handle credit notes not found" do
|
77
|
+
XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("api_exception.xml"), :code => "404"))
|
78
|
+
|
79
|
+
assert_raises XeroGateway::CreditNoteNotFoundError do
|
80
|
+
@gateway.get_credit_note('unknown-credit-note-id')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
60
84
|
should "handle random root elements" do
|
61
85
|
XeroGateway::OAuth.any_instance.stubs(:put).returns(stub(:plain_body => "<RandomRootElement></RandomRootElement>", :code => "200"))
|
62
86
|
|
data/test/unit/invoice_test.rb
CHANGED
@@ -229,7 +229,7 @@ class InvoiceTest < Test::Unit::TestCase
|
|
229
229
|
|
230
230
|
# Test that pushing anything else into add_line_item fails.
|
231
231
|
["invalid", 100, nil, []].each do | invalid_object |
|
232
|
-
assert_raise(XeroGateway::
|
232
|
+
assert_raise(XeroGateway::InvalidLineItemError) { invoice.add_line_item(invalid_object) }
|
233
233
|
assert_equal(2, invoice.line_items.size)
|
234
234
|
end
|
235
235
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../test_helper.rb')
|
2
|
+
|
3
|
+
class ManualJournalTest < Test::Unit::TestCase
|
4
|
+
include TestHelper
|
5
|
+
|
6
|
+
context "creating test manual journals" do
|
7
|
+
should "work" do
|
8
|
+
manual_journal = create_test_manual_journal
|
9
|
+
|
10
|
+
# test transaction defaults
|
11
|
+
assert_equal 'POSTED', manual_journal.status
|
12
|
+
assert_kind_of Date, manual_journal.date
|
13
|
+
assert_equal 'test narration', manual_journal.narration
|
14
|
+
|
15
|
+
# Test the journal_line defaults.
|
16
|
+
journal_line = manual_journal.journal_lines.first
|
17
|
+
assert_equal('FIRST LINE', journal_line.description)
|
18
|
+
assert_equal('200', journal_line.account_code)
|
19
|
+
assert_equal(BigDecimal.new('100'), journal_line.line_amount)
|
20
|
+
end
|
21
|
+
|
22
|
+
should "allow overriding transaction defaults" do
|
23
|
+
assert_equal 'DRAFT', create_test_manual_journal(:status => 'DRAFT').status
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "adding journal lines" do
|
28
|
+
setup do
|
29
|
+
@manual_journal = create_test_manual_journal
|
30
|
+
end
|
31
|
+
|
32
|
+
should "work" do
|
33
|
+
assert_equal 2, @manual_journal.journal_lines.size
|
34
|
+
assert @manual_journal.valid?
|
35
|
+
|
36
|
+
journal_line_params = {:description => "Test Item 1", :line_amount => 100, :account_code => '200'}
|
37
|
+
|
38
|
+
# Test adding line item by hash
|
39
|
+
journal_line = @manual_journal.add_journal_line(journal_line_params)
|
40
|
+
assert_kind_of(XeroGateway::JournalLine, journal_line)
|
41
|
+
assert_equal(journal_line_params[:description], journal_line.description)
|
42
|
+
assert_equal(journal_line_params[:line_amount], journal_line.line_amount)
|
43
|
+
assert_equal(3, @manual_journal.journal_lines.size)
|
44
|
+
|
45
|
+
# Test adding line item by XeroGateway::JournalLine
|
46
|
+
journal_line = @manual_journal.add_journal_line(journal_line_params)
|
47
|
+
assert_kind_of(XeroGateway::JournalLine, journal_line)
|
48
|
+
assert_equal(journal_line_params[:description], journal_line.description)
|
49
|
+
assert_equal(journal_line_params[:line_amount], journal_line.line_amount)
|
50
|
+
assert_equal(4, @manual_journal.journal_lines.size)
|
51
|
+
|
52
|
+
# Test that having only 1 journal line fails.
|
53
|
+
@manual_journal.journal_lines = []
|
54
|
+
@manual_journal.add_journal_line(journal_line_params)
|
55
|
+
assert !@manual_journal.valid?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
context "building and parsing XML" do
|
61
|
+
should "work vice versa" do
|
62
|
+
manual_journal = create_test_manual_journal
|
63
|
+
manual_journal_as_xml = manual_journal.to_xml
|
64
|
+
manual_journal_element = REXML::XPath.first(REXML::Document.new(manual_journal_as_xml), "/ManualJournal")
|
65
|
+
|
66
|
+
# checking for mandatory fields
|
67
|
+
assert_xml_field manual_journal_element, 'Date'
|
68
|
+
assert_xml_field manual_journal_element, 'Narration', :value => 'test narration'
|
69
|
+
assert_xml_field manual_journal_element, 'Status', :value => 'POSTED'
|
70
|
+
|
71
|
+
parsed_manual_journal = XeroGateway::ManualJournal.from_xml(manual_journal_element)
|
72
|
+
assert_equal(manual_journal, parsed_manual_journal)
|
73
|
+
end
|
74
|
+
|
75
|
+
should "work for optional params" do
|
76
|
+
manual_journal = create_test_manual_journal(:url => 'http://example.com?with=params&and=more')
|
77
|
+
manual_journal_element = REXML::XPath.first(REXML::Document.new(manual_journal.to_xml), "/ManualJournal")
|
78
|
+
|
79
|
+
assert_xml_field manual_journal_element, 'Url', :value => 'http://example.com\?with=params&and=more'
|
80
|
+
|
81
|
+
parsed_manual_journal = XeroGateway::ManualJournal.from_xml(manual_journal_element)
|
82
|
+
assert_equal 'http://example.com?with=params&and=more', parsed_manual_journal.url
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def assert_xml_field(xml, field_name, options={})
|
89
|
+
assert_match /#{field_name}/, xml.to_s, "Didn't find the field #{field_name} in the XML document!"
|
90
|
+
assert_match /#{field_name}.*#{options[:value]}.*#{field_name}/, xml.to_s, "The field #{field_name} was expected to be '#{options[:value]}'!" if options[:value]
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/xero_gateway.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "xero_gateway"
|
3
|
-
s.version = "2.0.
|
4
|
-
s.date = "2012-
|
3
|
+
s.version = "2.0.15"
|
4
|
+
s.date = "2012-06-15"
|
5
5
|
s.summary = "Enables ruby based applications to communicate with the Xero API"
|
6
6
|
s.email = "tim@connorsoftware.com"
|
7
7
|
s.homepage = "http://github.com/tlconnor/xero_gateway"
|
metadata
CHANGED
@@ -1,56 +1,79 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: xero_gateway
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
- 15
|
10
|
+
version: 2.0.15
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Tim Connor
|
9
14
|
- Nik Wakelin
|
10
15
|
autorequire:
|
11
16
|
bindir: bin
|
12
17
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
18
|
+
|
19
|
+
date: 2012-06-15 00:00:00 -07:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
16
23
|
name: builder
|
17
|
-
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
26
|
none: false
|
19
|
-
requirements:
|
20
|
-
- -
|
21
|
-
- !ruby/object:Gem::Version
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 15
|
31
|
+
segments:
|
32
|
+
- 2
|
33
|
+
- 1
|
34
|
+
- 2
|
22
35
|
version: 2.1.2
|
23
36
|
type: :runtime
|
24
|
-
|
25
|
-
|
26
|
-
- !ruby/object:Gem::Dependency
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
27
39
|
name: oauth
|
28
|
-
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
42
|
none: false
|
30
|
-
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 31
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
- 3
|
50
|
+
- 6
|
33
51
|
version: 0.3.6
|
34
52
|
type: :runtime
|
35
|
-
|
36
|
-
|
37
|
-
- !ruby/object:Gem::Dependency
|
53
|
+
version_requirements: *id002
|
54
|
+
- !ruby/object:Gem::Dependency
|
38
55
|
name: activesupport
|
39
|
-
|
56
|
+
prerelease: false
|
57
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
58
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
45
66
|
type: :runtime
|
46
|
-
|
47
|
-
version_requirements: *70356902600700
|
67
|
+
version_requirements: *id003
|
48
68
|
description: Enables ruby based applications to communicate with the Xero API
|
49
69
|
email: tim@connorsoftware.com
|
50
70
|
executables: []
|
71
|
+
|
51
72
|
extensions: []
|
73
|
+
|
52
74
|
extra_rdoc_files: []
|
53
|
-
|
75
|
+
|
76
|
+
files:
|
54
77
|
- Gemfile
|
55
78
|
- LICENSE
|
56
79
|
- Rakefile
|
@@ -74,8 +97,10 @@ files:
|
|
74
97
|
- lib/xero_gateway/http.rb
|
75
98
|
- lib/xero_gateway/http_encoding_helper.rb
|
76
99
|
- lib/xero_gateway/invoice.rb
|
100
|
+
- lib/xero_gateway/journal_line.rb
|
77
101
|
- lib/xero_gateway/line_item.rb
|
78
102
|
- lib/xero_gateway/line_item_calculations.rb
|
103
|
+
- lib/xero_gateway/manual_journal.rb
|
79
104
|
- lib/xero_gateway/money.rb
|
80
105
|
- lib/xero_gateway/oauth.rb
|
81
106
|
- lib/xero_gateway/organisation.rb
|
@@ -92,6 +117,7 @@ files:
|
|
92
117
|
- test/integration/create_contact_test.rb
|
93
118
|
- test/integration/create_credit_note_test.rb
|
94
119
|
- test/integration/create_invoice_test.rb
|
120
|
+
- test/integration/create_manual_journal_test.rb
|
95
121
|
- test/integration/get_accounts_test.rb
|
96
122
|
- test/integration/get_bank_transaction_test.rb
|
97
123
|
- test/integration/get_bank_transactions_test.rb
|
@@ -102,12 +128,15 @@ files:
|
|
102
128
|
- test/integration/get_currencies_test.rb
|
103
129
|
- test/integration/get_invoice_test.rb
|
104
130
|
- test/integration/get_invoices_test.rb
|
131
|
+
- test/integration/get_manual_journal_test.rb
|
132
|
+
- test/integration/get_manual_journals_test.rb
|
105
133
|
- test/integration/get_organisation_test.rb
|
106
134
|
- test/integration/get_tax_rates_test.rb
|
107
135
|
- test/integration/get_tracking_categories_test.rb
|
108
136
|
- test/integration/update_bank_transaction_test.rb
|
109
137
|
- test/integration/update_contact_test.rb
|
110
138
|
- test/integration/update_invoice_test.rb
|
139
|
+
- test/integration/update_manual_journal_test.rb
|
111
140
|
- test/test_helper.rb
|
112
141
|
- test/unit/account_test.rb
|
113
142
|
- test/unit/bank_transaction_test.rb
|
@@ -116,33 +145,45 @@ files:
|
|
116
145
|
- test/unit/currency_test.rb
|
117
146
|
- test/unit/gateway_test.rb
|
118
147
|
- test/unit/invoice_test.rb
|
148
|
+
- test/unit/manual_journal_test.rb
|
119
149
|
- test/unit/oauth_test.rb
|
120
150
|
- test/unit/organisation_test.rb
|
121
151
|
- test/unit/tax_rate_test.rb
|
122
152
|
- test/unit/tracking_category_test.rb
|
123
153
|
- lib/xero_gateway/ca-certificates.crt
|
154
|
+
has_rdoc: true
|
124
155
|
homepage: http://github.com/tlconnor/xero_gateway
|
125
156
|
licenses: []
|
157
|
+
|
126
158
|
post_install_message:
|
127
159
|
rdoc_options: []
|
128
|
-
|
160
|
+
|
161
|
+
require_paths:
|
129
162
|
- lib
|
130
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
163
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
164
|
none: false
|
132
|
-
requirements:
|
133
|
-
- -
|
134
|
-
- !ruby/object:Gem::Version
|
135
|
-
|
136
|
-
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
hash: 3
|
169
|
+
segments:
|
170
|
+
- 0
|
171
|
+
version: "0"
|
172
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
173
|
none: false
|
138
|
-
requirements:
|
139
|
-
- -
|
140
|
-
- !ruby/object:Gem::Version
|
141
|
-
|
174
|
+
requirements:
|
175
|
+
- - ">="
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
hash: 3
|
178
|
+
segments:
|
179
|
+
- 0
|
180
|
+
version: "0"
|
142
181
|
requirements: []
|
182
|
+
|
143
183
|
rubyforge_project:
|
144
|
-
rubygems_version: 1.
|
184
|
+
rubygems_version: 1.6.2
|
145
185
|
signing_key:
|
146
186
|
specification_version: 3
|
147
187
|
summary: Enables ruby based applications to communicate with the Xero API
|
148
188
|
test_files: []
|
189
|
+
|