quick_book_gateway 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/core_ext/hash.rb +5 -0
- data/lib/core_ext/nil.rb +5 -0
- data/lib/core_ext/string.rb +7 -0
- data/lib/errors.rb +19 -0
- data/lib/gateway/quickbook.rb +95 -0
- data/lib/gateway/result.rb +29 -0
- data/lib/quick_book_gateway.rb +24 -0
- data/lib/service/callback.rb +27 -0
- data/lib/service/finder.rb +71 -0
- data/lib/service/persistence.rb +57 -0
- data/lib/service/query.rb +25 -0
- data/lib/service/record.rb +60 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 851b66e6a5c13cda71cadd30f647ff29efd9f453
|
4
|
+
data.tar.gz: 981305ca194a403b00fa359ec92d8ec9e9d93e0d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4f4b7d16a39071ff90781f9969c48b5c8c2b21ddfc75b83d71b63c4c2b8800e9f222c2da384c9d539851b307fedb11361e9cfb4d6c1b636b53376c4870aa9e4d
|
7
|
+
data.tar.gz: 9d203c40fd021dd5e4d9895d3477f52c7c8f0daef028eb1e10d424c13bdf1884df699e99eeaa790f5f3bcf0cc24261cb7503e68bf2b43dd49284df23da72731f
|
data/lib/core_ext/nil.rb
ADDED
data/lib/errors.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Errors
|
2
|
+
class GatewayError < StandardError
|
3
|
+
def initialize(msg = "Gateway error.")
|
4
|
+
super(msg)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class QuickBookgatewayInvalid < GatewayError
|
9
|
+
def initialize(msg = "Quickbook gateway is invalid.")
|
10
|
+
super(msg)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class RecordDeleteError < GatewayError
|
15
|
+
def initialize(msg = "Can't delete quickbook record.")
|
16
|
+
super(msg)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'oauth'
|
2
|
+
|
3
|
+
module Gateway
|
4
|
+
class Quickbook < Result
|
5
|
+
|
6
|
+
attr_reader :api
|
7
|
+
attr_accessor :consumer, :access_token, :company_id
|
8
|
+
|
9
|
+
module HTTP
|
10
|
+
JSON = "application/json"
|
11
|
+
end
|
12
|
+
|
13
|
+
module ENVIRONMENT
|
14
|
+
SANDBOX = "sandbox"
|
15
|
+
PRODUCTION = "production"
|
16
|
+
end
|
17
|
+
|
18
|
+
module API
|
19
|
+
VERSION = "v3"
|
20
|
+
COMPANY = "company/?" #replace ? with company ID
|
21
|
+
|
22
|
+
QUERY = "query"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def api=(value)
|
27
|
+
@api = value
|
28
|
+
end
|
29
|
+
|
30
|
+
public
|
31
|
+
#Connect to QuickBook online API gateway
|
32
|
+
#Options are as follow
|
33
|
+
# String: Environment ENVIRONMENT::SANDBOX or ENVIRONMENT::PRODUCTION. AutoLoad QuickBook key, secret, access token, access secret, company ID from config/quickbook.yml corresponding to environment given.
|
34
|
+
# Hash:
|
35
|
+
# :qb_key => QuickBook Key
|
36
|
+
# :qb_secret => QuickBook Secret
|
37
|
+
# :token => Access Token
|
38
|
+
# :secret => Access Sceret
|
39
|
+
# :company_id => QuickBook Company ID
|
40
|
+
# :environment => ENVIRONMENT::SANDBOX or ENVIRONMENT::PRODUCTION
|
41
|
+
def self.connect(options)
|
42
|
+
if(options.class == String)
|
43
|
+
configuration = YAML.load_file("config/quickbook.yml")
|
44
|
+
environment = options
|
45
|
+
options = Hash.new
|
46
|
+
options[:qb_key] = configuration[environment]["key"]
|
47
|
+
options[:qb_secret] = configuration[environment]["secret"]
|
48
|
+
options[:token] = configuration[environment]["access_token"]
|
49
|
+
options[:secret] = configuration[environment]["access_secret"]
|
50
|
+
options[:company_id] = configuration[environment]["company_id"]
|
51
|
+
options[:environment] = environment
|
52
|
+
end
|
53
|
+
|
54
|
+
gateway = new
|
55
|
+
gateway.consumer = OAuth::Consumer.new(options[:qb_key], options[:qb_secret], {
|
56
|
+
:site => "https://oauth.intuit.com",
|
57
|
+
:request_token_path => "/oauth/v1/get_request_token",
|
58
|
+
:authorize_url => "https://appcenter.intuit.com/Connect/Begin",
|
59
|
+
:access_token_path => "/oauth/v1/get_access_token"
|
60
|
+
})
|
61
|
+
|
62
|
+
gateway.access_token = OAuth::AccessToken.new(gateway.consumer, options[:token], options[:secret])
|
63
|
+
|
64
|
+
gateway.company_id = options[:company_id]
|
65
|
+
|
66
|
+
case options[:environment]
|
67
|
+
when ENVIRONMENT::PRODUCTION
|
68
|
+
gateway.send("api=", "https://quickbooks.api.intuit.com")
|
69
|
+
else
|
70
|
+
gateway.send("api=", "https://sandbox-quickbooks.api.intuit.com")
|
71
|
+
end
|
72
|
+
|
73
|
+
Service::Record.quickbook_gateway = gateway
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def url(path, params = {})
|
78
|
+
url = File.join(self.api, API::VERSION, API::COMPANY.gsub("?", self.company_id.to_s), path)
|
79
|
+
return "#{url}?#{params.to_uri_query}"
|
80
|
+
end
|
81
|
+
|
82
|
+
public
|
83
|
+
def get(path, parameters = {})
|
84
|
+
return handle_query_result(self.access_token.get(url(path, parameters), {"Accept" => HTTP::JSON}))
|
85
|
+
end
|
86
|
+
|
87
|
+
def query_data(query)
|
88
|
+
return get(API::QUERY, {:query => query})
|
89
|
+
end
|
90
|
+
|
91
|
+
def post(path, parameters, body)
|
92
|
+
return handle_query_result(self.access_token.post(url(path, parameters), body, {"Content-Type" => HTTP::JSON, "Accept" => HTTP::JSON}))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Gateway
|
4
|
+
class Result
|
5
|
+
|
6
|
+
include Errors
|
7
|
+
|
8
|
+
def handle_query_result(response)
|
9
|
+
if(response.code == "200")
|
10
|
+
result = JSON.parse(response.body)
|
11
|
+
|
12
|
+
if result["Fault"]
|
13
|
+
error_message = ""
|
14
|
+
result["Fault"]["Error"].each{|error|
|
15
|
+
error_message = "#{error["Message"]}. #{error["Detail"]}"
|
16
|
+
}
|
17
|
+
|
18
|
+
raise GatewayError, error_message
|
19
|
+
end
|
20
|
+
|
21
|
+
return result["QueryResponse"] if result["QueryResponse"]
|
22
|
+
|
23
|
+
return result
|
24
|
+
else
|
25
|
+
raise GatewayError, response.body
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'errors'
|
2
|
+
|
3
|
+
require 'core_ext/string'
|
4
|
+
require 'core_ext/nil'
|
5
|
+
require 'core_ext/hash'
|
6
|
+
|
7
|
+
require 'gateway/result'
|
8
|
+
require 'gateway/quickbook'
|
9
|
+
|
10
|
+
require 'service/query'
|
11
|
+
require 'service/finder'
|
12
|
+
require 'service/callback'
|
13
|
+
require 'service/persistence'
|
14
|
+
require 'service/record'
|
15
|
+
|
16
|
+
Dir.glob("app/services/*.rb") do|service|
|
17
|
+
require "./#{service}"
|
18
|
+
end
|
19
|
+
|
20
|
+
Dir.glob("quick_book_lib/*.rb") do|library|
|
21
|
+
require "./#{library}"
|
22
|
+
end if(File.exists?("quick_book_lib"))
|
23
|
+
|
24
|
+
Gateway::Quickbook.connect(Gateway::Quickbook::ENVIRONMENT::SANDBOX) if File.exist?("./config/quickbook.yml")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Service
|
2
|
+
module Callback
|
3
|
+
def before_save
|
4
|
+
end
|
5
|
+
|
6
|
+
def after_save
|
7
|
+
end
|
8
|
+
|
9
|
+
def before_create
|
10
|
+
end
|
11
|
+
|
12
|
+
def after_create
|
13
|
+
end
|
14
|
+
|
15
|
+
def before_update
|
16
|
+
end
|
17
|
+
|
18
|
+
def after_update
|
19
|
+
end
|
20
|
+
|
21
|
+
def before_destroy
|
22
|
+
end
|
23
|
+
|
24
|
+
def after_destroy
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Service
|
2
|
+
module Finder
|
3
|
+
def self.extended(base)
|
4
|
+
base.send :extend, Query
|
5
|
+
end
|
6
|
+
|
7
|
+
#Find All records related to options provided
|
8
|
+
#Options are as below
|
9
|
+
# :conditions => where clause. Default is NOTHING
|
10
|
+
# For example, if you want to fetch customer with DisplayName 'Kunal' then :conditions => ["DisplayName = ?", "Kunal"]
|
11
|
+
#
|
12
|
+
# :select => Needed column list. Default is ALL
|
13
|
+
# For ex. if you want Id, DisplayName of all customers then :select => "Id, DisplayName"
|
14
|
+
def find_all(options = {})
|
15
|
+
raise QuickBookgatewayInvalid unless quickbook_gateway
|
16
|
+
|
17
|
+
records = Array.new
|
18
|
+
|
19
|
+
qb_data = quickbook_gateway.query_data(create_query(options))[entity]
|
20
|
+
|
21
|
+
qb_data.each{|qb_record|
|
22
|
+
model = self.new(qb_record)
|
23
|
+
model.is_new_record = false
|
24
|
+
|
25
|
+
records << model
|
26
|
+
} if qb_data
|
27
|
+
|
28
|
+
return records
|
29
|
+
end
|
30
|
+
|
31
|
+
#Only fetch first record as per options provided
|
32
|
+
#Options are as below
|
33
|
+
# Same as Find All
|
34
|
+
# Or you can directly provide Id of record.
|
35
|
+
# For ex. if you want to fetch customer with Id 1, then Customer.find(1)
|
36
|
+
def find(options = {})
|
37
|
+
if(options.class == Fixnum)
|
38
|
+
model = self.new(quickbook_gateway.get("#{entity.downcase}/#{options}")[entity])
|
39
|
+
model.is_new_record = false
|
40
|
+
return model
|
41
|
+
end
|
42
|
+
|
43
|
+
options[:top] = 1
|
44
|
+
return self.find_all(options)[0]
|
45
|
+
end
|
46
|
+
|
47
|
+
#Try to find record on QuickBook Online as per options provided else create instance of new
|
48
|
+
#Options should be an Hash contains columns and values to find record.
|
49
|
+
# For ex. if you want to update customer(Kunal) country to India if exist or create new:
|
50
|
+
# kunal = Customer.find_or_initialize({"DisplayName" => "Kunal")
|
51
|
+
# kunal.Country = "India"
|
52
|
+
# kunal.save!
|
53
|
+
def find_or_initialize(options)
|
54
|
+
return new() if(options.keys.length == 0)
|
55
|
+
|
56
|
+
conditions = [""]
|
57
|
+
options.keys.each{|column|
|
58
|
+
conditions[0] += "AND #{column}=?"
|
59
|
+
conditions << options[column]
|
60
|
+
}
|
61
|
+
|
62
|
+
conditions[0] = conditions[0][4..conditions[0].length].to_s
|
63
|
+
|
64
|
+
qb_record = find({:conditions => conditions})
|
65
|
+
|
66
|
+
qb_record = new(options) unless qb_record
|
67
|
+
|
68
|
+
return qb_record
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Service
|
2
|
+
module Persistence
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.send :include, Callback
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def qb_entity
|
10
|
+
return case self.class.to_s
|
11
|
+
when 'TaxRate' then 'TaxService'
|
12
|
+
else entity.downcase
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
public
|
17
|
+
#Create or Update current instance to QuickBook
|
18
|
+
def save!
|
19
|
+
new_record = self.is_new_record
|
20
|
+
|
21
|
+
before_save
|
22
|
+
before_create if new_record
|
23
|
+
before_update unless new_record
|
24
|
+
|
25
|
+
params = {}
|
26
|
+
params[:operation] = :update unless(self.is_new_record)
|
27
|
+
|
28
|
+
self.attributes = quickbook_gateway.post(qb_entity, params , self.attributes.to_json())[qb_entity]
|
29
|
+
|
30
|
+
after_update unless new_record
|
31
|
+
after_create if new_record
|
32
|
+
after_save
|
33
|
+
end
|
34
|
+
|
35
|
+
#Delete QuickBook record, require Id attribute
|
36
|
+
#This will delete or make record Inactive based on object class.
|
37
|
+
#For Invoice it will delete record from server else make record Active false.
|
38
|
+
def destroy
|
39
|
+
before_destroy
|
40
|
+
|
41
|
+
entities_for_delete = ["INVOICE"]
|
42
|
+
|
43
|
+
raise RecordDeleteError, "Can't delete record without ID" if self["Id"].to_i == 0
|
44
|
+
if(entities_for_delete.index(qb_entity.upcase))
|
45
|
+
raise RecordDeleteError, "Can't delete new record" if self.is_new_record
|
46
|
+
quickbook_gateway.post(qb_entity.downcase, {:operation => :delete} , {"Id" => self["Id"]}.to_json())
|
47
|
+
self.attributes = {}
|
48
|
+
self.is_new_record = true
|
49
|
+
else
|
50
|
+
self.Active = "false"
|
51
|
+
self.save!
|
52
|
+
end
|
53
|
+
|
54
|
+
after_destroy
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Service
|
2
|
+
module Query
|
3
|
+
|
4
|
+
def sanitize_sql(ary)
|
5
|
+
return ary if ary.class != Array
|
6
|
+
|
7
|
+
statement, *values = ary
|
8
|
+
|
9
|
+
return statement.gsub('?') do
|
10
|
+
"'" + values[0].to_s.gsub("'", "\\\\'") +"'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_query(options = {})
|
15
|
+
columns = "*"
|
16
|
+
|
17
|
+
condition = "WHERE #{sanitize_sql(options[:conditions])}" unless(options[:conditions].to_s.blank?)
|
18
|
+
columns = options[:select] unless(options[:select].to_s.blank?)
|
19
|
+
|
20
|
+
max_result = "STARTPOSITION 1 MAXRESULTS #{options[:top]}" if(options[:top].to_i > 0)
|
21
|
+
|
22
|
+
return "SELECT #{columns} FROM #{entity} #{condition} #{max_result}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Service
|
2
|
+
#Base class use to map QuickBook record with Ruby instance
|
3
|
+
#QuickBook API documentation https://developer.intuit.com/v2/apiexplorer?apiname=V3QBO
|
4
|
+
class Record
|
5
|
+
@@quickbook_gateway
|
6
|
+
@@entity
|
7
|
+
|
8
|
+
extend Service::Finder
|
9
|
+
include Service::Persistence
|
10
|
+
|
11
|
+
attr_accessor :attributes, :is_new_record
|
12
|
+
|
13
|
+
#Set instance of Gateway::QuickBook
|
14
|
+
def self.quickbook_gateway=(gateway)
|
15
|
+
@@quickbook_gateway = gateway
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.quickbook_gateway
|
19
|
+
return @@quickbook_gateway
|
20
|
+
end
|
21
|
+
|
22
|
+
def quickbook_gateway
|
23
|
+
return @@quickbook_gateway
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(attributes = {})
|
27
|
+
self.is_new_record = true
|
28
|
+
self.attributes = attributes
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def self.entity
|
33
|
+
self.name.split("::").last
|
34
|
+
end
|
35
|
+
|
36
|
+
def entity
|
37
|
+
self.class.entity
|
38
|
+
end
|
39
|
+
|
40
|
+
public
|
41
|
+
def [](key)
|
42
|
+
return self.attributes[key.to_s]
|
43
|
+
end
|
44
|
+
|
45
|
+
def []=(key, value)
|
46
|
+
self.attributes[key.to_s] = value
|
47
|
+
end
|
48
|
+
|
49
|
+
def method_missing(method_sym, *args, &block)
|
50
|
+
attribute = method_sym.to_s
|
51
|
+
if (args.length == 1 and method_sym.to_s[-1, 1] == "=")
|
52
|
+
self[attribute[(0..method_sym.length - 2)].to_s] = args[0]
|
53
|
+
elsif (self.attributes.keys.index(attribute))
|
54
|
+
return self[attribute]
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: quick_book_gateway
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kunal Lalge
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: oauth
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Connect to QuickBook Online and easily synchronize data.
|
28
|
+
email: kunallalge@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/core_ext/hash.rb
|
34
|
+
- lib/core_ext/nil.rb
|
35
|
+
- lib/core_ext/string.rb
|
36
|
+
- lib/errors.rb
|
37
|
+
- lib/gateway/quickbook.rb
|
38
|
+
- lib/gateway/result.rb
|
39
|
+
- lib/quick_book_gateway.rb
|
40
|
+
- lib/service/callback.rb
|
41
|
+
- lib/service/finder.rb
|
42
|
+
- lib/service/persistence.rb
|
43
|
+
- lib/service/query.rb
|
44
|
+
- lib/service/record.rb
|
45
|
+
homepage: https://rubygems.org/gems/quick_book_gateway
|
46
|
+
licenses: []
|
47
|
+
metadata: {}
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 2.4.8
|
65
|
+
signing_key:
|
66
|
+
specification_version: 4
|
67
|
+
summary: QuickBook Online Gateway
|
68
|
+
test_files: []
|