quickbooks 0.0.3 → 0.0.5

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 (186) hide show
  1. data/README +9 -5
  2. data/Rakefile +2 -2
  3. data/doc/classes/Array.html +244 -0
  4. data/doc/classes/Array.src/M000001.html +18 -0
  5. data/doc/classes/Array.src/M000002.html +19 -0
  6. data/doc/classes/Array.src/M000003.html +18 -0
  7. data/doc/classes/Array.src/M000004.html +23 -0
  8. data/doc/classes/Array.src/M000005.html +18 -0
  9. data/doc/classes/Array.src/M000006.html +21 -0
  10. data/doc/classes/Array.src/M000007.html +18 -0
  11. data/doc/classes/Array.src/M000008.html +18 -0
  12. data/doc/classes/Class.html +5 -5
  13. data/doc/classes/Class.src/{M000003.html → M000027.html} +1 -1
  14. data/doc/classes/Hash.html +264 -14
  15. data/doc/classes/Hash.src/M000009.html +18 -0
  16. data/doc/classes/Hash.src/M000010.html +21 -0
  17. data/doc/classes/Hash.src/M000011.html +18 -0
  18. data/doc/classes/Hash.src/M000012.html +21 -0
  19. data/doc/classes/Hash.src/M000013.html +18 -0
  20. data/doc/classes/Hash.src/{M000001.html → M000014.html} +6 -5
  21. data/doc/classes/Hash.src/{M000002.html → M000015.html} +6 -2
  22. data/doc/classes/Hash.src/M000016.html +20 -0
  23. data/doc/classes/Hash.src/M000017.html +18 -0
  24. data/doc/classes/Hash.src/M000018.html +20 -0
  25. data/doc/classes/Hash.src/M000019.html +38 -0
  26. data/doc/classes/{Quickbooks/Qbxml/Error.src/M000031.html → Hash.src/M000020.html} +5 -5
  27. data/doc/classes/Hash.src/M000021.html +18 -0
  28. data/doc/classes/Hash.src/M000022.html +20 -0
  29. data/doc/classes/Hash.src/M000023.html +18 -0
  30. data/doc/classes/Hash.src/M000024.html +18 -0
  31. data/doc/classes/Hash.src/M000025.html +18 -0
  32. data/doc/classes/Hash.src/M000026.html +22 -0
  33. data/doc/classes/Object.html +56 -22
  34. data/doc/classes/Object.src/{M000004.html → M000028.html} +0 -0
  35. data/doc/classes/Object.src/{M000005.html → M000029.html} +0 -0
  36. data/doc/classes/Object.src/{M000006.html → M000030.html} +1 -1
  37. data/doc/classes/Object.src/{M000007.html → M000031.html} +0 -0
  38. data/doc/classes/Object.src/M000032.html +18 -0
  39. data/doc/classes/Object.src/M000033.html +18 -0
  40. data/doc/classes/QBHelpers/Ole.html +17 -17
  41. data/doc/classes/QBHelpers/Ole.src/{M000066.html → M000108.html} +0 -0
  42. data/doc/classes/QBHelpers/Ole.src/{M000067.html → M000109.html} +0 -0
  43. data/doc/classes/QBHelpers/Ole.src/{M000068.html → M000110.html} +0 -0
  44. data/doc/classes/Quickbooks.html +44 -4
  45. data/doc/classes/Quickbooks/{Qbxml/Error.html → Address.html} +14 -43
  46. data/doc/classes/Quickbooks/Address.src/M000107.html +19 -0
  47. data/doc/classes/Quickbooks/Base.html +118 -243
  48. data/doc/classes/Quickbooks/Base.src/{M000034.html → M000080.html} +1 -1
  49. data/doc/classes/Quickbooks/Base.src/{M000035.html → M000081.html} +1 -1
  50. data/doc/classes/Quickbooks/Base.src/{M000036.html → M000082.html} +1 -1
  51. data/doc/classes/Quickbooks/Base.src/{M000037.html → M000083.html} +1 -1
  52. data/doc/classes/Quickbooks/Base.src/M000084.html +23 -0
  53. data/doc/classes/Quickbooks/Base.src/{M000041.html → M000085.html} +1 -1
  54. data/doc/classes/Quickbooks/Base.src/{M000042.html → M000086.html} +2 -2
  55. data/doc/classes/Quickbooks/Base.src/{M000043.html → M000087.html} +3 -2
  56. data/doc/classes/Quickbooks/Base.src/M000088.html +19 -0
  57. data/doc/classes/Quickbooks/Base.src/{M000045.html → M000089.html} +1 -1
  58. data/doc/classes/Quickbooks/Base.src/M000090.html +19 -0
  59. data/doc/classes/Quickbooks/Base.src/{M000052.html → M000091.html} +1 -1
  60. data/doc/classes/Quickbooks/Base.src/M000092.html +45 -0
  61. data/doc/classes/Quickbooks/Base.src/{M000054.html → M000093.html} +2 -2
  62. data/doc/classes/Quickbooks/Base.src/M000094.html +18 -0
  63. data/doc/classes/Quickbooks/Base.src/M000095.html +20 -0
  64. data/doc/classes/Quickbooks/BillAddress.html +113 -0
  65. data/doc/classes/Quickbooks/Connection.html +68 -68
  66. data/doc/classes/Quickbooks/Connection.src/{M000056.html → M000097.html} +0 -0
  67. data/doc/classes/Quickbooks/Connection.src/{M000057.html → M000098.html} +0 -0
  68. data/doc/classes/Quickbooks/Connection.src/{M000058.html → M000099.html} +0 -1
  69. data/doc/classes/Quickbooks/Connection.src/{M000059.html → M000100.html} +1 -1
  70. data/doc/classes/Quickbooks/Connection.src/{M000060.html → M000101.html} +1 -1
  71. data/doc/classes/Quickbooks/Connection.src/{M000061.html → M000102.html} +2 -2
  72. data/doc/classes/Quickbooks/Connection.src/{M000062.html → M000103.html} +1 -1
  73. data/doc/classes/Quickbooks/Connection.src/{M000063.html → M000104.html} +1 -1
  74. data/doc/classes/Quickbooks/Connection.src/{M000064.html → M000105.html} +1 -1
  75. data/doc/classes/Quickbooks/Connection.src/{M000065.html → M000106.html} +2 -2
  76. data/doc/classes/Quickbooks/CreditCardInfo.html +113 -0
  77. data/doc/classes/Quickbooks/CustomerTypeRef.html +113 -0
  78. data/doc/classes/Quickbooks/ItemSalesTaxRef.html +113 -0
  79. data/doc/classes/Quickbooks/JobTypeRef.html +113 -0
  80. data/doc/classes/Quickbooks/ListItem.html +21 -44
  81. data/doc/classes/Quickbooks/ListItem.src/M000078.html +19 -0
  82. data/doc/classes/Quickbooks/ListItem.src/M000079.html +18 -0
  83. data/doc/classes/Quickbooks/Model.html +469 -0
  84. data/doc/classes/Quickbooks/Model.src/M000059.html +24 -0
  85. data/doc/classes/Quickbooks/Model.src/M000060.html +18 -0
  86. data/doc/classes/Quickbooks/Model.src/M000061.html +18 -0
  87. data/doc/classes/Quickbooks/Model.src/M000062.html +18 -0
  88. data/doc/classes/Quickbooks/Model.src/M000063.html +18 -0
  89. data/doc/classes/Quickbooks/Model.src/M000064.html +18 -0
  90. data/doc/classes/Quickbooks/Model.src/M000065.html +32 -0
  91. data/doc/classes/Quickbooks/Model.src/M000066.html +32 -0
  92. data/doc/classes/Quickbooks/Model.src/M000067.html +18 -0
  93. data/doc/classes/Quickbooks/{Base.src/M000046.html → Model.src/M000068.html} +2 -3
  94. data/doc/classes/Quickbooks/{Base.src/M000047.html → Model.src/M000069.html} +4 -4
  95. data/doc/classes/Quickbooks/{Base.src/M000048.html → Model.src/M000070.html} +2 -2
  96. data/doc/classes/Quickbooks/{Base.src/M000049.html → Model.src/M000071.html} +2 -2
  97. data/doc/classes/Quickbooks/{Base.src/M000050.html → Model.src/M000072.html} +3 -3
  98. data/doc/classes/Quickbooks/{Base.src/M000051.html → Model.src/M000073.html} +6 -5
  99. data/doc/classes/Quickbooks/Model.src/M000074.html +28 -0
  100. data/doc/classes/Quickbooks/Model.src/M000075.html +28 -0
  101. data/doc/classes/Quickbooks/Model.src/M000076.html +21 -0
  102. data/doc/classes/Quickbooks/Model.src/M000077.html +23 -0
  103. data/doc/classes/Quickbooks/ParentRef.html +113 -0
  104. data/doc/classes/Quickbooks/PreferredPaymentMethodRef.html +113 -0
  105. data/doc/classes/Quickbooks/PriceLevelRef.html +113 -0
  106. data/doc/classes/Quickbooks/Qbxml.html +0 -5
  107. data/doc/classes/Quickbooks/Qbxml/Request.html +55 -23
  108. data/doc/classes/Quickbooks/Qbxml/Request.src/M000050.html +34 -0
  109. data/doc/classes/Quickbooks/Qbxml/Request.src/{M000025.html → M000051.html} +2 -2
  110. data/doc/classes/Quickbooks/Qbxml/Request.src/M000052.html +81 -0
  111. data/doc/classes/Quickbooks/Qbxml/RequestSet.html +20 -20
  112. data/doc/classes/Quickbooks/Qbxml/RequestSet.src/{M000027.html → M000053.html} +0 -0
  113. data/doc/classes/Quickbooks/Qbxml/RequestSet.src/{M000028.html → M000054.html} +0 -0
  114. data/doc/classes/Quickbooks/Qbxml/RequestSet.src/{M000029.html → M000055.html} +0 -0
  115. data/doc/classes/Quickbooks/Qbxml/RequestSet.src/{M000030.html → M000056.html} +0 -0
  116. data/doc/classes/Quickbooks/Qbxml/Response.html +46 -46
  117. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000015.html → M000041.html} +1 -1
  118. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000016.html → M000042.html} +1 -1
  119. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000017.html → M000043.html} +1 -2
  120. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000018.html → M000044.html} +1 -1
  121. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000019.html → M000045.html} +1 -1
  122. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000020.html → M000046.html} +1 -1
  123. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000021.html → M000047.html} +1 -1
  124. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000022.html → M000048.html} +1 -1
  125. data/doc/classes/Quickbooks/Qbxml/Response.src/{M000023.html → M000049.html} +22 -11
  126. data/doc/classes/Quickbooks/Qbxml/ResponseSet.html +35 -35
  127. data/doc/classes/Quickbooks/Qbxml/ResponseSet.src/{M000008.html → M000034.html} +1 -1
  128. data/doc/classes/Quickbooks/Qbxml/ResponseSet.src/{M000009.html → M000035.html} +1 -1
  129. data/doc/classes/Quickbooks/Qbxml/ResponseSet.src/{M000010.html → M000036.html} +1 -1
  130. data/doc/classes/Quickbooks/Qbxml/ResponseSet.src/{M000011.html → M000037.html} +1 -1
  131. data/doc/classes/Quickbooks/Qbxml/ResponseSet.src/{M000012.html → M000038.html} +1 -1
  132. data/doc/classes/Quickbooks/Qbxml/ResponseSet.src/{M000013.html → M000039.html} +1 -1
  133. data/doc/classes/Quickbooks/Qbxml/ResponseSet.src/{M000014.html → M000040.html} +1 -1
  134. data/doc/classes/Quickbooks/Ref.html +139 -0
  135. data/doc/classes/Quickbooks/Ref.src/M000096.html +18 -0
  136. data/doc/classes/Quickbooks/SalesRepRef.html +113 -0
  137. data/doc/classes/Quickbooks/SalesTaxCodeRef.html +113 -0
  138. data/doc/classes/Quickbooks/ShipAddress.html +113 -0
  139. data/doc/classes/Quickbooks/TermsRef.html +113 -0
  140. data/doc/classes/Quickbooks/Transaction.html +21 -62
  141. data/doc/classes/Quickbooks/Transaction.src/M000057.html +19 -0
  142. data/doc/classes/Quickbooks/Transaction.src/M000058.html +18 -0
  143. data/doc/created.rid +1 -1
  144. data/doc/files/README.html +15 -4
  145. data/doc/files/lib/quickbooks/base_rb.html +15 -8
  146. data/doc/files/lib/quickbooks/connection_rb.html +1 -1
  147. data/doc/files/lib/quickbooks/model_rb.html +108 -0
  148. data/doc/files/lib/quickbooks/{qbxml/error_rb.html → models/common/address_rb.html} +5 -5
  149. data/doc/files/lib/quickbooks/models/common/all_refs_rb.html +101 -0
  150. data/doc/files/lib/quickbooks/models/customer/credit_card_info_rb.html +101 -0
  151. data/doc/files/lib/quickbooks/models/customer_rb.html +10 -1
  152. data/doc/files/lib/quickbooks/models/list_item_rb.html +1 -1
  153. data/doc/files/lib/quickbooks/models/transaction_rb.html +1 -1
  154. data/doc/files/lib/quickbooks/ole_helper_rb.html +1 -1
  155. data/doc/files/lib/quickbooks/qbxml/request_rb.html +1 -1
  156. data/doc/files/lib/quickbooks/qbxml/response_rb.html +1 -2
  157. data/doc/files/lib/quickbooks/qbxml_rb.html +109 -0
  158. data/doc/files/lib/quickbooks/ruby_magic_rb.html +1 -1
  159. data/doc/fr_class_index.html +16 -1
  160. data/doc/fr_file_index.html +5 -1
  161. data/doc/fr_method_index.html +110 -68
  162. data/lib/quickbooks/base.rb +54 -95
  163. data/lib/quickbooks/connection.rb +2 -3
  164. data/lib/quickbooks/model.rb +176 -0
  165. data/lib/quickbooks/models/common/address.rb +14 -0
  166. data/lib/quickbooks/models/common/all_refs.rb +36 -0
  167. data/lib/quickbooks/models/customer.rb +30 -31
  168. data/lib/quickbooks/models/customer/credit_card_info.rb +5 -0
  169. data/lib/quickbooks/models/list_item.rb +27 -16
  170. data/lib/quickbooks/models/transaction.rb +21 -12
  171. data/lib/quickbooks/qbxml.rb +2 -0
  172. data/lib/quickbooks/qbxml/request.rb +80 -31
  173. data/lib/quickbooks/qbxml/response.rb +21 -12
  174. data/lib/quickbooks/ruby_magic.rb +130 -4
  175. metadata +146 -74
  176. data/doc/classes/Quickbooks/Base.src/M000038.html +0 -19
  177. data/doc/classes/Quickbooks/Base.src/M000039.html +0 -25
  178. data/doc/classes/Quickbooks/Base.src/M000040.html +0 -27
  179. data/doc/classes/Quickbooks/Base.src/M000044.html +0 -19
  180. data/doc/classes/Quickbooks/Base.src/M000053.html +0 -36
  181. data/doc/classes/Quickbooks/Base.src/M000055.html +0 -18
  182. data/doc/classes/Quickbooks/ListItem.src/M000033.html +0 -31
  183. data/doc/classes/Quickbooks/Qbxml/Request.src/M000024.html +0 -23
  184. data/doc/classes/Quickbooks/Qbxml/Request.src/M000026.html +0 -57
  185. data/doc/classes/Quickbooks/Transaction.src/M000032.html +0 -26
  186. data/lib/quickbooks/qbxml/error.rb +0 -20
