Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
261 commits
Select commit Hold shift + click to select a range
32fae30
debug
Mar 19, 2014
83f62da
debug
Mar 19, 2014
154bc25
debug
Mar 19, 2014
56f1665
debug
Mar 19, 2014
77cbb81
debug
Mar 19, 2014
65c13d2
debug
Mar 19, 2014
4331d71
debug
Mar 19, 2014
842200b
debug
Mar 19, 2014
c873868
custom_fields
Mar 19, 2014
5c5201e
debug
Mar 19, 2014
83d071b
debug
Mar 19, 2014
99e10c7
debug
Mar 19, 2014
7ecb441
debug
Mar 19, 2014
681a9f3
debug
Mar 19, 2014
4007cdf
debug
Mar 19, 2014
2629592
debug
Mar 19, 2014
3bf943b
debug
Mar 19, 2014
a61405c
pluralize on class names
Mar 19, 2014
f3ed34b
debug
Mar 19, 2014
775d566
cleanup
Mar 19, 2014
b5a817b
debug
Mar 19, 2014
4aa32f1
debug
Mar 19, 2014
d2ebf66
fix npe
Mar 19, 2014
b546c9b
JSON post
Mar 19, 2014
8804761
post
Mar 19, 2014
9b19b95
debug
Mar 19, 2014
3367e6a
debug
Mar 19, 2014
c5dcf1e
post
Mar 19, 2014
a0a9ef2
debug
Mar 19, 2014
a494af7
debug
Mar 19, 2014
fb44aad
debug
Mar 19, 2014
78fb71b
json
Mar 19, 2014
03d55be
debug
Mar 19, 2014
86a2ef8
debug
Mar 19, 2014
acc050c
to_json
Mar 19, 2014
c9f536d
debug
Mar 19, 2014
8aac873
debug
Mar 19, 2014
a6020a8
debug
Mar 19, 2014
4b14fcb
debug
Mar 19, 2014
3bf7eb5
debug
Mar 19, 2014
2231c87
debug
Mar 19, 2014
048c023
debug
Mar 19, 2014
6a6da23
debug
Mar 19, 2014
76d6648
debug
Mar 19, 2014
be9abaf
debug
Mar 19, 2014
08739f6
debug
Mar 19, 2014
d30b809
debug
Mar 19, 2014
ee5462b
debug
Mar 19, 2014
8e363cf
debug
Mar 19, 2014
f56551f
debug
Mar 19, 2014
9ebe245
debug
Mar 19, 2014
37b377a
debug
Mar 19, 2014
9651904
test
Mar 19, 2014
079917f
debug
Mar 19, 2014
ffbf83f
test
Mar 19, 2014
d6d7337
test
Mar 19, 2014
1b37b0f
debug
Mar 19, 2014
c6675a9
debug
Mar 19, 2014
0500743
debug
Mar 19, 2014
9947c3c
debug
Mar 19, 2014
8b7eeb7
debug
Mar 19, 2014
93dd1ce
debug
Mar 19, 2014
c816401
debug
Mar 19, 2014
f3fc297
debug
Mar 19, 2014
4bc2afb
debug
Mar 19, 2014
b1b443b
debug
Mar 19, 2014
1631a32
debug
Mar 19, 2014
e834cf4
debug
Mar 19, 2014
25b0d15
debug
Mar 19, 2014
1f6ce0a
debug
Mar 19, 2014
82cce74
debug
Mar 20, 2014
7424bfe
debug
Mar 20, 2014
b7dfc95
debug
Mar 20, 2014
83de461
debug
Mar 20, 2014
1fda9ec
debug
Mar 20, 2014
3d88fae
debug
Mar 20, 2014
1d92ba5
debug
Mar 20, 2014
20408e7
debug
Mar 20, 2014
f308ea0
debug
Mar 20, 2014
c4b48e6
debug
Mar 20, 2014
6678b79
debug
Mar 20, 2014
3e1c330
debug
Mar 20, 2014
f3f89e6
debug
Mar 20, 2014
3d0fa1c
debug
Mar 20, 2014
09ae041
debug
Mar 20, 2014
6833d15
debug
Mar 20, 2014
510c78c
debug
Mar 20, 2014
9c42617
debug
Mar 20, 2014
3dd9087
debug
Mar 20, 2014
65f533b
debug
Mar 20, 2014
6500ea3
debug
Mar 20, 2014
475dd78
debug
Mar 20, 2014
5a662dc
debug
Mar 20, 2014
8dce474
debug
Mar 20, 2014
5b39092
debug
Mar 20, 2014
e531c9d
debug
Mar 20, 2014
d87455f
debug
Mar 20, 2014
f55aa86
new attributes
Mar 20, 2014
f6a6412
debug
Mar 20, 2014
5c6a204
debug
Mar 20, 2014
e35ee8a
debug
Mar 20, 2014
1cac128
debug
Mar 20, 2014
4ba8037
debug
Mar 20, 2014
d306541
debug
Mar 20, 2014
612af02
debug
Mar 20, 2014
f39982c
debug
Mar 20, 2014
071ab97
debug
Mar 20, 2014
34166fd
debug
Mar 20, 2014
3b12dd0
debug
Mar 20, 2014
942d40d
debug
Mar 20, 2014
ac18be4
debug
Mar 20, 2014
c39f58f
test
Mar 20, 2014
ad4a490
debug
Mar 20, 2014
8ae66d0
test
Mar 20, 2014
b3578d8
debug
Mar 20, 2014
7e78ee3
debug
Mar 20, 2014
b6c0c34
debug
Mar 20, 2014
c58606a
reflections must not be null
Mar 20, 2014
dc17cb6
debug
Mar 20, 2014
9fc52b8
debug
Mar 20, 2014
23309a9
debug
Mar 20, 2014
7290ec6
debug
Mar 20, 2014
36365a6
debug
Mar 20, 2014
f69b795
debug
Mar 20, 2014
7beff9f
debug
Mar 20, 2014
2d2b1af
debug
Mar 20, 2014
5981f70
arrays
Mar 20, 2014
02b0047
debug
Mar 20, 2014
866dafa
debug
Mar 20, 2014
331309a
debug
Mar 20, 2014
99a6950
debug
Mar 20, 2014
5fcf4d7
test
Mar 20, 2014
8c0198e
debug
Mar 20, 2014
f8e6cc8
debug
Mar 20, 2014
e5a9416
debug
Mar 20, 2014
8cbed36
uri encoding
Mar 21, 2014
0b9d904
debug
Mar 21, 2014
c7769cf
debug
Mar 21, 2014
65de32a
debug
Mar 21, 2014
46dc70b
debug
Mar 21, 2014
90c0711
debug
Mar 21, 2014
3546774
debug
Mar 21, 2014
4e1f505
test
Mar 21, 2014
dd9b2b3
test
Mar 21, 2014
f7b602f
test
Mar 21, 2014
cb8b820
debug
Mar 21, 2014
fd0cca7
debug
Mar 21, 2014
8e6b3cd
debug
Mar 21, 2014
cf878c5
test
Mar 21, 2014
2ae49cc
debug
Mar 21, 2014
b382214
debug
Mar 21, 2014
2f2009f
test
Mar 21, 2014
120d257
test
Mar 21, 2014
b44068d
test
Mar 21, 2014
a65d1ca
test
Mar 21, 2014
e0bef19
debug
Mar 21, 2014
9eddfa2
debug
Mar 21, 2014
85f0ac0
debug
Mar 21, 2014
28f949a
debug
Mar 21, 2014
b8b2165
debug
Mar 21, 2014
d1e9a71
debug
Mar 21, 2014
bba8d93
debug
Mar 21, 2014
4ecbd01
debug
Mar 21, 2014
35aa236
debug
Mar 21, 2014
0079159
debug
Mar 21, 2014
dbca869
debug
Mar 21, 2014
ec68700
debug
Mar 21, 2014
5d37bfa
debug
Mar 21, 2014
8b7de60
debug
Mar 21, 2014
59e16aa
debug
Mar 21, 2014
04219a4
debug
Mar 21, 2014
a75442e
debug
Mar 21, 2014
de4a0a8
debug
Mar 21, 2014
6bd667a
debug
Mar 21, 2014
748b460
debug
Mar 21, 2014
53e4ab3
debug
Mar 21, 2014
7a98d97
debug
Mar 21, 2014
d88f989
debug
Mar 21, 2014
00ab174
debug
Mar 21, 2014
333fa70
debug
Mar 21, 2014
214e572
debug
Mar 21, 2014
32d4c58
debug
Mar 21, 2014
4cdcf71
debug
Mar 21, 2014
b373223
debug
Mar 21, 2014
c88e01e
debug
Mar 21, 2014
64c97c7
debug
Mar 21, 2014
787c5a0
debug
Mar 21, 2014
5843566
debug
Mar 21, 2014
2e395ba
debug
Mar 21, 2014
bdbb34f
debug
Mar 21, 2014
c69593d
debug
Mar 21, 2014
18e7bbc
debug
Mar 21, 2014
3a9b145
debug
Mar 21, 2014
f19f2e4
debug
Mar 21, 2014
11fa181
debug
Mar 21, 2014
51884b6
debug
Mar 21, 2014
0b4e279
debug
Mar 21, 2014
177bba3
debug
Mar 21, 2014
58ccb56
debug
Mar 21, 2014
4efc01b
debug
Mar 21, 2014
a0911fd
debug
Mar 21, 2014
649c788
debug
Mar 21, 2014
d6abe84
debug
Mar 21, 2014
d6119d9
debug
Mar 21, 2014
3be46bf
debug
Mar 21, 2014
714f122
debug
Mar 21, 2014
e54c9d7
debug
Mar 21, 2014
9952c55
debug
Mar 21, 2014
11f8b7d
debug
Mar 21, 2014
f24090a
debug
Mar 21, 2014
deab3d8
debug
Mar 21, 2014
b802d46
debug
Mar 21, 2014
24c1a6f
debug
Mar 21, 2014
a5a8a05
debug
Mar 21, 2014
ce2c74e
debug
Mar 21, 2014
45026d6
debug
Mar 21, 2014
a0e1840
debug
Mar 21, 2014
37ea509
debug
Mar 21, 2014
98ff1ba
debug
Mar 21, 2014
5c61647
debug
Mar 21, 2014
b577cf8
debug
Mar 21, 2014
bf28161
debug
Mar 21, 2014
94cd2c2
debug
Mar 21, 2014
3001916
debug
Mar 21, 2014
651425b
debug
Mar 21, 2014
c2d2b18
debug
Mar 21, 2014
9bf8ceb
debug
Mar 21, 2014
a7d73ee
debug
Mar 21, 2014
b1391da
debug
Mar 21, 2014
283f759
debug
Mar 21, 2014
75c60d7
debug
Mar 21, 2014
aef7c6b
debug
Mar 21, 2014
ad5f389
debug
Mar 21, 2014
fba2406
debug
Mar 21, 2014
57fb763
debug
Mar 21, 2014
f44efc3
debug
Mar 21, 2014
06f9659
debug
Mar 21, 2014
839bc1a
debug
Mar 21, 2014
469a5df
debug
Mar 21, 2014
90e9282
debug
Mar 21, 2014
91937c1
debug
Mar 21, 2014
29966a9
debug
Mar 21, 2014
3f018b1
debug
Mar 21, 2014
1f07d8e
debug
Mar 21, 2014
46b49eb
debug
Mar 21, 2014
c365a47
debug
Mar 21, 2014
84ccdad
additional strange characters
Mar 25, 2014
0879493
updated the regex for filtering
Mar 26, 2014
45eabb8
regex
Mar 26, 2014
c04bf9d
update to regexp
Mar 26, 2014
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 75 additions & 57 deletions lib/zoho_invoice/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,12 @@ def self.create(client, options = {})
self.new(client, options).save
end

