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 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
@@ -0,0 +1,5 @@
1
+ class Hash
2
+ def to_uri_query
3
+ return self.map{|key, value| "#{key}=#{URI.encode_www_form_component(value)}" if value}.select{|value| value if value}.join("&")
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class NilClass
2
+ def blank?
3
+ return true
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class String
2
+ BLANK_REGEX = /\A[[:space:]]*\z/
3
+
4
+ def blank?
5
+ return BLANK_REGEX === self
6
+ end
7
+ end
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: []