@@ -42,7 +42,6 @@ module Quickbooks
42
42
  @quickbooks = Ole.new('QBXMLRP2.RequestProcessor', 'QBXMLRP2 1.0 Type Library')
43
43
  @connection_type = @quickbooks.get_variable(connection_type)
44
44
  @connection_mode = @quickbooks.get_variable('qbFileOpen'+connection_mode)
45
- connection # Just to make it fail now instead of later if it can't connect properly.
46
45
  end
47
46
 
48
47
  # Returns true if there is an open connection to Quickbooks, false if not. Use session? to determine an open session.
@@ -57,7 +56,7 @@ module Quickbooks
57
56
 
58
57
  # Sends a request to Quickbooks. This request should be a valid QBXML request. Use Quickbooks::Qbxml::Request to generate valid requests.
59
58
  def send_xml(xml)
60
- connection.ProcessRequest(session, xml)
59
+ @quickbooks.ProcessRequest(session, xml)
61
60
  rescue => e
62
61
  warn "ERROR processing request:\n#{xml}"
63
62
  raise # Reraises the original error, only this way we got the xml output
@@ -95,7 +94,7 @@ module Quickbooks
95
94
  # Close the connection to Quickbooks. Automatically ends the session, if there is one.
96
95
  def close
97
96
  end_session
