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.
Files changed (293) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.txt +23 -0
  3. data/License.txt +54 -0
  4. data/Manifest.txt +73 -0
  5. data/README.txt +79 -0
  6. data/Rakefile +17 -78
  7. data/lib/quickbooks.rb +153 -7
  8. data/lib/quickbooks/adaptability.rb +67 -0
  9. data/lib/quickbooks/adapters/http_adapter.rb +94 -0
  10. data/lib/quickbooks/adapters/https_adapter.rb +57 -0
  11. data/lib/quickbooks/adapters/ole_adapter.rb +141 -110
  12. data/lib/quickbooks/adapters/spew_adapter.rb +39 -0
  13. data/lib/quickbooks/adapters/tcp_adapter.rb +71 -0
  14. data/lib/quickbooks/adapters/test_adapter.rb +62 -0
  15. data/lib/quickbooks/element.rb +385 -0
  16. data/lib/quickbooks/element_collection.rb +105 -0
  17. data/lib/quickbooks/extlib.rb +8 -0
  18. data/lib/quickbooks/extlib/assertions.rb +8 -0
  19. data/lib/quickbooks/extlib/class.rb +98 -0
  20. data/lib/quickbooks/extlib/days_and_times.rb +10 -0
  21. data/lib/quickbooks/extlib/days_and_times/duration.rb +362 -0
  22. data/lib/quickbooks/extlib/days_and_times/numeric.rb +48 -0
  23. data/lib/quickbooks/extlib/days_and_times/object.rb +19 -0
  24. data/lib/quickbooks/extlib/days_and_times/time.rb +137 -0
  25. data/lib/quickbooks/extlib/hash.rb +327 -0
  26. data/lib/quickbooks/extlib/hook.rb +366 -0
  27. data/lib/quickbooks/extlib/inflection.rb +436 -0
  28. data/lib/quickbooks/extlib/logger.rb +202 -0
  29. data/lib/quickbooks/extlib/object.rb +162 -0
  30. data/lib/quickbooks/extlib/pathname.rb +15 -0
  31. data/lib/quickbooks/extlib/rubygems.rb +38 -0
  32. data/lib/quickbooks/extlib/string.rb +32 -0
  33. data/lib/quickbooks/extlib/time.rb +41 -0
  34. data/lib/quickbooks/model.rb +503 -121
  35. data/lib/quickbooks/option.rb +95 -0
  36. data/lib/quickbooks/property.rb +69 -0
  37. data/lib/quickbooks/ruby_ext.rb +251 -0
  38. data/lib/quickbooks/type.rb +75 -0
  39. data/lib/quickbooks/types.rb +58 -0
  40. data/lib/quickbooks/version.rb +10 -0
  41. data/lib/quickbooks/xsd.rb +243 -0
  42. data/lib/quickbooks/xsd/attribute.rb +57 -0
  43. data/lib/quickbooks/xsd/choice.rb +94 -0
  44. data/lib/quickbooks/xsd/complex_type.rb +77 -0
  45. data/lib/quickbooks/xsd/element.rb +214 -0
  46. data/lib/quickbooks/xsd/group.rb +94 -0
  47. data/lib/quickbooks/xsd/restriction.rb +51 -0
  48. data/lib/quickbooks/xsd/sequence.rb +85 -0
  49. data/lib/quickbooks/xsd/simple_type.rb +87 -0
  50. data/lib/quickbooks/xsd/union.rb +70 -0
  51. data/lib/quickbooks/xsd/validation.rb +89 -0
  52. data/lib/quickbooks/xsd/xml_parse.rb +80 -0
  53. data/quickbooks.gemspec +36 -0
  54. data/source/qbxml21_demo.xsd +2223 -0
  55. data/source/qbxmlops_demo.xsd +483 -0
  56. data/source/qbxmltypes.xsd +160 -0
  57. data/spec/association_spec.rb +5 -0
  58. data/spec/element_collection_spec.rb +8 -0
  59. data/spec/find_spec.rb +30 -0
  60. data/spec/lib/quickbooks/element_spec.rb +83 -0
  61. data/spec/lib/quickbooks/elements/sales_order_add_spec.rb +14 -0
  62. data/spec/lib/quickbooks/elements/vendor_add_spec.rb +78 -0
  63. data/spec/lib/quickbooks/model_spec.rb +89 -0
  64. data/spec/lib/quickbooks/models/customer_spec.rb +52 -0
  65. data/spec/lib/quickbooks/models/invoice_spec.rb +12 -0
  66. data/spec/lib/quickbooks/models/sales_order_line_spec.rb +0 -0
  67. data/spec/lib/quickbooks/models/sales_order_spec.rb +145 -0
  68. data/spec/lib/quickbooks/xsd/choice_spec.rb +18 -0
  69. data/spec/pending_spec.rb +11 -0
  70. data/spec/quickbooks_spec.rb +38 -0
  71. data/spec/repeatable_spec.rb +20 -0
  72. data/spec/requires_spec.rb +10 -0
  73. data/spec/spec_helper.rb +5 -0
  74. data/spec/validation_spec.rb +24 -0
  75. metadata +133 -285
  76. metadata.gz.sig +0 -0
  77. data/LICENSE +0 -16
  78. data/README +0 -49
  79. data/doc/classes/Array.html +0 -368
  80. data/doc/classes/Array.src/M000026.html +0 -18
  81. data/doc/classes/Array.src/M000027.html +0 -21
  82. data/doc/classes/Array.src/M000028.html +0 -18
  83. data/doc/classes/Array.src/M000029.html +0 -19
  84. data/doc/classes/Array.src/M000030.html +0 -18
  85. data/doc/classes/Array.src/M000031.html +0 -23
  86. data/doc/classes/Array.src/M000032.html +0 -18
  87. data/doc/classes/Array.src/M000033.html +0 -18
  88. data/doc/classes/Array.src/M000034.html +0 -18
  89. data/doc/classes/Array.src/M000035.html +0 -19
  90. data/doc/classes/Array.src/M000036.html +0 -18
  91. data/doc/classes/Array.src/M000037.html +0 -23
  92. data/doc/classes/Array.src/M000038.html +0 -18
  93. data/doc/classes/Array.src/M000039.html +0 -21
  94. data/doc/classes/Array.src/M000040.html +0 -18
  95. data/doc/classes/Array.src/M000041.html +0 -18
  96. data/doc/classes/Class.html +0 -171
  97. data/doc/classes/Class.src/M000042.html +0 -18
  98. data/doc/classes/Class.src/M000043.html +0 -18
  99. data/doc/classes/Hash.html +0 -516
  100. data/doc/classes/Hash.src/M000001.html +0 -18
  101. data/doc/classes/Hash.src/M000002.html +0 -21
  102. data/doc/classes/Hash.src/M000003.html +0 -18
  103. data/doc/classes/Hash.src/M000004.html +0 -21
  104. data/doc/classes/Hash.src/M000005.html +0 -18
  105. data/doc/classes/Hash.src/M000006.html +0 -20
  106. data/doc/classes/Hash.src/M000007.html +0 -18
  107. data/doc/classes/Hash.src/M000008.html +0 -22
  108. data/doc/classes/Hash.src/M000009.html +0 -18
  109. data/doc/classes/Hash.src/M000010.html +0 -21
  110. data/doc/classes/Hash.src/M000011.html +0 -24
  111. data/doc/classes/Hash.src/M000012.html +0 -18
  112. data/doc/classes/Hash.src/M000013.html +0 -21
  113. data/doc/classes/Hash.src/M000014.html +0 -18
  114. data/doc/classes/Hash.src/M000015.html +0 -21
  115. data/doc/classes/Hash.src/M000016.html +0 -18
  116. data/doc/classes/Hash.src/M000017.html +0 -21
  117. data/doc/classes/Hash.src/M000018.html +0 -27
  118. data/doc/classes/Hash.src/M000019.html +0 -20
  119. data/doc/classes/Hash.src/M000020.html +0 -18
  120. data/doc/classes/Hash.src/M000021.html +0 -18
  121. data/doc/classes/Hash.src/M000022.html +0 -18
  122. data/doc/classes/Hash.src/M000023.html +0 -18
  123. data/doc/classes/Hash.src/M000024.html +0 -18
  124. data/doc/classes/Hash.src/M000025.html +0 -28
  125. data/doc/classes/Object.html +0 -278
  126. data/doc/classes/Object.src/M000044.html +0 -24
  127. data/doc/classes/Object.src/M000045.html +0 -19
  128. data/doc/classes/Object.src/M000046.html +0 -24
  129. data/doc/classes/Object.src/M000047.html +0 -18
  130. data/doc/classes/Object.src/M000048.html +0 -19
  131. data/doc/classes/Object.src/M000049.html +0 -18
  132. data/doc/classes/Object.src/M000050.html +0 -18
  133. data/doc/classes/Object.src/M000051.html +0 -18
  134. data/doc/classes/Qbxml.html +0 -131
  135. data/doc/classes/Qbxml/Request.html +0 -224
  136. data/doc/classes/Qbxml/Request.src/M000132.html +0 -66
  137. data/doc/classes/Qbxml/Request.src/M000133.html +0 -19
  138. data/doc/classes/Qbxml/Request.src/M000134.html +0 -83
  139. data/doc/classes/Qbxml/RequestSet.html +0 -199
  140. data/doc/classes/Qbxml/RequestSet.src/M000135.html +0 -22
  141. data/doc/classes/Qbxml/RequestSet.src/M000136.html +0 -26
  142. data/doc/classes/Qbxml/RequestSet.src/M000137.html +0 -18
  143. data/doc/classes/Qbxml/RequestSet.src/M000138.html +0 -22
  144. data/doc/classes/Qbxml/Response.html +0 -302
  145. data/doc/classes/Qbxml/Response.src/M000123.html +0 -24
  146. data/doc/classes/Qbxml/Response.src/M000124.html +0 -19
  147. data/doc/classes/Qbxml/Response.src/M000125.html +0 -55
  148. data/doc/classes/Qbxml/Response.src/M000126.html +0 -18
  149. data/doc/classes/Qbxml/Response.src/M000127.html +0 -18
  150. data/doc/classes/Qbxml/Response.src/M000128.html +0 -18
  151. data/doc/classes/Qbxml/Response.src/M000129.html +0 -18
  152. data/doc/classes/Qbxml/Response.src/M000130.html +0 -18
  153. data/doc/classes/Qbxml/Response.src/M000131.html +0 -63
  154. data/doc/classes/Qbxml/ResponseSet.html +0 -244
  155. data/doc/classes/Qbxml/ResponseSet.src/M000116.html +0 -22
  156. data/doc/classes/Qbxml/ResponseSet.src/M000117.html +0 -26
  157. data/doc/classes/Qbxml/ResponseSet.src/M000118.html +0 -24
  158. data/doc/classes/Qbxml/ResponseSet.src/M000119.html +0 -20
  159. data/doc/classes/Qbxml/ResponseSet.src/M000120.html +0 -27
  160. data/doc/classes/Qbxml/ResponseSet.src/M000121.html +0 -18
  161. data/doc/classes/Qbxml/ResponseSet.src/M000122.html +0 -18
  162. data/doc/classes/Quickbooks.html +0 -196
  163. data/doc/classes/Quickbooks/Address.html +0 -139
  164. data/doc/classes/Quickbooks/Address.src/M000115.html +0 -19
  165. data/doc/classes/Quickbooks/Base.html +0 -567
  166. data/doc/classes/Quickbooks/Base.src/M000095.html +0 -18
  167. data/doc/classes/Quickbooks/Base.src/M000096.html +0 -20
  168. data/doc/classes/Quickbooks/Base.src/M000097.html +0 -19
  169. data/doc/classes/Quickbooks/Base.src/M000098.html +0 -18
  170. data/doc/classes/Quickbooks/Base.src/M000099.html +0 -19
  171. data/doc/classes/Quickbooks/Base.src/M000100.html +0 -30
  172. data/doc/classes/Quickbooks/Base.src/M000101.html +0 -22
  173. data/doc/classes/Quickbooks/Base.src/M000102.html +0 -30
  174. data/doc/classes/Quickbooks/Base.src/M000103.html +0 -19
  175. data/doc/classes/Quickbooks/Base.src/M000104.html +0 -19
  176. data/doc/classes/Quickbooks/Base.src/M000105.html +0 -18
  177. data/doc/classes/Quickbooks/Base.src/M000106.html +0 -18
  178. data/doc/classes/Quickbooks/Base.src/M000107.html +0 -19
  179. data/doc/classes/Quickbooks/Base.src/M000108.html +0 -18
  180. data/doc/classes/Quickbooks/Base.src/M000109.html +0 -45
  181. data/doc/classes/Quickbooks/Base.src/M000110.html +0 -18
  182. data/doc/classes/Quickbooks/Base.src/M000111.html +0 -18
  183. data/doc/classes/Quickbooks/Base.src/M000112.html +0 -20
  184. data/doc/classes/Quickbooks/BillAddress.html +0 -113
  185. data/doc/classes/Quickbooks/CreditCardInfo.html +0 -113
  186. data/doc/classes/Quickbooks/Customer.html +0 -113
  187. data/doc/classes/Quickbooks/CustomerTypeRef.html +0 -113
  188. data/doc/classes/Quickbooks/Deleted.html +0 -113
  189. data/doc/classes/Quickbooks/ItemSalesTaxRef.html +0 -113
  190. data/doc/classes/Quickbooks/JobTypeRef.html +0 -113
  191. data/doc/classes/Quickbooks/ListDeleted.html +0 -139
  192. data/doc/classes/Quickbooks/ListDeleted.src/M000114.html +0 -18
  193. data/doc/classes/Quickbooks/ListItem.html +0 -170
  194. data/doc/classes/Quickbooks/ListItem.src/M000093.html +0 -20
  195. data/doc/classes/Quickbooks/ListItem.src/M000094.html +0 -18
  196. data/doc/classes/Quickbooks/Model.html +0 -462
  197. data/doc/classes/Quickbooks/Model.src/M000073.html +0 -24
  198. data/doc/classes/Quickbooks/Model.src/M000074.html +0 -19
  199. data/doc/classes/Quickbooks/Model.src/M000075.html +0 -18
  200. data/doc/classes/Quickbooks/Model.src/M000076.html +0 -18
  201. data/doc/classes/Quickbooks/Model.src/M000077.html +0 -18
  202. data/doc/classes/Quickbooks/Model.src/M000078.html +0 -22
  203. data/doc/classes/Quickbooks/Model.src/M000079.html +0 -32
  204. data/doc/classes/Quickbooks/Model.src/M000080.html +0 -32
  205. data/doc/classes/Quickbooks/Model.src/M000081.html +0 -18
  206. data/doc/classes/Quickbooks/Model.src/M000082.html +0 -18
  207. data/doc/classes/Quickbooks/Model.src/M000083.html +0 -22
  208. data/doc/classes/Quickbooks/Model.src/M000084.html +0 -23
  209. data/doc/classes/Quickbooks/Model.src/M000085.html +0 -18
  210. data/doc/classes/Quickbooks/Model.src/M000086.html +0 -21
  211. data/doc/classes/Quickbooks/Model.src/M000087.html +0 -26
  212. data/doc/classes/Quickbooks/Model.src/M000088.html +0 -26
  213. data/doc/classes/Quickbooks/Model.src/M000089.html +0 -26
  214. data/doc/classes/Quickbooks/Model.src/M000090.html +0 -21
  215. data/doc/classes/Quickbooks/Model.src/M000091.html +0 -23
  216. data/doc/classes/Quickbooks/OleAdapter.html +0 -129
  217. data/doc/classes/Quickbooks/OleAdapter/Connection.html +0 -343
  218. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000058.html +0 -18
  219. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000059.html +0 -19
  220. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000060.html +0 -24
  221. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000061.html +0 -18
  222. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000062.html +0 -18
  223. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000063.html +0 -21
  224. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000064.html +0 -24
  225. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000065.html +0 -20
  226. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000066.html +0 -24
  227. data/doc/classes/Quickbooks/OleAdapter/Connection.src/M000067.html +0 -24
  228. data/doc/classes/Quickbooks/OleAdapter/Ole.html +0 -209
  229. data/doc/classes/Quickbooks/OleAdapter/Ole.src/M000068.html +0 -21
  230. data/doc/classes/Quickbooks/OleAdapter/Ole.src/M000069.html +0 -22
  231. data/doc/classes/Quickbooks/OleAdapter/Ole.src/M000070.html +0 -18
  232. data/doc/classes/Quickbooks/ParentRef.html +0 -113
  233. data/doc/classes/Quickbooks/PreferredPaymentMethodRef.html +0 -113
  234. data/doc/classes/Quickbooks/PriceLevelRef.html +0 -113
  235. data/doc/classes/Quickbooks/QbxmlDebugAdapter.html +0 -111
  236. data/doc/classes/Quickbooks/QbxmlDebugAdapter/Connection.html +0 -139
  237. data/doc/classes/Quickbooks/QbxmlDebugAdapter/Connection.src/M000057.html +0 -18
  238. data/doc/classes/Quickbooks/Ref.html +0 -146
  239. data/doc/classes/Quickbooks/Ref.src/M000113.html +0 -18
  240. data/doc/classes/Quickbooks/SalesRepRef.html +0 -113
  241. data/doc/classes/Quickbooks/SalesTaxCodeRef.html +0 -113
  242. data/doc/classes/Quickbooks/ShipAddress.html +0 -113
  243. data/doc/classes/Quickbooks/TermsRef.html +0 -113
  244. data/doc/classes/Quickbooks/Transaction.html +0 -154
  245. data/doc/classes/Quickbooks/Transaction.src/M000071.html +0 -19
  246. data/doc/classes/Quickbooks/Transaction.src/M000072.html +0 -18
  247. data/doc/classes/Quickbooks/TxnDeleted.html +0 -139
  248. data/doc/classes/Quickbooks/TxnDeleted.src/M000092.html +0 -18
  249. data/doc/classes/String.html +0 -203
  250. data/doc/classes/String.src/M000052.html +0 -18
  251. data/doc/classes/String.src/M000053.html +0 -18
  252. data/doc/classes/String.src/M000054.html +0 -18
  253. data/doc/classes/String.src/M000055.html +0 -18
  254. data/doc/classes/String.src/M000056.html +0 -18
  255. data/doc/created.rid +0 -1
  256. data/doc/files/LICENSE.html +0 -129
  257. data/doc/files/README.html +0 -225
  258. data/doc/files/lib/qbxml/request_rb.html +0 -110
  259. data/doc/files/lib/qbxml/response_rb.html +0 -109
  260. data/doc/files/lib/qbxml/support_rb.html +0 -101
  261. data/doc/files/lib/qbxml_rb.html +0 -122
  262. data/doc/files/lib/quickbooks/adapters/ole_adapter_rb.html +0 -108
  263. data/doc/files/lib/quickbooks/adapters/qbxml_debug_adapter_rb.html +0 -108
  264. data/doc/files/lib/quickbooks/base_rb.html +0 -327
  265. data/doc/files/lib/quickbooks/model_rb.html +0 -108
  266. data/doc/files/lib/quickbooks/models/common/address_rb.html +0 -101
  267. data/doc/files/lib/quickbooks/models/common/all_refs_rb.html +0 -101
  268. data/doc/files/lib/quickbooks/models/customer/credit_card_info_rb.html +0 -101
  269. data/doc/files/lib/quickbooks/models/customer_rb.html +0 -110
  270. data/doc/files/lib/quickbooks/models/deleted_rb.html +0 -101
  271. data/doc/files/lib/quickbooks/models/list_item_rb.html +0 -101
  272. data/doc/files/lib/quickbooks/models/transaction_rb.html +0 -101
  273. data/doc/files/lib/quickbooks/ruby_magic_rb.html +0 -120
  274. data/doc/files/lib/quickbooks_rb.html +0 -125
  275. data/doc/fr_class_index.html +0 -64
  276. data/doc/fr_file_index.html +0 -45
  277. data/doc/fr_method_index.html +0 -164
  278. data/doc/index.html +0 -24
  279. data/doc/rdoc-style.css +0 -208
  280. data/lib/qbxml.rb +0 -3
  281. data/lib/qbxml/request.rb +0 -207
  282. data/lib/qbxml/response.rb +0 -197
  283. data/lib/qbxml/support.rb +0 -130
  284. data/lib/quickbooks/adapters/qbxml_debug_adapter.rb +0 -10
  285. data/lib/quickbooks/base.rb +0 -277
  286. data/lib/quickbooks/models/common/address.rb +0 -14
  287. data/lib/quickbooks/models/common/all_refs.rb +0 -37
  288. data/lib/quickbooks/models/customer.rb +0 -70
  289. data/lib/quickbooks/models/customer/credit_card_info.rb +0 -5
  290. data/lib/quickbooks/models/deleted.rb +0 -22
  291. data/lib/quickbooks/models/list_item.rb +0 -41
  292. data/lib/quickbooks/models/transaction.rb +0 -49
  293. 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 OleAdapter
