quickbooks 0.4.0 → 0.9.9
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.tar.gz.sig +0 -0
- data/History.txt +23 -0
- data/License.txt +54 -0
- data/Manifest.txt +73 -0
- data/README.txt +79 -0
- data/Rakefile +17 -78
- data/lib/quickbooks.rb +153 -7
- data/lib/quickbooks/adaptability.rb +67 -0
- data/lib/quickbooks/adapters/http_adapter.rb +94 -0
- data/lib/quickbooks/adapters/https_adapter.rb +57 -0
- data/lib/quickbooks/adapters/ole_adapter.rb +141 -110
- data/lib/quickbooks/adapters/spew_adapter.rb +39 -0
- data/lib/quickbooks/adapters/tcp_adapter.rb +71 -0
- data/lib/quickbooks/adapters/test_adapter.rb +62 -0
- data/lib/quickbooks/element.rb +385 -0
- data/lib/quickbooks/element_collection.rb +105 -0
- data/lib/quickbooks/extlib.rb +8 -0
- data/lib/quickbooks/extlib/assertions.rb +8 -0
- data/lib/quickbooks/extlib/class.rb +98 -0
- data/lib/quickbooks/extlib/days_and_times.rb +10 -0
- data/lib/quickbooks/extlib/days_and_times/duration.rb +362 -0
- data/lib/quickbooks/extlib/days_and_times/numeric.rb +48 -0
- data/lib/quickbooks/extlib/days_and_times/object.rb +19 -0
- data/lib/quickbooks/extlib/days_and_times/time.rb +137 -0
- data/lib/quickbooks/extlib/hash.rb +327 -0
- data/lib/quickbooks/extlib/hook.rb +366 -0
- data/lib/quickbooks/extlib/inflection.rb +436 -0
- data/lib/quickbooks/extlib/logger.rb +202 -0
- data/lib/quickbooks/extlib/object.rb +162 -0
- data/lib/quickbooks/extlib/pathname.rb +15 -0
- data/lib/quickbooks/extlib/rubygems.rb +38 -0
- data/lib/quickbooks/extlib/string.rb +32 -0
- data/lib/quickbooks/extlib/time.rb +41 -0
- data/lib/quickbooks/model.rb +503 -121
- data/lib/quickbooks/option.rb +95 -0
- data/lib/quickbooks/property.rb +69 -0
- data/lib/quickbooks/ruby_ext.rb +251 -0
- data/lib/quickbooks/type.rb +75 -0
- data/lib/quickbooks/types.rb +58 -0
- data/lib/quickbooks/version.rb +10 -0
- data/lib/quickbooks/xsd.rb +243 -0
- data/lib/quickbooks/xsd/attribute.rb +57 -0
- data/lib/quickbooks/xsd/choice.rb +94 -0
- data/lib/quickbooks/xsd/complex_type.rb +77 -0
- data/lib/quickbooks/xsd/element.rb +214 -0
- data/lib/quickbooks/xsd/group.rb +94 -0
- data/lib/quickbooks/xsd/restriction.rb +51 -0
- data/lib/quickbooks/xsd/sequence.rb +85 -0
- data/lib/quickbooks/xsd/simple_type.rb +87 -0
- data/lib/quickbooks/xsd/union.rb +70 -0
- data/lib/quickbooks/xsd/validation.rb +89 -0
- data/lib/quickbooks/xsd/xml_parse.rb +80 -0
- data/quickbooks.gemspec +36 -0
- data/source/qbxml21_demo.xsd +2223 -0
- data/source/qbxmlops_demo.xsd +483 -0
- data/source/qbxmltypes.xsd +160 -0
- data/spec/association_spec.rb +5 -0
- data/spec/element_collection_spec.rb +8 -0
- data/spec/find_spec.rb +30 -0
- data/spec/lib/quickbooks/element_spec.rb +83 -0
- data/spec/lib/quickbooks/elements/sales_order_add_spec.rb +14 -0
- data/spec/lib/quickbooks/elements/vendor_add_spec.rb +78 -0
- data/spec/lib/quickbooks/model_spec.rb +89 -0
- data/spec/lib/quickbooks/models/customer_spec.rb +52 -0
- data/spec/lib/quickbooks/models/invoice_spec.rb +12 -0
- data/spec/lib/quickbooks/models/sales_order_line_spec.rb +0 -0
- data/spec/lib/quickbooks/models/sales_order_spec.rb +145 -0
- data/spec/lib/quickbooks/xsd/choice_spec.rb +18 -0
- data/spec/pending_spec.rb +11 -0
- data/spec/quickbooks_spec.rb +38 -0
- data/spec/repeatable_spec.rb +20 -0
- data/spec/requires_spec.rb +10 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/validation_spec.rb +24 -0
- metadata +133 -285
- metadata.gz.sig +0 -0
- data/LICENSE +0 -16
- data/README +0 -49
- data/doc/classes/Array.html +0 -368
- data/doc/classes/Array.src/M000026.html +0 -18
- data/doc/classes/Array.src/M000027.html +0 -21
- data/doc/classes/Array.src/M000028.html +0 -18
- data/doc/classes/Array.src/M000029.html +0 -19
- data/doc/classes/Array.src/M000030.html +0 -18
- data/doc/classes/Array.src/M000031.html +0 -23
- data/doc/classes/Array.src/M000032.html +0 -18
- data/doc/classes/Array.src/M000033.html +0 -18
- data/doc/classes/Array.src/M000034.html +0 -18
- data/doc/classes/Array.src/M000035.html +0 -19
- data/doc/classes/Array.src/M000036.html +0 -18
- data/doc/classes/Array.src/M000037.html +0 -23
- data/doc/classes/Array.src/M000038.html +0 -18
- data/doc/classes/Array.src/M000039.html +0 -21
- data/doc/classes/Array.src/M000040.html +0 -18
- data/doc/classes/Array.src/M000041.html +0 -18
- data/doc/classes/Class.html +0 -171
- data/doc/classes/Class.src/M000042.html +0 -18
- data/doc/classes/Class.src/M000043.html +0 -18
- data/doc/classes/Hash.html +0 -516
- data/doc/classes/Hash.src/M000001.html +0 -18
- data/doc/classes/Hash.src/M000002.html +0 -21
- data/doc/classes/Hash.src/M000003.html +0 -18
- data/doc/classes/Hash.src/M000004.html +0 -21
- data/doc/classes/Hash.src/M000005.html +0 -18
- data/doc/classes/Hash.src/M000006.html +0 -20
- data/doc/classes/Hash.src/M000007.html +0 -18
- data/doc/classes/Hash.src/M000008.html +0 -22
- data/doc/classes/Hash.src/M000009.html +0 -18
- data/doc/classes/Hash.src/M000010.html +0 -21
- data/doc/classes/Hash.src/M000011.html +0 -24
- data/doc/classes/Hash.src/M000012.html +0 -18
- data/doc/classes/Hash.src/M000013.html +0 -21
- data/doc/classes/Hash.src/M000014.html +0 -18
- data/doc/classes/Hash.src/M000015.html +0 -21
- data/doc/classes/Hash.src/M000016.html +0 -18
- data/doc/classes/Hash.src/M000017.html +0 -21
- data/doc/classes/Hash.src/M000018.html +0 -27
- data/doc/classes/Hash.src/M000019.html +0 -20
- data/doc/classes/Hash.src/M000020.html +0 -18
- data/doc/classes/Hash.src/M000021.html +0 -18
- data/doc/classes/Hash.src/M000022.html +0 -18
- data/doc/classes/Hash.src/M000023.html +0 -18
- data/doc/classes/Hash.src/M000024.html +0 -18
- data/doc/classes/Hash.src/M000025.html +0 -28
- data/doc/classes/Object.html +0 -278
- data/doc/classes/Object.src/M000044.html +0 -24
- data/doc/classes/Object.src/M000045.html +0 -19
- data/doc/classes/Object.src/M000046.html +0 -24
- data/doc/classes/Object.src/M000047.html +0 -18
- data/doc/classes/Object.src/M000048.html +0 -19
- data/doc/classes/Object.src/M000049.html +0 -18
- data/doc/classes/Object.src/M000050.html +0 -18
- data/doc/classes/Object.src/M000051.html +0 -18
- data/doc/classes/Qbxml.html +0 -131
- data/doc/classes/Qbxml/Request.html +0 -224
- data/doc/classes/Qbxml/Request.src/M000132.html +0 -66
- data/doc/classes/Qbxml/Request.src/M000133.html +0 -19
- data/doc/classes/Qbxml/Request.src/M000134.html +0 -83
- data/doc/classes/Qbxml/RequestSet.html +0 -199
- data/doc/classes/Qbxml/RequestSet.src/M000135.html +0 -22
- data/doc/classes/Qbxml/RequestSet.src/M000136.html +0 -26
- data/doc/classes/Qbxml/RequestSet.src/M000137.html +0 -18
- data/doc/classes/Qbxml/RequestSet.src/M000138.html +0 -22
- data/doc/classes/Qbxml/Response.html +0 -302
- data/doc/classes/Qbxml/Response.src/M000123.html +0 -24
- data/doc/classes/Qbxml/Response.src/M000124.html +0 -19
- data/doc/classes/Qbxml/Response.src/M000125.html +0 -55
- data/doc/classes/Qbxml/Response.src/M000126.html +0 -18
- data/doc/classes/Qbxml/Response.src/M000127.html +0 -18
- data/doc/classes/Qbxml/Response.src/M000128.html +0 -18
- data/doc/classes/Qbxml/Response.src/M000129.html +0 -18
- data/doc/classes/Qbxml/Response.src/M000130.html +0 -18
- data/doc/classes/Qbxml/Response.src/M000131.html +0 -63
- data/doc/classes/Qbxml/ResponseSet.html +0 -244
- data/doc/classes/Qbxml/ResponseSet.src/M000116.html +0 -22
- data/doc/classes/Qbxml/ResponseSet.src/M000117.html +0 -26
- data/doc/classes/Qbxml/ResponseSet.src/M000118.html +0 -24
- data/doc/classes/Qbxml/ResponseSet.src/M000119.html +0 -20
- data/doc/classes/Qbxml/ResponseSet.src/M000120.html +0 -27
- data/doc/classes/Qbxml/ResponseSet.src/M000121.html +0 -18
- data/doc/classes/Qbxml/ResponseSet.src/M000122.html +0 -18
- data/doc/classes/Quickbooks.html +0 -196
- data/doc/classes/Quickbooks/Address.html +0 -139
- data/doc/classes/Quickbooks/Address.src/M000115.html +0 -19
- data/doc/classes/Quickbooks/Base.html +0 -567
- data/doc/classes/Quickbooks/Base.src/M000095.html +0 -18
- data/doc/classes/Quickbooks/Base.src/M000096.html +0 -20
- data/doc/classes/Quickbooks/Base.src/M000097.html +0 -19
- data/doc/classes/Quickbooks/Base.src/M000098.html +0 -18
- data/doc/classes/Quickbooks/Base.src/M000099.html +0 -19
- data/doc/classes/Quickbooks/Base.src/M000100.html +0 -30
- data/doc/classes/Quickbooks/Base.src/M000101.html +0 -22
- data/doc/classes/Quickbooks/Base.src/M000102.html +0 -30
- data/doc/classes/Quickbooks/Base.src/M000103.html +0 -19
- data/doc/classes/Quickbooks/Base.src/M000104.html +0 -19
- data/doc/classes/Quickbooks/Base.src/M000105.html +0 -18
- data/doc/classes/Quickbooks/Base.src/M000106.html +0 -18
- data/doc/classes/Quickbooks/Base.src/M000107.html +0 -19
- data/doc/classes/Quickbooks/Base.src/M000108.html +0 -18
- data/doc/classes/Quickbooks/Base.src/M000109.html +0 -45
- data/doc/classes/Quickbooks/Base.src/M000110.html +0 -18
- data/doc/classes/Quickbooks/Base.src/M000111.html +0 -18
- data/doc/classes/Quickbooks/Base.src/M000112.html +0 -20
- data/doc/classes/Quickbooks/BillAddress.html +0 -113
- data/doc/classes/Quickbooks/CreditCardInfo.html +0 -113
- data/doc/classes/Quickbooks/Customer.html +0 -113
- data/doc/classes/Quickbooks/CustomerTypeRef.html +0 -113
- data/doc/classes/Quickbooks/Deleted.html +0 -113
- data/doc/classes/Quickbooks/ItemSalesTaxRef.html +0 -113
- data/doc/classes/Quickbooks/JobTypeRef.html +0 -113
- data/doc/classes/Quickbooks/ListDeleted.html +0 -139
- data/doc/classes/Quickbooks/ListDeleted.src/M000114.html +0 -18
- data/doc/classes/Quickbooks/ListItem.html +0 -170
- data/doc/classes/Quickbooks/ListItem.src/M000093.html +0 -20
- data/doc/classes/Quickbooks/ListItem.src/M000094.html +0 -18
- data/doc/classes/Quickbooks/Model.html +0 -462
- data/doc/classes/Quickbooks/Model.src/M000073.html +0 -24
- data/doc/classes/Quickbooks/Model.src/M000074.html +0 -19
- data/doc/classes/Quickbooks/Model.src/M000075.html +0 -18
- data/doc/classes/Quickbooks/Model.src/M000076.html +0 -18
- data/doc/classes/Quickbooks/Model.src/M000077.html +0 -18
- data/doc/classes/Quickbooks/Model.src/M000078.html +0 -22
- data/doc/classes/Quickbooks/Model.src/M000079.html +0 -32
- data/doc/classes/Quickbooks/Model.src/M000080.html +0 -32
- data/doc/classes/Quickbooks/Model.src/M000081.html +0 -18
- data/doc/classes/Quickbooks/Model.src/M000082.html +0 -18
- data/doc/classes/Quickbooks/Model.src/M000083.html +0 -22
- data/doc/classes/Quickbooks/Model.src/M000084.html +0 -23
- data/doc/classes/Quickbooks/Model.src/M000085.html +0 -18
- data/doc/classes/Quickbooks/Model.src/M000086.html +0 -21
- data/doc/classes/Quickbooks/Model.src/M000087.html +0 -26
- data/doc/classes/Quickbooks/Model.src/M000088.html +0 -26
- data/doc/classes/Quickbooks/Model.src/M000089.html +0 -26
- data/doc/classes/Quickbooks/Model.src/M000090.html +0 -21
- data/doc/classes/Quickbooks/Model.src/M000091.html +0 -23
- data/doc/classes/Quickbooks/OleAdapter.html +0 -129
- data/doc/classes/Quickbooks/OleAdapter/Connection.html +0 -343
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000058.html +0 -18
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000059.html +0 -19
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000060.html +0 -24
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000061.html +0 -18
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000062.html +0 -18
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000063.html +0 -21
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000064.html +0 -24
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000065.html +0 -20
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000066.html +0 -24
- data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000067.html +0 -24
- data/doc/classes/Quickbooks/OleAdapter/Ole.html +0 -209
- data/doc/classes/Quickbooks/OleAdapter/Ole.src/M000068.html +0 -21
- data/doc/classes/Quickbooks/OleAdapter/Ole.src/M000069.html +0 -22
- data/doc/classes/Quickbooks/OleAdapter/Ole.src/M000070.html +0 -18
- data/doc/classes/Quickbooks/ParentRef.html +0 -113
- data/doc/classes/Quickbooks/PreferredPaymentMethodRef.html +0 -113
- data/doc/classes/Quickbooks/PriceLevelRef.html +0 -113
- data/doc/classes/Quickbooks/QbxmlDebugAdapter.html +0 -111
- data/doc/classes/Quickbooks/QbxmlDebugAdapter/Connection.html +0 -139
- data/doc/classes/Quickbooks/QbxmlDebugAdapter/Connection.src/M000057.html +0 -18
- data/doc/classes/Quickbooks/Ref.html +0 -146
- data/doc/classes/Quickbooks/Ref.src/M000113.html +0 -18
- data/doc/classes/Quickbooks/SalesRepRef.html +0 -113
- data/doc/classes/Quickbooks/SalesTaxCodeRef.html +0 -113
- data/doc/classes/Quickbooks/ShipAddress.html +0 -113
- data/doc/classes/Quickbooks/TermsRef.html +0 -113
- data/doc/classes/Quickbooks/Transaction.html +0 -154
- data/doc/classes/Quickbooks/Transaction.src/M000071.html +0 -19
- data/doc/classes/Quickbooks/Transaction.src/M000072.html +0 -18
- data/doc/classes/Quickbooks/TxnDeleted.html +0 -139
- data/doc/classes/Quickbooks/TxnDeleted.src/M000092.html +0 -18
- data/doc/classes/String.html +0 -203
- data/doc/classes/String.src/M000052.html +0 -18
- data/doc/classes/String.src/M000053.html +0 -18
- data/doc/classes/String.src/M000054.html +0 -18
- data/doc/classes/String.src/M000055.html +0 -18
- data/doc/classes/String.src/M000056.html +0 -18
- data/doc/created.rid +0 -1
- data/doc/files/LICENSE.html +0 -129
- data/doc/files/README.html +0 -225
- data/doc/files/lib/qbxml/request_rb.html +0 -110
- data/doc/files/lib/qbxml/response_rb.html +0 -109
- data/doc/files/lib/qbxml/support_rb.html +0 -101
- data/doc/files/lib/qbxml_rb.html +0 -122
- data/doc/files/lib/quickbooks/adapters/ole_adapter_rb.html +0 -108
- data/doc/files/lib/quickbooks/adapters/qbxml_debug_adapter_rb.html +0 -108
- data/doc/files/lib/quickbooks/base_rb.html +0 -327
- data/doc/files/lib/quickbooks/model_rb.html +0 -108
- data/doc/files/lib/quickbooks/models/common/address_rb.html +0 -101
- data/doc/files/lib/quickbooks/models/common/all_refs_rb.html +0 -101
- data/doc/files/lib/quickbooks/models/customer/credit_card_info_rb.html +0 -101
- data/doc/files/lib/quickbooks/models/customer_rb.html +0 -110
- data/doc/files/lib/quickbooks/models/deleted_rb.html +0 -101
- data/doc/files/lib/quickbooks/models/list_item_rb.html +0 -101
- data/doc/files/lib/quickbooks/models/transaction_rb.html +0 -101
- data/doc/files/lib/quickbooks/ruby_magic_rb.html +0 -120
- data/doc/files/lib/quickbooks_rb.html +0 -125
- data/doc/fr_class_index.html +0 -64
- data/doc/fr_file_index.html +0 -45
- data/doc/fr_method_index.html +0 -164
- data/doc/index.html +0 -24
- data/doc/rdoc-style.css +0 -208
- data/lib/qbxml.rb +0 -3
- data/lib/qbxml/request.rb +0 -207
- data/lib/qbxml/response.rb +0 -197
- data/lib/qbxml/support.rb +0 -130
- data/lib/quickbooks/adapters/qbxml_debug_adapter.rb +0 -10
- data/lib/quickbooks/base.rb +0 -277
- data/lib/quickbooks/models/common/address.rb +0 -14
- data/lib/quickbooks/models/common/all_refs.rb +0 -37
- data/lib/quickbooks/models/customer.rb +0 -70
- data/lib/quickbooks/models/customer/credit_card_info.rb +0 -5
- data/lib/quickbooks/models/deleted.rb +0 -22
- data/lib/quickbooks/models/list_item.rb +0 -41
- data/lib/quickbooks/models/transaction.rb +0 -49
- data/lib/quickbooks/ruby_magic.rb +0 -213
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Kernel
|
|
2
|
+
def try_retry(times, *exception_classes)
|
|
3
|
+
options = exception_classes.last.is_a?(Hash) ? exception_classes.pop : {}
|
|
4
|
+
raise ArgumentError unless block_given?
|
|
5
|
+
retried = 0
|
|
6
|
+
begin
|
|
7
|
+
yield
|
|
8
|
+
rescue *exception_classes => e
|
|
9
|
+
puts "Error: #{e.inspect}. Retrying..." if $DEBUG
|
|
10
|
+
options[:before_retry].call if options[:before_retry]
|
|
11
|
+
retried += 1
|
|
12
|
+
if retried < 2
|
|
13
|
+
retry
|
|
14
|
+
else
|
|
15
|
+
raise e, "(Retried once)"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Quickbooks
|
|
22
|
+
module Adaptability
|
|
23
|
+
def self.extended(base)
|
|
24
|
+
base.send(:class_variable_set, :@@connection_adapter, nil)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def use_adapter(adapter, *args)
|
|
28
|
+
# Should complain if the adapter doesn't exist.
|
|
29
|
+
@@connection_adapter = try_retry(1, NameError, :before_retry => lambda {require "quickbooks/adapters/#{adapter.to_s}_adapter"}) {
|
|
30
|
+
Object.module_eval("::Quickbooks::Adapters::#{adapter.to_s.camel_case}Adapter::Connection", __FILE__, __LINE__)
|
|
31
|
+
}
|
|
32
|
+
@@connection ||= nil
|
|
33
|
+
@connection ||= nil
|
|
34
|
+
@@connection.close if @@connection
|
|
35
|
+
@connection.close if @connection
|
|
36
|
+
@@connection = @connection = nil
|
|
37
|
+
@@connection_args = args
|
|
38
|
+
return @@connection_adapter
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Establishes a connection to the Quickbooks API
|
|
42
|
+
def establish_connection(*args)
|
|
43
|
+
@@connection_adapter ||= use_adapter(:spew)
|
|
44
|
+
@@connection = @@connection_adapter.new(*args)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns the current Connection
|
|
48
|
+
def connection
|
|
49
|
+
@@connection_args ||= []
|
|
50
|
+
(@connection ||= nil) || (@@connection ||= self.establish_connection(*(@@connection_args)))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Sets the current Connection.
|
|
54
|
+
#
|
|
55
|
+
# This is normally not needed, but in the case that you may want to connect a separate connection to Quickbooks,
|
|
56
|
+
# you can use this method to explicitly set the connection in a class that inherits from Quickbooks::Base.
|
|
57
|
+
# Quickbooks::Base.connection = Quickbooks::Connection.new('My Test App', 'C:\\Some File.QBW', 'user', 'pass')
|
|
58
|
+
def connection=(conn)
|
|
59
|
+
raise ArgumentError, "Cannot set connection to anything but a (*)Adapter::Connection object" unless conn.class.name =~ /Adapter::Connection$/
|
|
60
|
+
@connection = conn
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def connected?
|
|
64
|
+
@connection.nil? ? false : @connection.connected?
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'base64'
|
|
3
|
+
|
|
4
|
+
module Kernel
|
|
5
|
+
def try_retry(times, *exception_classes)
|
|
6
|
+
options = exception_classes.last.is_a?(Hash) ? exception_classes.pop : {}
|
|
7
|
+
raise ArgumentError unless block_given?
|
|
8
|
+
retried = 0
|
|
9
|
+
begin
|
|
10
|
+
yield
|
|
11
|
+
rescue *exception_classes => e
|
|
12
|
+
puts "Error: #{e.inspect}. Retrying..." if $DEBUG
|
|
13
|
+
options[:before_retry].call if options[:before_retry]
|
|
14
|
+
retried += 1
|
|
15
|
+
if retried < 2
|
|
16
|
+
retry
|
|
17
|
+
else
|
|
18
|
+
raise e, "(Retried once)"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# This adapter sends QBXML via an HTTP connection which is kept open for each session, and expects intelligent real-time QB responses from the other end.
|
|
25
|
+
module Quickbooks
|
|
26
|
+
module Adapters
|
|
27
|
+
module HttpAdapter
|
|
28
|
+
# Whatever we do here, we just have to be sure that decrypt(encrypt(data)) == data
|
|
29
|
+
module SimpleEncryption
|
|
30
|
+
def encrypt(data)
|
|
31
|
+
data
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def decrypt(data)
|
|
35
|
+
data
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Connection
|
|
40
|
+
include SimpleEncryption
|
|
41
|
+
def initialize(host, username, password)
|
|
42
|
+
host, port = host.split(/:/,2) if host =~ /:/
|
|
43
|
+
port = nil if port == ''
|
|
44
|
+
@host = host
|
|
45
|
+
@port = port || 56225
|
|
46
|
+
@username = username
|
|
47
|
+
@password = password
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns true if there is an open connection to Quickbooks, false if not. Use session? to determine an open session.
|
|
51
|
+
def connected?
|
|
52
|
+
(@connection ||= new_connection).started?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def close
|
|
56
|
+
@connection.finish rescue nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Sends a request to Quickbooks. This request should be a valid QBXML request. Use Qbxml::Request to generate valid requests.
|
|
60
|
+
def send_xml(xml)
|
|
61
|
+
# TODO: request.basic_auth url.user, url.password
|
|
62
|
+
res = try_retry(1, EOFError, :before_retry => lambda {@connection.finish}) {
|
|
63
|
+
connection.post('/ProcessRequest', encrypt(xml), {'Content-Type' => 'application/xml', 'Authorization' => "Basic #{Base64.encode64(@username+':'+@password).gsub(/\n/,'')}"})
|
|
64
|
+
}
|
|
65
|
+
response = decrypt(res.body)
|
|
66
|
+
return response
|
|
67
|
+
# rescue => e
|
|
68
|
+
# Rescue from the error: I guess we have to return a pretend response that says the network failed.
|
|
69
|
+
# raise "Failsafe not yet implemented!: #{e.inspect}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def qbxml_versions
|
|
73
|
+
res = try_retry(1, EOFError, :before_retry => lambda {@connection.finish}) { connection.get('/QBXMLVersionsForSession', {'Authorization' => "Basic #{Base64.encode64(@username+':'+@password).gsub(/\n/,'')}"}) }
|
|
74
|
+
response = decrypt(res.body)
|
|
75
|
+
return response.chomp.split(/,/)
|
|
76
|
+
end
|
|
77
|
+
def latest_qbxml_version
|
|
78
|
+
@latest_qbxml_version ||= qbxml_versions.sort.last
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns the active connection to Quickbooks, or creates a new one if none exists.
|
|
82
|
+
def connection
|
|
83
|
+
@connection.start unless connected?
|
|
84
|
+
@connection
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
def new_connection
|
|
89
|
+
Net::HTTP.new(@host, @port)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'net/https'
|
|
2
|
+
require File.dirname(__FILE__) + '/http_adapter'
|
|
3
|
+
|
|
4
|
+
module Quickbooks
|
|
5
|
+
module Adapters
|
|
6
|
+
module HttpsAdapter
|
|
7
|
+
# Whatever we do here, we just have to be sure that decrypt(encrypt(data)) == data
|
|
8
|
+
module SimpleEncryption
|
|
9
|
+
def encrypt(data)
|
|
10
|
+
data
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def decrypt(data)
|
|
14
|
+
data
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Connection < HttpAdapter::Connection
|
|
19
|
+
private
|
|
20
|
+
def new_connection
|
|
21
|
+
root_ca_file = '/Users/daniel/Projects/qbonline/demoCA/cacert.pem'
|
|
22
|
+
http = Net::HTTP.new(@ip, @port)
|
|
23
|
+
http.use_ssl = true
|
|
24
|
+
if File.exist?(root_ca_file)
|
|
25
|
+
http.ca_file = root_ca_file
|
|
26
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
27
|
+
# http.verify_depth = 5
|
|
28
|
+
http.verify_depth = 5
|
|
29
|
+
http.enable_post_connection_check = false #true
|
|
30
|
+
else
|
|
31
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
32
|
+
end
|
|
33
|
+
http
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Fixes the transfer-threading problem with net/http. I don't really need a timeout for this implementation.
|
|
41
|
+
class ::Net::BufferedIO
|
|
42
|
+
private
|
|
43
|
+
def rbuf_fill
|
|
44
|
+
begin
|
|
45
|
+
@rbuf << @io.read_nonblock(4096)
|
|
46
|
+
rescue Errno::EWOULDBLOCK
|
|
47
|
+
if IO.select([@io], nil, nil, (@read_timeout ||= 5))
|
|
48
|
+
@rbuf << @io.read_nonblock(4096)
|
|
49
|
+
else
|
|
50
|
+
raise Timeout::TimeoutError
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
class ::OpenSSL::SSL::SSLSocket
|
|
56
|
+
alias :read_nonblock :readpartial
|
|
57
|
+
end
|
|
@@ -8,140 +8,171 @@ module Quickbooks
|
|
|
8
8
|
# Quickbooks does not allow the user to close the company or exit the program until all outside applications are
|
|
9
9
|
# disconnected from its API. Therefore, this class includes an at_exit hook that automatically closes all open
|
|
10
10
|
# connections for you when your program exits.
|
|
11
|
-
module
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# Pass in the OLE name of the application you want connected to, and the name of a type library, if you need one.
|
|
17
|
-
def initialize(ole_app, type_library=nil)
|
|
18
|
-
@ole_app = ole_app
|
|
19
|
-
@type_library = type_library
|
|
20
|
-
@ole = WIN32OLE.new(ole_app)
|
|
21
|
-
self.classes
|
|
11
|
+
module Adapters
|
|
12
|
+
module OleAdapter
|
|
13
|
+
def self.debug!
|
|
14
|
+
@debug = true
|
|
22
15
|
end
|
|
16
|
+
def self.debug?
|
|
17
|
+
@debug ||= false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Ole
|
|
21
|
+
# Simply holds the actual OLE object.
|
|
22
|
+
attr_reader :ole
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# ...
|
|
31
|
-
def get_variable(var_name)
|
|
32
|
-
return nil unless @type_library
|
|
33
|
-
self.classes.each do |class_name|
|
|
34
|
-
found = self.constant_for(class_name.name).variables.find {|var| var.name == var_name}
|
|
35
|
-
return found if found
|
|
24
|
+
# Pass in the OLE name of the application you want connected to, and the name of a type library, if you need one.
|
|
25
|
+
def initialize(ole_app, type_library=nil)
|
|
26
|
+
@ole_app = ole_app
|
|
27
|
+
@type_library = type_library
|
|
28
|
+
@ole = WIN32OLE.new(ole_app)
|
|
29
|
+
self.classes
|
|
36
30
|
end
|
|
37
|
-
end
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
# Finds an OLE variable in the OLE type library, if you specified one in new().
|
|
33
|
+
# This navigates the OLE classes and constants for you and returns the variable that matches the _var_name_ you specify.
|
|
34
|
+
# This is used with Quickbooks to get the qbFileOpenDoNotCare parameter to pass to the OpenConnection2 method:
|
|
35
|
+
# @quickbooks = Ole.new('QBXMLRP2.RequestProcessor', 'QBXMLRP2 1.0 Type Library')
|
|
36
|
+
# @quickbooks.OpenConnection2('','Sample Application',@quickbooks.get_variable('localQBD'))
|
|
37
|
+
# @session = @quickbooks.BeginSession('',@quickbooks.get_variable('qbFileOpenDoNotCare'))
|
|
38
|
+
# ...
|
|
39
|
+
def get_variable(var_name)
|
|
40
|
+
return nil unless @type_library
|
|
41
|
+
self.classes.each do |class_name|
|
|
42
|
+
found = self.constant_for(class_name.name).variables.find {|var| var.name == var_name}
|
|
43
|
+
return found if found
|
|
44
|
+
end
|
|
45
|
+
end
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
def classes #:nodoc:
|
|
48
|
+
return nil unless @type_library
|
|
49
|
+
@classes ||= WIN32OLE_TYPE.ole_classes(@type_library)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def constant_for(class_name) #:nodoc:
|
|
53
|
+
return nil unless @type_library
|
|
54
|
+
@constant_for ||= {}
|
|
55
|
+
@constant_for[class_name] ||= WIN32OLE_TYPE.new(@type_library, class_name)
|
|
56
|
+
end
|
|
49
57
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
# method_missing sends all other methods to the OLE object, so you can treat this object as your OLE object.
|
|
59
|
+
def method_missing(method_name, *args)
|
|
60
|
+
@ole.send(method_name, *args)
|
|
61
|
+
end
|
|
53
62
|
end
|
|
54
|
-
end
|
|
55
63
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
class Connection
|
|
65
|
+
class << self #:nodoc: all
|
|
66
|
+
def connections
|
|
67
|
+
@connections ||= []
|
|
68
|
+
end
|
|
69
|
+
def connections=(v)
|
|
70
|
+
# raise ArgumentError, "Quickbooks::Connection.connections can only contain Quickbooks::Connection objects, but contains #{v.collect {|c| c.class.name}.uniq.join(', ')} objects" unless v.collect {|c| c.class.name}.uniq == ['Quickbooks::Connection']
|
|
71
|
+
@connections = v
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
at_exit do
|
|
75
|
+
Connection.connections.each do |conn|
|
|
76
|
+
conn.close rescue nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
60
79
|
end
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
80
|
+
|
|
81
|
+
# Initializes an instance of Quickbooks::Connection.
|
|
82
|
+
# - _application_name_ is required.
|
|
83
|
+
# - _file_ is optional, in which case the connection will be made to the currently open company file in the Quickbooks application.
|
|
84
|
+
# - _user_ and _password_ may be required if you have specified a specific file to open.
|
|
85
|
+
# - _connection_type_ can be one of: ['unknown', 'localQBD', 'remoteQBD', 'localQBDLaunchUI', 'remoteQBOE']
|
|
86
|
+
# - _connection_mode_ can be one of: ['SingleUser', 'MultiUser', 'DoNotCare']
|
|
87
|
+
def initialize(application_name='RubyApplication', file='', connection_type='localQBD', connection_mode='DoNotCare')
|
|
88
|
+
@file = file
|
|
89
|
+
@application_name = application_name
|
|
90
|
+
@quickbooks = Ole.new('QBXMLRP2.RequestProcessor', 'QBXMLRP2 1.0 Type Library')
|
|
91
|
+
@connection_type = @quickbooks.get_variable(connection_type)
|
|
92
|
+
@connection_mode = @quickbooks.get_variable('qbFileOpen'+connection_mode)
|
|
64
93
|
end
|
|
65
94
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
95
|
+
# Sets up the login to the QuickBooks company file.
|
|
96
|
+
def login(username, password)
|
|
97
|
+
@user = username
|
|
98
|
+
@password = password
|
|
70
99
|
end
|
|
71
|
-
end
|
|
72
100
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
# - _connection_type_ can be one of: ['unknown', 'localQBD', 'remoteQBD', 'localQBDLaunchUI', 'remoteQBOE']
|
|
78
|
-
# - _connection_mode_ can be one of: ['SingleUser', 'MultiUser', 'DoNotCare']
|
|
79
|
-
def initialize(application_name='RubyApplication', file='', user='', password='', connection_type='localQBD', connection_mode='DoNotCare')
|
|
80
|
-
@file = file
|
|
81
|
-
@user = user
|
|
82
|
-
@password = password
|
|
83
|
-
@application_name = application_name
|
|
84
|
-
@quickbooks = Ole.new('QBXMLRP2.RequestProcessor', 'QBXMLRP2 1.0 Type Library')
|
|
85
|
-
@connection_type = @quickbooks.get_variable(connection_type)
|
|
86
|
-
@connection_mode = @quickbooks.get_variable('qbFileOpen'+connection_mode)
|
|
87
|
-
end
|
|
101
|
+
# Returns true if there is an open connection to Quickbooks, false if not. Use session? to determine an open session.
|
|
102
|
+
def connected?
|
|
103
|
+
@connected ||= false
|
|
104
|
+
end
|
|
88
105
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
# Close the connection to Quickbooks. Automatically ends the session, if there is one.
|
|
107
|
+
def close
|
|
108
|
+
end_session
|
|
109
|
+
if connected? && connection.CloseConnection.nil?
|
|
110
|
+
@connected = false
|
|
111
|
+
@connection = nil
|
|
112
|
+
Connection.connections = Connection.connections - [self]
|
|
113
|
+
end
|
|
114
|
+
return !@connected # Returns false if CloseConnection failed.
|
|
115
|
+
end
|
|
93
116
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
# Returns true if there is an active session. Use connected? to determine whether you are connected or not.
|
|
118
|
+
def session?
|
|
119
|
+
!@session.nil?
|
|
120
|
+
end
|
|
98
121
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
122
|
+
# Sends a request to Quickbooks. This request should be a valid QBXML request. Use Qbxml::Request to generate valid requests.
|
|
123
|
+
def send_xml(xml)
|
|
124
|
+
warn "Sending XML:\n#{xml}" if OleAdapter.debug?
|
|
125
|
+
connection.ProcessRequest(session, xml)
|
|
126
|
+
rescue => e
|
|
127
|
+
raise e, "ERROR processing request:\n#{xml}\n -> #{e.inspect}" # Reraises the original error, only this way we got the xml output
|
|
128
|
+
end
|
|
106
129
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@connected = true
|
|
114
|
-
@connection
|
|
130
|
+
# Check the version of QB
|
|
131
|
+
def qbxml_versions
|
|
132
|
+
connection.QBXMLVersionsForSession(session)
|
|
133
|
+
end
|
|
134
|
+
def latest_qbxml_version
|
|
135
|
+
@latest_qbxml_version ||= qbxml_versions.sort.last
|
|
115
136
|
end
|
|
116
|
-
end
|
|
117
137
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
138
|
+
# Returns the active connection to Quickbooks, or creates a new one if none exists.
|
|
139
|
+
def connection
|
|
140
|
+
(@connection ||= nil) || begin
|
|
141
|
+
@connection = @quickbooks.ole
|
|
142
|
+
begin
|
|
143
|
+
@connection.OpenConnection2('123',@application_name,@connection_type)
|
|
144
|
+
rescue WIN32OLERuntimeError => e
|
|
145
|
+
if e.inspect =~ /unknown property or method `OpenConnection2'/
|
|
146
|
+
@connection.OpenConnection('123',@application_name)
|
|
147
|
+
else
|
|
148
|
+
raise e
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
Connection.connections << self
|
|
152
|
+
@connected = true
|
|
153
|
+
@connection
|
|
154
|
+
end
|
|
122
155
|
end
|
|
123
|
-
end
|
|
124
156
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
false
|
|
157
|
+
# Begin a session to Quickbooks.
|
|
158
|
+
def session
|
|
159
|
+
(@session ||= nil) || begin
|
|
160
|
+
# Should set personal-data-pref to pdpNotNeeded by default, so that it doesn't ask.
|
|
161
|
+
# connection.PutPersonalDataPref(@quickbooks.get_variable('pdpNotNeeded'))
|
|
162
|
+
@session = connection.BeginSession(@file,@connection_mode)
|
|
163
|
+
end
|
|
133
164
|
end
|
|
134
|
-
end
|
|
135
165
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
166
|
+
# End the current Quickbooks session. After ending a session, a new session may be reopened if desired.
|
|
167
|
+
def end_session
|
|
168
|
+
if !@session.nil?
|
|
169
|
+
connection.EndSession(@session)
|
|
170
|
+
@session = nil
|
|
171
|
+
true
|
|
172
|
+
else
|
|
173
|
+
false
|
|
174
|
+
end
|
|
143
175
|
end
|
|
144
|
-
return !@connected # Returns false if CloseConnection failed.
|
|
145
176
|
end
|
|
146
177
|
end
|
|
147
178
|
end
|