98
- if connected? && connection.CloseConnection(@session)
97
+ if connected? && connection.CloseConnection
99
98
  @connected = false
100
99
  @connection = nil
101
100
  Quickbooks::Connection.connections = Quickbooks::Connection.connections - [self]
@@ -0,0 +1,176 @@
1
+ require 'quickbooks/ruby_magic'
2
+ module Quickbooks
3
+ CAMELIZE_EXCEPTIONS = {'list_id' => 'ListID', 'txn_id' => 'TxnID', 'owner_id' => 'OwnerID'}
4
+ # These were all created from the info in qbxmlops70.xml, found in the docs in the QBSDK package.
5
+ class Model
6
+ class << self
7
+
8
+ def inherited(klass)
9
+ def klass.valid_filters
10
+ superclass.valid_filters + (@valid_filters ||= [])
11
+ end
12
+ def klass.filter_aliases
13
+ superclass.filter_aliases.merge(@filter_aliases ||= {})
14
+ end
15
+ klass.instance_variable_set('@object_properties', {})
16
+ end
17
+
18
+ def valid_filters=(v)
19
+ @valid_filters = v.stringify_values!
20
+ end
21
+ def valid_filters
22
+ @valid_filters ||= []
23
+ end
24
+ def filter_aliases=(v)
25
+ @filter_aliases = v.stringify_keys!.stringify_values!
26
+ end
27
+ def filter_aliases
28
+ @filter_aliases ||= {}
29
+ end
30
+ def camelized_valid_filters
31
+ valid_filters.camelize_values(Quickbooks::CAMELIZE_EXCEPTIONS)
32
+ end
33
+
34
+ # Register multiple read/writable properties at once. For example:
35
+ # read_write :first_name, :last_name, :phone, :alt_phone
36
+ # For reference attributes (like parent_ref), use ParentRef - a class constant for that object.
37
+ # _read_write_ will set the property setter and accessor accordingly.
38
+ def read_write(*args)
39
+ if args.blank?
40
+ @read_write || (@read_write = [])
41
+ else
42
+ args.each do |prop|
43
+ if prop.is_a?(Symbol)
44
+ read_write << prop
45
+ attr_accessor prop
46
+ else
47
+ @object_properties[prop.class_leaf_name.underscore.to_sym] = prop
48
+ read_write << prop.class_leaf_name.underscore.to_sym
49
+ class_eval "def #{prop.class_leaf_name.underscore}=(v); @#{prop.class_leaf_name.underscore} = #{prop.name}.new(v); end
50
+ def #{prop.class_leaf_name.underscore}; @#{prop.class_leaf_name.underscore}; end"
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ # Read-only attributes: These are attributes, but not modifiable in Quickbooks
57
+ def read_only(*args)
58
+ if args.blank?
59
+ @read_only || (@read_only = [])
60
+ else
61
+ args.each do |prop|
62
+ if prop.is_a?(Symbol)
63
+ read_only << prop
64
+ attr_accessor prop
65
+ else
66
+ @object_properties[prop.class_leaf_name.underscore.to_sym] = prop
67
+ read_only << prop.class_leaf_name.underscore.to_sym
68
+ class_eval "def #{prop.class_leaf_name.underscore}=(v); @#{prop.class_leaf_name.underscore} = #{prop.name}.new(v); end
69
+ def #{prop.class_leaf_name.underscore}; @#{prop.class_leaf_name.underscore}; end"
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def properties
76
+ read_only + read_write
77
+ end
78
+ end
79
+
80
+ self.valid_filters = [:max_returned]
81
+
82
+ # The default for all subclasses is simply to apply the attributes given, and mark the object as a new_record?
83
+ def initialize(args={})
84
+ self.attributes = args
85
+ end
86
+
87
+ # Returns a hash that represents all this object's attributes.
88
+ def attributes(include_read_only=false)
89
+ attrs = {}
90
+ (include_read_only ? self.class.read_only + self.class.read_write : self.class.read_write).each do |column|
91
+ attrs[column.to_s] = instance_variable_get('@' + column.to_s)
92
+ end
93
+ attrs
94
+ end
95
+ # Updates all attributes included in _attrs_ to the values given. The object will now be dirty?.
96
+ def attributes=(attrs)
97
+ raise ArgumentError, "attributes can only be set to a hash of attributes" unless attrs.is_a?(Hash)
98
+ attrs.each do |key,value|
99
+ if self.respond_to?(key.to_s.underscore+'=')
100
+ self.send(key.to_s.underscore+'=', value)
101
+ end
102
+ end
103
+ end
104
+
105
+ # Keeps track of the original values the object had when it was instantiated from a quickbooks response. dirty? and dirty_attributes compare the current values with these ones.
106
+ def original_values
107
+ @original_values || (@original_values = {})
108
+ end
109
+
110
+ # Returns true if any attributes have changed since the object was last loaded or updated from Quickbooks.
111
+ def dirty?
112
+ # Concept: For each column that the current model includes, has the value been changed?
113
+ self.class.read_write.any? do |column|
114
+ self.instance_variable_get('@' + column.to_s) != original_values[column.to_s]
115
+ end
116
+ end
117
+
118
+ # Returns a hash of the attributes and their (new) values that have been changed since the object was last loaded or updated from Quickbooks.
119
+ # If you send in some attributes, it will compare to those given instead of original_attributes.
120
+ def dirty_attributes(compare={})
121
+ compare = original_values if compare.blank?
122
+ pairs = {}
123
+ self.class.read_write.each do |column|
124
+ value = instance_variable_get('@' + column.to_s)
125
+ if value != compare[column.to_s]
126
+ pairs[column.to_s] = value
127
+ end
128
+ end
129
+ pairs
130
+ end
131
+
132
+ def to_dirty_hash
133
+ hsh = {}
134
+ self.dirty_attributes.each do |key,value|
135
+ if value.is_a?(Quickbooks::Model)
136
+ h = value.to_dirty_hash
137
+ hsh[key] = h unless h.blank?
138
+ else
139
+ hsh[key] = value
140
+ end
141
+ end
142
+ hsh.order!(self.class.read_write.stringify_values)
143
+ hsh
144
+ end
145
+
146
+ def to_hash(include_read_only=false)
147
+ hsh = {}
148
+ self.attributes(include_read_only).each do |key,value|
149
+ if value.is_a?(Quickbooks::Model)
150
+ h = value.to_hash(include_read_only)
151
+ hsh[key] = h unless h.blank?
152
+ else
153
+ hsh[key] = value
154
+ end
155
+ end
156
+ hsh.order!((include_read_only ? self.class.read_only + self.class.read_write : self.class.read_write).stringify_values)
157
+ hsh
158
+ end
159
+
160
+ def ==(other)
161
+ return false unless other.is_a?(self.class)
162
+ !self.class.read_write.any? do |column|
163
+ self.instance_variable_get('@' + column.to_s) != other.instance_variable_get('@' + column.to_s)
164
+ end
165
+ end
166
+
167
+ def ===(other)
168
+ # other could be a hash
169
+ if other.is_a?(Hash)
170
+ self == self.class.new(other)
171
+ else
172
+ self == other
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,14 @@
1
+ module Quickbooks
2
+ class Address < Model
3
+ def self.inherited(klass)
4
+ super
5
+ klass.read_write :addr1, :addr2, :addr3, :addr4, :addr5, :city, :state, :postal_code, :country, :note
6
+ end
7
+ end
8
+
9
+ class BillAddress < Address
10
+ end
11
+
12
+ class ShipAddress < Address
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ module Quickbooks
2
+ class Ref < Model
3
+ def self.inherited(klass)
4
+ klass.read_only :list_id, :full_name
5
+ end
6
+
7
+ # Put in here the connector code for 'association' calls
8
+ end
9
+
10
+ class ParentRef < Ref
11
+ end
12
+
13
+ class CustomerTypeRef < Ref
14
+ end
15
+
16
+ class TermsRef < Ref
17
+ end
18
+
19
+ class SalesRepRef < Ref
20
+ end
21
+
22
+ class SalesTaxCodeRef < Ref
23
+ end
24
+
25
+ class ItemSalesTaxRef < Ref
26
+ end
27
+
28
+ class PreferredPaymentMethodRef < Ref
29
+ end
30
+
31
+ class JobTypeRef < Ref
32
+ end
33
+
34
+ class PriceLevelRef < Ref
35
+ end
36
+ end
@@ -1,56 +1,55 @@
1
+ require 'quickbooks/models/common/all_refs'
2
+ require 'quickbooks/models/common/address'
3
+ require 'quickbooks/models/customer/credit_card_info'
4
+
1
5
  module Quickbooks