12
- class Ole
13
- # Simply holds the actual OLE object.
14
- attr_reader :ole
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
- # Finds an OLE variable in the OLE type library, if you specified one in new().
25
- # This navigates the OLE classes and constants for you and returns the variable that matches the _var_name_ you specify.
26
- # This is used with Quickbooks to get the qbFileOpenDoNotCare parameter to pass to the OpenConnection2 method:
27
- # @quickbooks = Ole.new('QBXMLRP2.RequestProcessor', 'QBXMLRP2 1.0 Type Library')
28
- # @quickbooks.OpenConnection2('','Sample Application',@quickbooks.get_variable('localQBD'))
29
- # @session = @quickbooks.BeginSession('',@quickbooks.get_variable('qbFileOpenDoNotCare'))
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
- def classes #:nodoc:
40
- return nil unless @type_library
41
- @classes ||= WIN32OLE_TYPE.ole_classes(@type_library)
42
- end
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
- def constant_for(class_name) #:nodoc:
45
- return nil unless @type_library
46
- @constant_for ||= {}
47
- @constant_for[class_name] ||= WIN32OLE_TYPE.new(@type_library, class_name)
48
- end
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
- # method_missing sends all other methods to the OLE object, so you can treat this object as your OLE object.
51
- def method_missing(method_name, *args)
52
- @ole.send(method_name, *args)
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
- class Connection
57
- class << self #:nodoc: all
58
- def connections
59
- @connections || (@connections = [])
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
- def connections=(v)
62
- # 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']
63
- @connections = v
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
- at_exit do
67
- Connection.connections.each do |conn|
68
- conn.close
69
- end
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
- # Initializes an instance of Quickbooks::Connection.
74
- # - _application_name_ is required.
75
- # - _file_ is optional, in which case the connection will be made to the currently open company file in the Quickbooks application.
76
- # - _user_ and _password_ may be required if you have specified a specific file to open.
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
- # Returns true if there is an open connection to Quickbooks, false if not. Use session? to determine an open session.
90
- def connected?
91
- @connected ||= false
92
- end
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
- # Returns true if there is an active session. Use connected? to determine whether you are connected or not.
95
- def session?
96
- !@session.nil?
97
- end
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
- # Sends a request to Quickbooks. This request should be a valid QBXML request. Use Qbxml::Request to generate valid requests.
100
- def send_xml(xml)
101
- connection.ProcessRequest(session, xml)
102
- rescue => e
103
- warn "ERROR processing request:\n#{xml}"
104
- raise # Reraises the original error, only this way we got the xml output
105
- end
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
- # Returns the active connection to Quickbooks, or creates a new one if none exists.
108
- def connection
109
- @connection || begin
110
- @connection = @quickbooks.ole
111
- @connection.OpenConnection2('',@application_name,@connection_type)
112
- Connection.connections << self
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
- # Begin a session to Quickbooks.
119
- def session
120
- @session || begin
121
- @session = connection.BeginSession(@file,@connection_mode)
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
- # End the current Quickbooks session. After ending a session, a new session may be reopened if desired.
126
- def end_session
127
- if !@session.nil?
128
- connection.EndSession(@session)
129
- @session = nil
130
- true
131
- else
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
- # Close the connection to Quickbooks. Automatically ends the session, if there is one.
137
- def close
138
- end_session
139
- if connected? && connection.CloseConnection.nil?
140
- @connected = false
141
- @connection = nil
142
- Connection.connections = Connection.connections - [self]
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