# TODO need to build a class that is something like ActiveRecord::Relation
# TODO need to be able to handle associations when hydrating objects
#
def self.search(client, input_text, options = {})
result_hash = client.get("/api/view/search/#{self.to_s.split('::').last.downcase}s", :searchtext => input_text).body
objects_to_hydrate = result_hash['Response']["#{self.to_s.split('::').last}s"]["#{self.to_s.split('::').last}"]
self.process_objects(client, objects_to_hydrate)
rescue Faraday::Error::ClientError => e
if e.response[:body]
raise ZohoInvoice::Error::ClientError.from_response(e.response)
end
end

def initialize(client, options = {})
@client = client

# Assign all of the single attribtues
#
if !self.attributes.empty?
if !self.attributes.blank?
(self.attributes & options.keys).each do |attribute|
self.send("#{attribute}=", options[attribute])
end
Expand Down Expand Up @@ -76,29 +63,29 @@ def attributes
# TODO Determining the resource to use will need to change
#
def save

klass_name = self.class.to_s.split('::').last

action = 'create'
action = 'update' if !send("#{klass_name.downcase}_id").nil?

result = client.post("/api/#{klass_name.downcase + 's'}/#{action}", :XMLString => self.to_xml)

if action == 'create' && !result.body.nil? && !result.body['Response'][klass_name].nil?
self.send("#{klass_name.downcase}_id=", result.body['Response'][klass_name]["#{klass_name}ID"])
invoice_id = send("#{klass_name.downcase}_id")
if(invoice_id.blank?)
result = client.post("/api/v3/#{klass_name.downcase + 's'}", :JSONString => self.to_json)
else
result = client.put("/api/v3/#{klass_name.downcase + 's'}/#{invoice_id}", :JSONString => self.to_json)
end
if invoice_id.blank? && !result.body.blank? && !result.body[klass_name].blank?
self.send("#{klass_name.downcase}_id=", result.body[klass_name.downcase]["#{klass_name.downcase}_id"])
end