2
6
  class Customer < ListItem
3
- properties :parent_ref,
4
- :sublevel,
7
+ self.valid_filters = [:active_status, :from_modified_date, :to_modified_date, :name_filter, :name_range_filter, :total_balance_filter]
5
8
 
6
- :name,
9
+ # Properties are in the order required by QBXML
10
+ read_write :name,
11
+ :is_active,
12
+ ParentRef,
13
+ :company_name,
7
14
  :salutation,
8
15
  :first_name,
9
16
  :middle_name,
10
17
  :last_name,
11
18
  :suffix,
12
- :company_name,
13
-
19
+ BillAddress,
20
+ ShipAddress,
21
+ :print_as,
14
22
  :phone,
15
- :mobile,
16
- :alt_phone,
23
+ # :mobile, # only in QBOE
17
24
  :pager,
25
+ :alt_phone,
18
26
  :fax,
19
27
  :email,
20
28
  :contact,
21
29
  :alt_contact,
22
- :bill_address,
23
- :ship_address,
24
-
25
- :notes,
26
-
30
+ CustomerTypeRef,
31
+ TermsRef,
32
+ SalesRepRef,
33
+ # :open_balance,
34
+ # :open_balance_date,
35
+ SalesTaxCodeRef,
36
+ ItemSalesTaxRef,
37
+ :sales_tax_country,
38
+ :resale_nmber,
27
39
  :account_number,
28
- :credit_card_info,
29
40
  :credit_limit,
30
- :balance,
31
- :open_balance,
32
- :open_balance_date,
33
- :total_balance,
34
- :preferred_payment_method_ref,
35
-
41
+ PreferredPaymentMethodRef,
42
+ CreditCardInfo,
36
43
  :job_status,
37
44
  :job_start_date,
38
45
  :job_projected_end_date,
39
46
  :job_end_date,
40
47
  :job_desc,
41
- :job_type_ref,
42
-
43
- :print_as,
44
- :customer_type_ref,
45
- :delivery_method,
46
- :price_level_ref,
47
- :terms_ref,
48
- :sales_rep_ref,
49
- :sales_tax_code_ref,
50
- :item_sales_tax_ref,
51
- :resale_nmber,
48
+ JobTypeRef,
49
+ :notes,
52
50
  :is_statement_with_parent,
53
- :data_ext_ref
51
+ :delivery_method,
52
+ PriceLevelRef
54
53
 
55
54
  # def initialize
56
55
  # @parent_ref = ListItem.new
@@ -0,0 +1,5 @@
1
+ module Quickbooks
2
+ class CreditCardInfo < Model
3
+ read_write :credit_card_number, :expiration_month, :expiration_year, :name_on_card, :credit_card_address, :credit_card_postal_code
4
+ end
5
+ end
@@ -1,22 +1,33 @@
1
1
  class Quickbooks::ListItem < Quickbooks::Base