self
rescue Faraday::Error::ClientError => e
if e.response[:body]
if e.response && e.response[:body]
raise ZohoInvoice::Error::ClientError.from_response(e.response)
end
end

# This needs to be a Nokogiri::XML::Builder
#
def to_xml(*args)
build_attributes.to_xml(*args)
def to_json(*args)
to_hash(*args).to_json
end

def to_hash(*args)
build_attributes(ZohoInvoice::Invoice::CREATE_UPDATE_ATTRIBUTES)["#{self.class.to_s.split('::').last}"]
end

def self.create_attributes(attrs)
Expand All @@ -124,65 +111,96 @@ def camel_case(str)
self.class.camel_case(str)
end

def build_attributes
Nokogiri::XML::Builder.new do |xml|
xml.send("#{self.class.to_s.split('::').last}") {
self.attributes.each do |attr|
vals = self.send(attr)
if !vals.nil? && !vals.is_a?(Array)
xml.send("#{camel_case(attr.to_s)}_", self.send(attr))
end
def build_attributes(attrs)
h = {}
attrs.each do |attr|
vals = self.send(attr)
if(vals.is_a?(Array) || vals.is_a?(Hash) || vals.is_a?(String))
h["#{attr.to_s}"] = stringify_object_values(vals) unless(vals.blank?)
else
h["#{attr.to_s}"] = stringify_object_values(vals) unless(vals.nil?)
end
end
self.reflections.each do |refl|
refl_val = self.send(refl)
if !refl_val.blank?
refl_a = []
refl_val.each {|r| refl_h << r}
h[refl] = refl_a
end
end
g = {}
g[self.class.to_s.split('::').last] = h
g
end

def stringify_object_values(obj)
return(obj.to_s.gsub(/[`~!@#$%^&*()+=\\|\[{\]}'";:\/?><]/, ' ').squeeze(' ').strip) unless(obj.is_a?(Array) || obj.is_a?(Hash))
res = nil
if(obj.is_a?(Array))
res = []
obj.each do |elt|
if(elt.is_a?(Array) || elt.is_a?(Hash) || elt.is_a?(String))
res << stringify_object_values(elt) unless(elt.blank?)
else
res << stringify_object_values(elt) unless(elt.nil?)
end
self.reflections.each do |refl|
if !refl.empty?
xml.send(camel_case(refl.to_s)) {
self.send(refl).each { |x| xml << x.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::NO_DECLARATION) }
}
end
end
elsif(obj.is_a?(Hash))
res = {}
obj.each do |key, elt|
if(elt.is_a?(Array) || elt.is_a?(Hash) || elt.is_a?(String))
res[key] = stringify_object_values(elt) unless(elt.blank?)
else
res[key] = stringify_object_values(elt) unless(elt.nil?)
end
}
end
end
res
end

private

def self.retrieve(client, url)
klass = self.to_s.split('::').last
def self.retrieve(client, url, plural = true)
klass_name = self.to_s.split('::').last.downcase
klass_name += 's' if(plural)
page = 1
query = {}
objects_to_hydrate = []

begin
result_hash = client.get(url, query).body
potential_objects = result_hash['Response'][klass + 's']
potential_objects = result_hash

if potential_objects
potential_objects = potential_objects[klass]
potential_objects = potential_objects[klass_name]
if potential_objects.is_a? Hash
potential_objects = [potential_objects]
end
objects_to_hydrate += potential_objects
end

page_context = result_hash['Response']['PageContext']
page_context = result_hash['page_context']
if page_context
num_pages = page_context['Total_Pages'].to_i
page += 1
query = { :page => page }
has_more_page = page_context['has_more_page']
if(has_more_page)
page += 1
query = { :page => page }
end
else
num_pages = nil
has_more_page = false
end
end while num_pages && page <= num_pages
end while has_more_page

self.process_objects(client, objects_to_hydrate)
rescue Faraday::Error::ClientError => e
if e.response[:body]
if e.response && e.response[:body]
raise ZohoInvoice::Error::ClientError.from_response(e.response)
end
end

def self.process_objects(client, objects_to_hydrate)
if objects_to_hydrate.nil?
if objects_to_hydrate.blank?
return []
else
if objects_to_hydrate.is_a?(Hash) #Convert hash to array if only a single object is returned
Expand All @@ -191,7 +209,7 @@ def self.process_objects(client, objects_to_hydrate)
objects_to_hydrate.map do |result|
new_hash = {}
result.each do |key, value|
new_hash[key.to_underscore.to_sym] = value if !value.is_a?(Hash) && !value.is_a?(Array)
new_hash[key.to_underscore.to_sym] = value
end
self.new(client, new_hash)
end
Expand Down
10 changes: 6 additions & 4 deletions lib/zoho_invoice/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ def post(path, params={})
request(:post, path, credentials.merge(params))
end

def put(path, params={})
request(:put, path, credentials.merge(params))
end

def request(verb, path, params={})
connection.send(verb, path, params)
end
Expand All @@ -37,15 +41,13 @@ def request(verb, path, params={})
def connection
@connection ||= Faraday.new(@client_options) do |c|
c.use Faraday::Response::RaiseError

c.request :multipart
c.request :url_encoded

c.response :xml, :content_type => /\bxml$/

c.response :json, :content_type => /\bjson$/
c.adapter Faraday.default_adapter
end
end

end

end
4 changes: 2 additions & 2 deletions lib/zoho_invoice/customer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ def self.all(client, options = {})
end

def self.all_active(client, options = {})
retrieve(client, '/api/customers').each { |c| c.active = true }
retrieve(client, '/api/v3/customers').each { |c| c.active = true }
end

def self.all_inactive(client, options = {})
retrieve(client, '/api/customers/inactive').each { |c| c.active = false }
retrieve(client, '/api/v3/customers/inactive').each { |c| c.active = false }
end

end
Expand Down
6 changes: 3 additions & 3 deletions lib/zoho_invoice/error/client_error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ def self.parse_error(response)
error = self.new
if !response[:body].nil?
response_body = response[:body]
error.response_status = response_body['Response']['status']
error.code = response_body['Response']['Code']
error.message = response_body['Response']['Message']
error.response_status = response_body['status']
error.code = response_body['code']
error.message = response_body['message']
end
error.http_status = response[:status]
error
Expand Down
78 changes: 64 additions & 14 deletions lib/zoho_invoice/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,72 @@
module ZohoInvoice
class Invoice < Base

define_object_attrs :invoice_id,
:created_time, :exchange_rate,
:last_modified_time, :l_f_name,
:last_sync_time, :late_fee_amount,
:source, :total,
:reference_id, :invoice_item_total,
:invoice_number, :tax_total,
:p_o_number, :balance,
:status, :notes,
:customer_id, :terms,
READ_ATTRIBUTES = [
:invoice_id,
:created_time,
:exchange_rate,
:last_modified_time,
:l_f_name,
:last_sync_time,
:late_fee_amount,
:source,
:total,
:reference_id,
:invoice_item_total,
:invoice_number,
:tax_total,
:p_o_number,
:balance,
:status,
:notes,
:customer_id,
:terms,
:customer_name,
:custom_fields,
:invoice_date,
:payments_due,
:due_date,
:currency_code
:currency_code,
:additional_field1,
:adjustment,
:adjustment_description,
:allow_partial_payments,
:contact_persons,
:date,
:description,
:discount,
:discount_type,
:expense_id,
:gateway_name,
:invoiced_estimate_id,
:is_discount_before_tax,
:item_id,
:line_items,
:name,
:payment_gateways,
:payment_options,
:payment_terms,
:payment_terms_label,
:project_id,
:quantity,
:rate,
:recurring_invoice_id,
:salesperson_name,
:shipping_charge,
:tax_id,
:template_id,
:time_entry_ids,
:unit
]

CREATE_UPDATE_ATTRIBUTES = READ_ATTRIBUTES - [:invoice_id]

define_object_attrs(*READ_ATTRIBUTES)

has_many :invoice_items

def self.find_by_customer_id(client, id, options = {})
retrieve(client, "/api/invoices/customer/#{id}")
retrieve(client, "/api/v3/invoices/customer/#{id}", false)
end

def self.find_by_multiple_customer_ids(client, ids, options={})
Expand All @@ -34,7 +80,7 @@ def self.find_by_multiple_customer_ids(client, ids, options={})
end

def self.find_unpaid_by_customer_id(client, id, options = {})
retrieve(client, "/api/invoices/unpaid/customer/#{id}")
retrieve(client, "/api/v3/invoices/unpaid/customer/#{id}", false)
end

def self.find_unpaid_by_multiple_customer_ids(client, ids, options={})
Expand All @@ -46,7 +92,11 @@ def self.find_unpaid_by_multiple_customer_ids(client, ids, options={})
end

def self.all(client)
retrieve(client, '/api/invoices')
retrieve(client, '/api/v3/invoices')
end

def self.find(client, id, options={})
retrieve(client, "/api/v3/invoices/#{id}", false)
end

end
Expand Down
2 changes: 1 addition & 1 deletion spec/fixtures/successful_customers_response
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Response>
<Customers uri='/api/customers'>
<Customers uri='/api/v3/customers'>
<Customer>
<CustomerID>1</CustomerID>
</Customer>
Expand Down
2 changes: 1 addition & 1 deletion spec/fixtures/successful_empty_response
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<Response>
<Somethings uri='/api/views/search/somethings'></Somethings>
<Somethings uri='/api/v3/views/search/somethings'></Somethings>
</Response>
2 changes: 1 addition & 1 deletion spec/fixtures/successful_multiple_customer_response
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Response>
<Invoices uri='/api/invoices/customer/1234'>
<Invoices uri='/api/v3/invoices/customer/1234'>
<Invoice>
<InvoiceID>1</InvoiceID>
<Status>Open</Status>
Expand Down
2 changes: 1 addition & 1 deletion spec/fixtures/successful_multiple_page_response_page_1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Response>
<Invoices uri='/api/invoices/customer/1234'>
<Invoices uri='/api/v3/invoices/customer/1234'>
<Invoice>
<InvoiceID>1</InvoiceID>
<Status>Open</Status>
Expand Down
Loading