2
- attr_accessor :list_id, :full_name, :edit_sequence, :time_created, :time_modified, :is_active
2
+
3
+ self.valid_filters = [:list_id, :full_name]
4
+ self.filter_aliases = {:modified_after => :from_modified_date, :modified_before => :to_modified_date}
5
+
6
+ def self.inherited(klass)
7
+ super
8
+ klass.read_only :list_id, :full_name, :edit_sequence, :time_created, :time_modified
9
+ end
3
10
 
4
11
  class << self
5
- def child_types
6
- [ "Account", "Account List", "BillingRate",
7
- "Class", "Currency", "Customer",
8
- "CustomerMessage", "CustomerType",
9
- "DateDrivenTerms", "Employee", "ItemDiscount",
10
- "ItemFixedAsset", "ItemGroup", "ItemInventory",
11
- "ItemInventoryAssembly", "ItemNonInventory",
12
- "ItemOtherCharge", "ItemPayment", "ItemSalesTax",
13
- "ItemSalesTaxGroup", "ItemService", "ItemSubtotal",
14
- "JobType", "OtherName", "PaymentMethod",
15
- "PayrollItemNonWage", "PayrollItemWage", "PriceLevel",
16
- "SalesRep", "SalesTaxCode", "ShipMethod",
17
- "StandardTerms", "TaxCode", "Template", "ToDo",
18
- "Vehicle", "Vendor", "VendorType"
19
- ]
12
+ def ListOrTxn
13
+ 'List'
20
14
  end
15
+
16
+ # def child_types
17
+ # [ "Account", "Account List", "BillingRate",
18
+ # "Class", "Currency", "Customer",
19
+ # "CustomerMessage", "CustomerType",
20
+ # "DateDrivenTerms", "Employee", "ItemDiscount",
21
+ # "ItemFixedAsset", "ItemGroup", "ItemInventory",
22
+ # "ItemInventoryAssembly", "ItemNonInventory",
23
+ # "ItemOtherCharge", "ItemPayment", "ItemSalesTax",
24
+ # "ItemSalesTaxGroup", "ItemService", "ItemSubtotal",
25
+ # "JobType", "OtherName", "PaymentMethod",
26
+ # "PayrollItemNonWage", "PayrollItemWage", "PriceLevel",
27
+ # "SalesRep", "SalesTaxCode", "ShipMethod",
28
+ # "StandardTerms", "TaxCode", "Template", "ToDo",
29
+ # "Vehicle", "Vendor", "VendorType"
30
+ # ]
31
+ # end
21
32
  end
22
33
  end
@@ -1,6 +1,11 @@
1
1
  class Quickbooks::Transaction < Quickbooks::Base
2
- # attr_accessor :id, :ref_number
3
- attr_accessor :list_id, :full_name, :edit_sequence, :time_created, :time_modified, :is_active
2
+
3
+ self.valid_filters = [:txn_id]
4
+
5
+ def self.inherited(klass)
6
+ super
7
+ klass.read_only :txn_id, :full_name, :edit_sequence, :time_created, :time_modified
8
+ end
4
9
 
5
10
  # Currently, the following transaction types can be modified:
6
11
  # • Bill
@@ -25,16 +30,20 @@ class Quickbooks::Transaction < Quickbooks::Base
25
30
  # • TimeTracking
26
31
 
27
32
  class << self
28
- def child_types
29
- [ "Bill", "BillPaymentCheck", "BillPaymentCreditCard",
30
- "BuildAssembly", "Charge", "Check", "CreditCardCharge",
31
- "CreditCardCredit", "CreditCardRefund", "CreditMemo",
32
- "Deposit", "Estimate", "InventoryAdjustment",
33
- "Invoice", "ItemReceipt", "JournalEntry",
34
- "PurchaseOrder", "ReceivePayment", "SalesOrder",
35
- "SalesReceipt", "SalesTaxPaymentCheck", "TimeTracking",
36
- "VehicleMileage", "VendorCredit"
37
- ]
33
+ def ListOrTxn
34
+ 'Txn'
38
35
  end
36
+
37
+ # def child_types
38
+ # [ "Bill", "BillPaymentCheck", "BillPaymentCreditCard",
39
+ # "BuildAssembly", "Charge", "Check", "CreditCardCharge",
40
+ # "CreditCardCredit", "CreditCardRefund", "CreditMemo",
41
+ # "Deposit", "Estimate", "InventoryAdjustment",
42
+ # "Invoice", "ItemReceipt", "JournalEntry",
43
+ # "PurchaseOrder", "ReceivePayment", "SalesOrder",
44
+ # "SalesReceipt", "SalesTaxPaymentCheck", "TimeTracking",
45
+ # "VehicleMileage", "VendorCredit"
46
+ # ]
47
+ # end
39
48
  end
40
49
  end
@@ -0,0 +1,2 @@
1
+ require 'quickbooks/qbxml/request'
2
+ require 'quickbooks/qbxml/response'
@@ -48,57 +48,96 @@ thequickbooks_qbxmlrequestsetxml
48
48
  end
49
49
 
50
50
  class Request
51
- # @type = there are four types of queries...
52
- # 1) List queries (:query)
53
- # 2) Object-specific transaction query (:transaction)
54
- # 3) Generic transaction query (:any_transaction)
55
- # 4) Reports (:report)
56
- # ...and then there are Mod requests...
57
- # # 1) Mod request (:mod)
58
- def initialize(object, type, options={})
51
+ # 1. List queries (:query)
52
+ # Request.new(object, :query) # <= will return the record matching the object supplied (automatically searches by list_id or txn_id)
53
+ # Request.new(Customer, :query, :limit => 1) # <= will return the first customer
54
+ # Request.new(Customer, :query, /some match/) # <= will return all customers matching the regexp
55
+ # 2. Object-specific transaction query (:transaction)
56
+ # Request.new(Transaction, :query) # <= will return the transaction matching the object supplied
57
+ # 3. Mod requests (:mod)
58
+ # Request.new(object, :mod) # <= will update the object
59
+ # 4. Delete requests (:delete)
60
+ # Request.new(object, :delete) # <= will delete the object
61
+ # We want the attributes of the object when we are updating, but no other time.
62
+ def initialize(object, type, options_or_regexp={})
63
+ options = options_or_regexp.is_a?(Regexp) ? {:matches => options_or_regexp} : options_or_regexp
59
64
  @type = type
60
65
  raise ArgumentError, "Quickbooks::Qbxml::Requests can only be of one of the following types: :query, :transaction, :any_transaction, :mod, :add, :delete, or :report" unless @type.is_one_of?(:query, :transaction, :any_transaction, :mod, :report, :add, :delete)
66
+ @klass = object.is_a?(Class) ? object : object.class
61
67
  @object = object
62
- @filters = options if @type.is_one_of?(:query, :transaction, :any_transaction, :report)
63
- @attributes = options.stringify_keys! if @type.is_one_of?(:mod, :add, :delete)
64
- # puts self.inspect
68
+ @options = options
69
+
70
+ # Return only specific properties: Request.new(Customer, :query, :only => [:list_id, :full_name]); Quickbooks::Customer.first(:only => :list_id)
71
+ @ret_elements = @options.delete(:only).to_a.only(@klass.properties).order!(@klass.properties).stringify_values.camelize_values!(Quickbooks::CAMELIZE_EXCEPTIONS) if @options.has_key?(:only)
72
+
73
+ # Includes only valid filters + aliases for valid filters, then transforms aliased filters to real filters, then camelizes keys to prepare for writing to XML, lastly orders the keys to a valid filter order.
74
+ # You may optionally have ListID OR FullName OR ( MaxReturned AND ActiveStatus AND FromModifiedDate AND ToModifiedDate AND ( NameFilter OR NameRangeFilter ) )
75
+ @filters = options.stringify_keys.only(@klass.valid_filters + @klass.filter_aliases.keys).transform_keys!(@klass.filter_aliases).camelize_keys!(Quickbooks::CAMELIZE_EXCEPTIONS).order!(@klass.camelized_valid_filters)
76
+
77
+ # Complain if:
78
+ # 1) type is :mod or :delete, and object supplied is not a valid model
79
+ raise ArgumentError, "A Quickbooks record object must be supplied to perform an add, mod or del action" if @type.is_one_of?(:add, :mod, :delete) && !@object.is_a?(Quickbooks::Base)
65
80
  end
66
81
 
67
82
  def self.next_request_id
68
- @request_id ||= 5000
83
+ @request_id ||= 0
69
84
  @request_id += 1
70
85
  end
71
86
 
72
- # This is where the magic happens to convert a request object into xml worthy of sending off to quickbooks.
87
+ # This is where the magic happens to convert a request object into xml worthy of quickbooks.
73
88
  def to_xml(as_set=true)
74
89
  return (RequestSet.new(self)).to_xml if as_set # Simple call yields xml as a single request in a request set. However, if the xml for the lone request is required, pass false.
75
90
  req = Builder::XmlMarkup.new(:indent => 2)
76
- case
91
+ request_root, container = case
77
92
  when @type.is_one_of?(:query)
78
- request_root = "#{@object}QueryRq"
79
- container = nil
80
- when @type == :mod
81
- request_root = "#{@object}ModRq"
82
- container = "#{@object}Mod"
93
+ ["#{@klass.class_leaf_name}QueryRq", nil]
83
94
  when @type == :add
84
- request_root = "#{@object}AddRq"
85
- container = "#{@object}Add"
95
+ ["#{@klass.class_leaf_name}AddRq", "#{@klass.class_leaf_name}Add"]
96
+ when @type == :mod
97
+ ["#{@klass.class_leaf_name}ModRq", "#{@klass.class_leaf_name}Mod"]
86
98
  when @type == :delete
87
- # request_root = "#{@object}DelRq"
88
- request_root = Quickbooks::ListItem.child_types.include?(@object) ? 'ListDelRq' : 'TxnDelRq'
89
- container = nil
90
- # container = Quickbooks::ListItem.child_types.include?(@object) ? 'ListDelType' : 'TxnDelType'
91
- @attributes.merge!((Quickbooks::ListItem.child_types.include?(@object) ? 'ListDelType' : 'TxnDelType') => @object)
99
+ ["#{@klass.ListOrTxn}DelRq", nil]
92
100
  else
93
101
  raise RuntimeError, "Could not convert this request to qbxml!\n#{self.inspect}"
94
102
  end
95
103
  inner_stuff = lambda {
96
- ['ListDelType', 'TxnDelType', 'ListID', 'TxnID', 'EditSequence'].each do |key|
97
- req.tag!(key, @attributes.delete(key)) if @attributes.has_key?(key)
98
- end if @attributes
99
- (@filters || @attributes).each do |key,value|
100
- req.tag!(key, value)
104
+ deep_tag = lambda {|k,v|
105
+ if v.is_a?(Hash)
106
+ if k == ''
107
+ v.each { |k,v|
108
+ deep_tag.call(k,v)
109
+ }
110
+ else
111
+ req.tag!(k.camelcase) { v.each { |k,v| deep_tag.call(k,v) } }
112
+ end
113
+ else
114
+ req.tag!(k.camelcase,uncast(v))
115
+ end
116
+ }
117
+
118
+ # Add the specific elements for the respective request type
119
+ if @type.is_one_of?(:add, :mod)
120
+ if @type == :mod
121
+ # First the ObjectId:
122
+ req.tag!(@klass.ListOrTxn + 'ID', @object.send("#{@klass.ListOrTxn}Id".underscore))
123
+ # Second the EditSequence
124
+ req.tag!('EditSequence', @object.send(:edit_sequence))
125
+ end
126
+ # Then, all the dirty_attributes
127
+ deep_tag.call('',@object.to_dirty_hash) # (this is an hash statically ordered to the model's qbxml attribute order)
128
+ elsif @type == :query && @object.class == @klass
129
+ # Sent an instance object for a query - we should include the ListId/TxnId (then other filters?)
130
+ req.tag!(@klass.ListOrTxn + 'ID', @object.send("#{@klass.ListOrTxn}Id".underscore))
131
+ deep_tag.call('', @filters)
132
+ elsif @type == :delete
133
+ req.tag!(@klass.ListOrTxn + 'DelType', @klass.class_leaf_name)
134
+ req.tag!(@klass.ListOrTxn + 'ID', @object.send("#{@klass.ListOrTxn}Id".underscore))
135
+ else
136
+ # just filters
137
+ deep_tag.call('', @filters)
101
138
  end
139
+ # Lastly, specify the fields to return, if desired
140
+ @ret_elements.each { |r| req.tag!('IncludeRetElement', r) } if !@ret_elements.blank?
102
141
  }
103
142
  req.tag!(request_root, :requestID => self.class.next_request_id) {
104
143
  if container
@@ -112,6 +151,16 @@ thequickbooks_qbxmlrequestsetxml
112
151
  # puts req.target!
113
152
  req.target!
114
153
  end
154
+
155
+ private
156
+ def uncast(v)
157
+ case
158
+ when v.is_a?(Time)
159
+ v.xmlschema
160
+ else
161
+ v
162
+ end
163
+ end
115
164
  end
116
165
 
117
166
  module RequestSetArrayExt