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,41 @@
|
|
|
1
|
+
require "date"
|
|
2
|
+
|
|
3
|
+
class Time
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# Convert to ISO 8601 representation
|
|
7
|
+
#
|
|
8
|
+
# Time.now.to_json #=> "\"2008-03-28T17:54:20-05:00\""
|
|
9
|
+
#
|
|
10
|
+
# @return [String]
|
|
11
|
+
# ISO 8601 compatible representation of the Time object.
|
|
12
|
+
#
|
|
13
|
+
# @api public
|
|
14
|
+
def to_json(*)
|
|
15
|
+
self.xmlschema.to_json
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Return receiver (for DateTime/Time conversion protocol).
|
|
20
|
+
#
|
|
21
|
+
# Time.now.to_time #=> Wed Nov 19 20:08:28 -0800 2008
|
|
22
|
+
#
|
|
23
|
+
# @return [Time] Receiver
|
|
24
|
+
#
|
|
25
|
+
# @api public
|
|
26
|
+
def to_time
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# Convert to DateTime (for DateTime/Time conversion protocol).
|
|
32
|
+
#
|
|
33
|
+
# Time.now.to_datetime #=> #<DateTime: 106046956823/43200,-1/3,2299161>
|
|
34
|
+
#
|
|
35
|
+
# @return [DateTime] DateTime object representing the same moment as receiver
|
|
36
|
+
#
|
|
37
|
+
# @api public
|
|
38
|
+
def to_datetime
|
|
39
|
+
DateTime.parse self.to_s
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/quickbooks/model.rb
CHANGED
|
@@ -1,178 +1,560 @@
|
|
|
1
|
-
require 'quickbooks/
|
|
2
|
-
|
|
1
|
+
require 'quickbooks/element'
|
|
3
2
|
module Quickbooks
|
|
4
|
-
#
|
|
5
|
-
CAMELIZE_EXCEPTIONS = {'list_id' => 'ListID', 'txn_id' => 'TxnID', 'owner_id' => 'OwnerID'}
|
|
6
|
-
|
|
3
|
+
# Model steps above related Element classes give you a way to manage your Quickbooks data like _objects_ rather than by managing the _communication_ of those objects.
|
|
7
4
|
class Model
|
|
5
|
+
# A ModelProperty is just a little data store that keeps track of characteristics of each of the model's properties.
|
|
6
|
+
class ModelProperty
|
|
7
|
+
def initialize(klass_name, addable=false, writable=false, readable=false) #:nodoc:
|
|
8
|
+
@klass_name = klass_name
|
|
9
|
+
@addable = !!addable
|
|
10
|
+
@writable = !!writable
|
|
11
|
+
@readable = !!readable
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Get the class name of the property.
|
|
15
|
+
def klass_name
|
|
16
|
+
@klass_name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Get the class constant for the property.
|
|
20
|
+
def klass
|
|
21
|
+
QB[@klass_name]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Can I include this property when creating a new object?
|
|
25
|
+
def addable?
|
|
26
|
+
@addable
|
|
27
|
+
end
|
|
28
|
+
def addable! #:nodoc:
|
|
29
|
+
@addable = true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Can I include this property when modifying an existing object?
|
|
33
|
+
def writable?
|
|
34
|
+
@writable
|
|
35
|
+
end
|
|
36
|
+
def writable! #:nodoc:
|
|
37
|
+
@writable = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Is this property returned when reading back from QuickBooks?
|
|
41
|
+
def readable?
|
|
42
|
+
@readable
|
|
43
|
+
end
|
|
44
|
+
def readable! #:nodoc:
|
|
45
|
+
@readable = true
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
8
49
|
class << self
|
|
50
|
+
attr_reader :properties
|
|
9
51
|
|
|
10
|
-
def
|
|
11
|
-
|
|
12
|
-
|
|
52
|
+
def help
|
|
53
|
+
"QB::#{short_name}.query_xsd for available query params\nQB::#{short_name}.write_xsd for create properties\nQB::#{short_name}.read_write_xsd for modifiable properties\nTry one of the above options to study this particular model's properties."
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Set or retrieve the XSD data for the Model's read elements -- properties that are readable.
|
|
57
|
+
def read_xsd(xsd=nil)
|
|
58
|
+
raise ArgumentError, "must be an Quickbooks::XSD::Element" unless xsd.nil? || xsd.is_a?(Quickbooks::XSD::Element)
|
|
59
|
+
if xsd
|
|
60
|
+
@read_xsd = xsd
|
|
61
|
+
create_combined_elements!
|
|
13
62
|
end
|
|
14
|
-
|
|
15
|
-
|
|
63
|
+
@read_xsd ||= nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Set or retrieve the XSD data for the Model's read/write elements -- properties that can be used in modifying objects.
|
|
67
|
+
# This is also the controller for validation of the object when you're modifying an existing object.
|
|
68
|
+
def read_write_xsd(xsd=nil)
|
|
69
|
+
raise ArgumentError, "must be an Quickbooks::XSD::Element" unless xsd.nil? || xsd.is_a?(Quickbooks::XSD::Element)
|
|
70
|
+
if xsd
|
|
71
|
+
@read_write_xsd = xsd
|
|
72
|
+
create_combined_elements!
|
|
16
73
|
end
|
|
17
|
-
|
|
74
|
+
@read_write_xsd ||= nil
|
|
18
75
|
end
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
76
|
+
|
|
77
|
+
# Set or retrieve the XSD data for the Model's write elements -- properties that can be used in creating a new object.
|
|
78
|
+
# This is also the controller for validation of the object when you're creating a new object.
|
|
79
|
+
def write_xsd(xsd=nil)
|
|
80
|
+
raise ArgumentError, "must be an Quickbooks::XSD::Element" if !xsd.nil? && !xsd.is_a?(Quickbooks::XSD::Element)
|
|
81
|
+
if xsd
|
|
82
|
+
@write_xsd = xsd
|
|
83
|
+
create_combined_elements!
|
|
84
|
+
end
|
|
85
|
+
@write_xsd ||= nil
|
|
23
86
|
end
|
|
24
|
-
|
|
25
|
-
|
|
87
|
+
|
|
88
|
+
# Set or retrieve the XSD data for the Model's write elements -- properties that can be used in creating a new object.
|
|
89
|
+
# This is also the controller for validation of the object when you're creating a new object.
|
|
90
|
+
def query_xsd(xsd=nil)
|
|
91
|
+
raise ArgumentError, "must be an Quickbooks::XSD::Element" if !xsd.nil? && !xsd.is_a?(Quickbooks::XSD::Element)
|
|
92
|
+
@query_xsd = xsd if xsd
|
|
93
|
+
@query_xsd ||= nil
|
|
26
94
|
end
|
|
27
|
-
|
|
28
|
-
|
|
95
|
+
|
|
96
|
+
def query_attributes
|
|
97
|
+
if query_xsd
|
|
98
|
+
query_xsd.attributes
|
|
99
|
+
else
|
|
100
|
+
raise RuntimeError, "You must first initialize query_xsd."
|
|
101
|
+
end
|
|
29
102
|
end
|
|
30
|
-
|
|
31
|
-
|
|
103
|
+
|
|
104
|
+
def create_combined_elements!
|
|
105
|
+
# If this runs more than once it's okay; but it shouldn't ever because the attributes are written in order so write_xsd are done last.
|
|
106
|
+
if read_xsd
|
|
107
|
+
@properties = {}
|
|
108
|
+
@associations = {}
|
|
109
|
+
[read_xsd, read_write_xsd, write_xsd].compact.inject([]) {|a,e| a.concat(e.children)}.each do |prop_xsd|
|
|
110
|
+
@properties[prop_xsd.name.to_sym] = ModelProperty.new(Quickbooks.get_constant(prop_xsd.name).short_name, write_xsd && write_xsd.include?(prop_xsd.name), read_write_xsd && read_write_xsd.include?(prop_xsd.name), read_xsd && read_xsd.include?(prop_xsd.name))
|
|
111
|
+
@associations[prop_xsd.name[0..-4].to_sym] = @properties[prop_xsd.name.to_sym] if prop_xsd.name =~ /Ref$/
|
|
112
|
+
end
|
|
113
|
+
end
|
|
32
114
|
end
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
115
|
+
private :create_combined_elements!
|
|
116
|
+
|
|
117
|
+
# Emulated Associations
|
|
118
|
+
attr_reader :associations # set up in .create_combined_elements!
|
|
119
|
+
# * * * *
|
|
120
|
+
|
|
121
|
+
# Request an array of whatever QuickBooks objects match the query. The args presented should be attributes with which to construct
|
|
122
|
+
# a *QueryRq for whatever model you're in.
|
|
123
|
+
def all(args={})
|
|
124
|
+
process_finder_args(args)
|
|
125
|
+
# This includes DataExt if user has asked for it.
|
|
126
|
+
if args.has_key?(:IncludeExtData)
|
|
127
|
+
data_scope = args.delete(:IncludeExtData)
|
|
128
|
+
args[:OwnerID] = [data_scope == true ? '0' : data_scope] if data_scope
|
|
129
|
+
end
|
|
130
|
+
# Create a #{short_name}Query out of the filters, and send it to quickbooks for a response; instantiate the response objects.
|
|
131
|
+
query = Quickbooks.get_constant("#{short_name}QueryRq").new(args)
|
|
132
|
+
if caller.join =~ /lib\/quickbooks\.rb:\d+:in `qbxml'/
|
|
133
|
+
Quickbooks.requestify(query).to_xml
|
|
134
|
+
else
|
|
135
|
+
catch :response do
|
|
136
|
+
response = Quickbooks.execute(query)[:QBXMLMsgsRs]["#{short_name}QueryRs"][0]
|
|
137
|
+
(response.nil? || response["#{short_name}Ret"].nil? || response["#{short_name}Ret"].empty?) ?
|
|
138
|
+
[] : response["#{short_name}Ret"].collect {|r| r.to_model}
|
|
139
|
+
end
|
|
37
140
|
end
|
|
38
|
-
cvf
|
|
39
141
|
end
|
|
40
|
-
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
142
|
+
# Request a single QuickBooks object that matches the query. The args presented should be attributes with which to construct
|
|
143
|
+
# a *QueryRq for whatever model you're in. This simply adds the property :MaxReturned => 1, if the request is still valid with it.
|
|
144
|
+
def first(args={})
|
|
145
|
+
process_finder_args(args)
|
|
146
|
+
# Create a #{short_name}Query out of the filters, and send it to quickbooks for a response; instantiate the response objects.
|
|
147
|
+
query = QB["#{short_name}QueryRq"].new(args)
|
|
148
|
+
started_valid = query.valid?
|
|
149
|
+
query[:MaxReturned] = 1
|
|
150
|
+
query.delete(:MaxReturned) if started_valid && !query.valid?
|
|
151
|
+
if caller.join =~ /lib\/quickbooks\.rb:\d+:in `qbxml'/
|
|
152
|
+
Quickbooks.requestify(query).to_xml
|
|
48
153
|
else
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
else
|
|
54
|
-
@object_properties[prop.class_leaf_name.underscore.to_sym] = prop
|
|
55
|
-
read_write << prop.class_leaf_name.underscore.to_sym
|
|
56
|
-
class_eval "def #{prop.class_leaf_name.underscore}=(v); @#{prop.class_leaf_name.underscore} = #{prop.name}.new(v); end
|
|
57
|
-
def #{prop.class_leaf_name.underscore}; @#{prop.class_leaf_name.underscore}; end"
|
|
58
|
-
end
|
|
154
|
+
catch :response do
|
|
155
|
+
response = Quickbooks.execute(query)[:QBXMLMsgsRs]["#{short_name}QueryRs"][0]
|
|
156
|
+
response.nil? || response["#{short_name}Ret"].nil? || response["#{short_name}Ret"][0].nil? ?
|
|
157
|
+
nil : response["#{short_name}Ret"][0].to_model
|
|
59
158
|
end
|
|
60
159
|
end
|
|
61
160
|
end
|
|
62
161
|
|
|
63
|
-
#
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
|
|
162
|
+
# Iterates through all records matching *args, but only grabs 70 at a time from QuickBooks.
|
|
163
|
+
def each(args={})
|
|
164
|
+
process_finder_args(args)
|
|
165
|
+
query = Quickbooks.get_constant("#{short_name}QueryRq").new(args)
|
|
166
|
+
started_valid = query.valid?
|
|
167
|
+
query[:iterator] = 'Start'
|
|
168
|
+
query[:MaxReturned] = 70
|
|
169
|
+
query.delete(:iterator, :MaxReturned) if started_valid && !query.valid?
|
|
170
|
+
if caller.join =~ /lib\/quickbooks\.rb:\d+:in `qbxml'/
|
|
171
|
+
Quickbooks.requestify(query).to_xml
|
|
67
172
|
else
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
173
|
+
catch :response do
|
|
174
|
+
result = Quickbooks.execute(query)
|
|
175
|
+
ary = result[:QBXMLMsgsRs]["#{short_name}QueryRs"][0]["#{short_name}Ret"].each {|e| yield e.to_model}
|
|
176
|
+
if iteratorID = result[:QBXMLMsgsRs]["#{short_name}QueryRs"][0][:iteratorID]
|
|
177
|
+
query[:iteratorID] = iteratorID
|
|
178
|
+
query[:iterator] = 'Continue'
|
|
179
|
+
until(result[:QBXMLMsgsRs]["#{short_name}QueryRs"][0][:iteratorRemainingCount].to_i == 0)
|
|
180
|
+
result = Quickbooks.execute(query)
|
|
181
|
+
ary.concat result[:QBXMLMsgsRs]["#{short_name}QueryRs"][0]["#{short_name}Ret"].each {|e| yield e.to_model}
|
|
182
|
+
end
|
|
77
183
|
end
|
|
184
|
+
ary
|
|
78
185
|
end
|
|
79
186
|
end
|
|
80
187
|
end
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
188
|
+
|
|
189
|
+
# Should be used only internally, but if you need this - use it to paint a new QuickBooks model object that thinks it already exists in QuickBooks.
|
|
190
|
+
def instantiate(attrs={})
|
|
191
|
+
obj = allocate
|
|
192
|
+
obj.instance_variable_set(:@new_record, nil)
|
|
193
|
+
obj.attributes = attrs
|
|
194
|
+
obj.instance_variable_set(:@new_record, false)
|
|
195
|
+
obj.send :clean!
|
|
196
|
+
obj
|
|
84
197
|
end
|
|
85
|
-
end
|
|
86
198
|
|
|
87
|
-
|
|
199
|
+
# Replies whether it's a :List or a :Txn item.
|
|
200
|
+
def item_type
|
|
201
|
+
@properties.has_key?(:ListID) ? :List : :Txn
|
|
202
|
+
end
|
|
88
203
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
204
|
+
private
|
|
205
|
+
def process_finder_args(args)
|
|
206
|
+
# This includes DataExt if user has asked for it.
|
|
207
|
+
if args.has_key?(:IncludeExtData)
|
|
208
|
+
data_scope = args.delete(:IncludeExtData)
|
|
209
|
+
args[:OwnerID] = [data_scope == true ? '0' : data_scope] if data_scope
|
|
210
|
+
end
|
|
211
|
+
end
|
|
92
212
|
end
|
|
93
|
-
|
|
94
|
-
#
|
|
95
|
-
def
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
213
|
+
|
|
214
|
+
# Paints up a new QuickBooks model object with the attributes you give it. Marked as new, so when you hit save, it will actually create it.
|
|
215
|
+
def initialize(attrs={})
|
|
216
|
+
@new_record = :init
|
|
217
|
+
self.attributes = attrs
|
|
218
|
+
@new_record = true
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def inspect #:nodoc:
|
|
222
|
+
"<#{self.class.short_name}:##{object_id}\n #{attributes.collect {|k,v| v.inspect}.join("\n").gsub(/\n/, "\n ")}>"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# true if new record, false if already exists.
|
|
226
|
+
#
|
|
227
|
+
# For deeper inspection, @new_record is set to:
|
|
228
|
+
# * :init if being initialized
|
|
229
|
+
# * true if new_record
|
|
230
|
+
# * nil if being instantiated
|
|
231
|
+
# * false if not new_record
|
|
232
|
+
def new_record?
|
|
233
|
+
!!@new_record
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Returns the ListID or TxnID value respective of its type.
|
|
237
|
+
def id
|
|
238
|
+
self[:"#{self.class.item_type}ID"]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Display a list of the available attribute names
|
|
242
|
+
def properties(key=nil)
|
|
243
|
+
if key
|
|
244
|
+
self.class.properties[key]
|
|
245
|
+
else
|
|
246
|
+
self.class.properties.keys
|
|
99
247
|
end
|
|
100
|
-
attrs
|
|
101
248
|
end
|
|
102
|
-
|
|
249
|
+
|
|
250
|
+
# Just return the attributes hash.
|
|
251
|
+
def attributes
|
|
252
|
+
@attributes ||= {}
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Pass a hash of attributes, and it'll set each one one by one via []=.
|
|
103
256
|
def attributes=(attrs)
|
|
104
|
-
raise ArgumentError, "
|
|
105
|
-
attrs.each do |
|
|
106
|
-
if self.
|
|
107
|
-
self.
|
|
257
|
+
raise ArgumentError, "must be a hash" unless attrs.is_a?(Hash)
|
|
258
|
+
attrs.each do |k,v|
|
|
259
|
+
if self.class.properties.has_key?(k.to_sym) || self.class.associations.has_key?(k.to_sym)
|
|
260
|
+
self[k.to_sym] = v
|
|
261
|
+
else
|
|
262
|
+
raise "Model #{self.class.short_name} does not have property #{k}!"
|
|
108
263
|
end
|
|
109
264
|
end
|
|
110
265
|
end
|
|
266
|
+
|
|
267
|
+
# Access attribute values by key. Also access associations this way.
|
|
268
|
+
def [](key)
|
|
269
|
+
return self.attributes = key if key.is_a?(Hash)
|
|
270
|
+
key = case key # make a string
|
|
271
|
+
when String
|
|
272
|
+
key.to_sym
|
|
273
|
+
when Symbol
|
|
274
|
+
key
|
|
275
|
+
when Module
|
|
276
|
+
key.short_name.to_sym
|
|
277
|
+
end
|
|
278
|
+
return get_associated(key) if self.class.associations.has_key?(key)
|
|
279
|
+
attr_klass = begin
|
|
280
|
+
QB[key.to_s]
|
|
281
|
+
rescue RuntimeError
|
|
282
|
+
raise "Invalid key '#{key.inspect}'"
|
|
283
|
+
end
|
|
284
|
+
repeatable = (new_record? ? self.class.write_xsd : (@new_record == false ? self.class.read_write_xsd : self.class.read_xsd)).repeatable?(key.to_s)
|
|
285
|
+
attributes[key] = ElementCollection.new(self, key) if repeatable && !attributes[key].is_a?(ElementCollection)
|
|
286
|
+
attributes[key]
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Set attribute values by key. Also set associated objects here.
|
|
290
|
+
def []=(key,value)
|
|
291
|
+
key = case key # make a symbol
|
|
292
|
+
when String
|
|
293
|
+
key.to_sym
|
|
294
|
+
when Symbol
|
|
295
|
+
key
|
|
296
|
+
when Module
|
|
297
|
+
key.short_name.to_sym
|
|
298
|
+
end
|
|
299
|
+
return associate(key, value) if self.class.associations.include?(key)
|
|
300
|
+
raise RuntimeError, "'#{key}' is not a valid property name for #{self.class.name}!" unless self.class.properties.has_key?(key)
|
|
301
|
+
raise RuntimeError, "'#{key}' cannot be assigned on creation" if new_record? && !self.class.properties[key].addable?
|
|
302
|
+
raise RuntimeError, "'#{key}' cannot be modified" if @new_record == false && !self.class.properties[key].writable?
|
|
303
|
+
# Instantiate the value into the attribute class, if necessary
|
|
304
|
+
attr_klass = QB[key.to_s]
|
|
305
|
+
# If it *should* be an ElementCollection, it shouldn't be set here at all!
|
|
306
|
+
if (new_record? ? self.class.write_xsd : (@new_record == false ? self.class.read_write_xsd : self.class.read_xsd)).repeatable?(key.to_s)
|
|
307
|
+
if @new_record.in?(nil, :init) || value.is_a?(Array)
|
|
308
|
+
attributes[key] = ElementCollection.new(self, key, value)
|
|
309
|
+
attributes[key].send(:dirty!)
|
|
310
|
+
else
|
|
311
|
+
# If it *should* be an array element, it shouldn't be set here as a single value. This is just for safeguard, so that syntax
|
|
312
|
+
# always shows what is going on. For an array element, set it with: object.some_attr = [value]
|
|
313
|
+
raise RuntimeError, "You can't set a single value into a multi-value element to using equals(=). Use \"model[:#{key}] << value\" to append, or wrap the value in an array -- \"model[:#{key}] = [ value ]\" -- if you want to completely replace the current value set."
|
|
314
|
+
end
|
|
315
|
+
else
|
|
316
|
+
value = @new_record.nil? ? attr_klass.instantiate(value) : attr_klass.new(value) unless value.is_a?(attr_klass)
|
|
317
|
+
value.send(:dirty!)
|
|
318
|
+
attributes[key] = value
|
|
319
|
+
end
|
|
320
|
+
remove_incorrect_associated(key.to_s.sub(/Ref$/,'').to_sym, value) if self.class.associations.include?(key.to_s.sub(/Ref$/,'').to_sym)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Completely remove an attribute value from the object. This is different from setting the attribute to nil.
|
|
324
|
+
def delete(*keys)
|
|
325
|
+
keys.each do |key|
|
|
326
|
+
key = case key # make a string
|
|
327
|
+
when String
|
|
328
|
+
key.to_sym
|
|
329
|
+
when Symbol
|
|
330
|
+
key
|
|
331
|
+
when Module
|
|
332
|
+
key.short_name.to_sym
|
|
333
|
+
end
|
|
334
|
+
attributes.delete(key)
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Return only the attributes that have been changed or whose descendents have changed. If include_required is true, then whatever is necessary to make the set of attributes valid is returned as well.
|
|
339
|
+
def dirty_attributes(include_required=false)
|
|
340
|
+
Hash[*((include_required ?
|
|
341
|
+
attributes.select {|k,v| v.dirty? || (new_record? ? self.class.write_xsd : self.class.read_write_xsd).required?(k) || !(new_record? ? self.class.write_xsd : self.class.read_write_xsd).validate(self.class.short_name => attributes.except(v.class.short_name))} :
|
|
342
|
+
attributes.select {|k,v| v.dirty?}).flatten)]
|
|
343
|
+
end
|
|
111
344
|
|
|
112
|
-
#
|
|
113
|
-
def
|
|
114
|
-
|
|
345
|
+
# Return only the attributes that have not been modified.
|
|
346
|
+
def clean_attributes
|
|
347
|
+
attributes.except(dirty_attributes.keys)
|
|
115
348
|
end
|
|
116
349
|
|
|
117
|
-
#
|
|
350
|
+
# Have any of my values, or my descendents' values, been changed?
|
|
118
351
|
def dirty?
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
self.instance_variable_get('@' + column.to_s) != original_values[column.to_s]
|
|
122
|
-
end
|
|
352
|
+
# Test for any dirty elements
|
|
353
|
+
@dirty || attributes.any? {|k,v| v.dirty?}
|
|
123
354
|
end
|
|
124
355
|
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
compare = original_values if compare.empty?
|
|
129
|
-
pairs = {}
|
|
130
|
-
self.class.read_write.each do |column|
|
|
131
|
-
value = instance_variable_get('@' + column.to_s)
|
|
132
|
-
if value != compare[column.to_s]
|
|
133
|
-
pairs[column.to_s] = value
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
pairs
|
|
356
|
+
# Do I have a relative *Ref class?
|
|
357
|
+
def can_ref?
|
|
358
|
+
Quickbooks.get_constant(self.class.short_name.gsub(/(Add|Mod|Ret)/,'') + 'Ref') && true rescue false
|
|
137
359
|
end
|
|
138
360
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
361
|
+
# Make a Ref object out of me.
|
|
362
|
+
def to_ref
|
|
363
|
+
begin
|
|
364
|
+
attrs = {}
|
|
365
|
+
idsym = :"#{self.class.item_type}ID"
|
|
366
|
+
if self[idsym]
|
|
367
|
+
attrs[idsym] = self[idsym]
|
|
368
|
+
elsif self[:FullName]
|
|
369
|
+
attrs[:FullName] = self[:FullName]
|
|
144
370
|
else
|
|
145
|
-
|
|
371
|
+
return nil # Not able to ref
|
|
146
372
|
end
|
|
373
|
+
Quickbooks.get_constant(self.class.short_name.gsub(/(Add|Mod|Ret)/,'') + 'Ref').new(attrs)
|
|
374
|
+
rescue NameError => e
|
|
375
|
+
nil
|
|
147
376
|
end
|
|
148
|
-
hsh
|
|
149
377
|
end
|
|
150
378
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
379
|
+
# Turn me into an element (ending in *Add or *Mod, depending on my new_record? situation).
|
|
380
|
+
def to_element
|
|
381
|
+
# List clean attributes
|
|
382
|
+
clean_attrs = clean_attributes.collect {|k,v| v.respond_to?(:to_element) ? v.to_element : v}.to_hash_via {|e| [(e.is_a?(ElementCollection) ? e.type.short_name : e.class.short_name), e]}
|
|
383
|
+
# List dirty attributes
|
|
384
|
+
dirty_attrs = dirty_attributes.collect {|k,v| v.respond_to?(:to_element) ? v.to_element : v}.to_hash_via {|e| [(e.is_a?(ElementCollection) ? e.type.short_name : e.class.short_name), e]}
|
|
385
|
+
# Set up the corresponding element with corresponding properties
|
|
386
|
+
element = element_klass.instantiate(clean_attrs)
|
|
387
|
+
element.attributes = dirty_attrs
|
|
388
|
+
# Transfer the order index (for attribute sorting) from elements that were in a collection.
|
|
389
|
+
element.instance_variable_set(:@collection_index, @collection_index) if instance_variables.include?('@collection_index')
|
|
390
|
+
element
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# I don't think this is actually being used anywhere...
|
|
394
|
+
def to_update_element
|
|
395
|
+
to_element.to_update_element
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def to_xml
|
|
399
|
+
to_element.to_xml
|
|
400
|
+
end
|
|
401
|
+
def to_dirty_xml(include_required=false)
|
|
402
|
+
to_element.to_dirty_xml(include_required)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Am I completely valid?
|
|
406
|
+
def valid?
|
|
407
|
+
validate.perfect?
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Use this to validate the object and get the Valean result instead of just a true/false value.
|
|
411
|
+
def validate
|
|
412
|
+
r = new_record? ? self.class.write_xsd.validate(self) : self.class.read_write_xsd.validate(self)
|
|
413
|
+
errors.replace(r.errors)
|
|
414
|
+
r
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# The errors returned by the latest validate call.
|
|
418
|
+
def errors
|
|
419
|
+
@errors ||= []
|
|
420
|
+
end
|
|
421
|
+
def add_error(msg) #:nodoc:
|
|
422
|
+
errors << [nil, msg]
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Save the object to QuickBooks, whether that means creating a new record or updating an existing one.
|
|
426
|
+
def save
|
|
427
|
+
save_associations
|
|
428
|
+
query = Quickbooks.get_constant("#{element_klass.short_name}Rq").new([ to_element.to_update_element ])
|
|
429
|
+
if caller.join =~ /lib\/quickbooks\.rb:\d+:in `qbxml'/
|
|
430
|
+
Quickbooks.requestify(query).to_xml
|
|
431
|
+
else
|
|
432
|
+
catch :response do
|
|
433
|
+
@last_response = Quickbooks.execute(query)
|
|
434
|
+
response = @last_response[:QBXMLMsgsRs]["#{element_klass.short_name}Rs"][0]
|
|
435
|
+
reload = if response.nil? || response["#{self.class.short_name}Ret"].nil? || response["#{self.class.short_name}Ret"].nil?
|
|
436
|
+
raise "Response doesn't make sense after saving #{inspect}: #{@last_response.inspect} (#{@last_response[:QBXMLMsgsRs]["#{element_klass.short_name}Rs"][0].nil?})"
|
|
437
|
+
else
|
|
438
|
+
response["#{self.class.short_name}Ret"].to_model
|
|
439
|
+
end
|
|
440
|
+
@attributes = reload.attributes
|
|
441
|
+
true
|
|
158
442
|
end
|
|
159
443
|
end
|
|
160
|
-
hsh
|
|
161
444
|
end
|
|
162
445
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
446
|
+
# Update some extended data by key and value.
|
|
447
|
+
# By default, just include the key and value, and the owner id of '0' will be used.
|
|
448
|
+
# However, if you want to specify a different owner, just add that on as a third parameter.
|
|
449
|
+
def update_extended_data(key, value, owner=nil)
|
|
450
|
+
# First, see if this key already exists or not.
|
|
451
|
+
load_extended_data unless attributes[:DataExt]
|
|
452
|
+
|
|
453
|
+
attrs = {
|
|
454
|
+
# Use the same OwnerID that was supplied for getting the DataExt.
|
|
455
|
+
:OwnerID => owner || ((attributes[:DataExt] && attributes[:DataExt][0]) ? attributes[:DataExt][0][:OwnerID] : '0'),
|
|
456
|
+
:DataExtName => key,
|
|
457
|
+
:"#{self.class.short_name == 'Company' ? 'Other' : self.class.item_type}DataExtType" => self.class.short_name,
|
|
458
|
+
:DataExtValue => value
|
|
459
|
+
}
|
|
460
|
+
case self.class.item_type
|
|
461
|
+
when :List
|
|
462
|
+
attrs[:ListObjRef] = {:ListID => id}
|
|
463
|
+
when :Txn
|
|
464
|
+
attrs[:TxnID] = id
|
|
465
|
+
# We don't need to bother with LineID extended data.
|
|
466
|
+
# attrs[:TxnLineID]
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
de = if self[:DataExt] && self[:DataExt].select {|de| de[:DataExtName] == key}
|
|
470
|
+
# Modify it.
|
|
471
|
+
QB::DataExtModRq.new(:DataExtMod => attrs)
|
|
472
|
+
else
|
|
473
|
+
# Create it.
|
|
474
|
+
QB::DataExtAddRq.new(:DataExtAdd => attrs)
|
|
167
475
|
end
|
|
476
|
+
Quickbooks.execute(de)
|
|
477
|
+
load_extended_data
|
|
478
|
+
end
|
|
479
|
+
# Use this to load extended data if you need to import into this same object.
|
|
480
|
+
def load_extended_data(owner='0')
|
|
481
|
+
attributes[:DataExt] = self.class.first(:"#{self.class.item_type}ID" => [id], :OwnerID => [owner])[:DataExt]
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# Save any unsaved or dirty associations.
|
|
485
|
+
def save_associations
|
|
486
|
+
# first save any of my associated items that aren't already existing
|
|
487
|
+
self.class.associations.each_key do |association_name|
|
|
488
|
+
instance_variable_set("@#{association_name}", nil) unless self.instance_variables.include?("@#{association_name}") # just to avoid needless warnings.
|
|
489
|
+
if instance_variable_get("@#{association_name}") && self[association_name].new_record?
|
|
490
|
+
self[association_name].save
|
|
491
|
+
self[association_name] = self[association_name] # re-assigns the associated Ref
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
# then save any of my children's associated items that aren't already existing
|
|
495
|
+
attributes.each { |k,attv| attv.save_associations if attv.respond_to?(:save_associations) }
|
|
168
496
|
end
|
|
169
497
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
498
|
+
# That's right. Just get rid of that object. QuickBooks knows how to handle things, and it won't let you destroy items if it'll mess things up.
|
|
499
|
+
def destroy!
|
|
500
|
+
query = Quickbooks.get_constant("#{self.class.item_type}DelRq").new("#{self.class.item_type}DelType" => self.class.short_name, "#{self.class.item_type}ID" => self["#{self.class.item_type}ID"])
|
|
501
|
+
if caller.join =~ /lib\/quickbooks\.rb:\d+:in `qbxml'/
|
|
502
|
+
Quickbooks.requestify(query).to_xml
|
|
503
|
+
else
|
|
504
|
+
catch :response do
|
|
505
|
+
Quickbooks.execute(query)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
private
|
|
511
|
+
|
|
512
|
+
def get_associated(association_name)
|
|
513
|
+
(self.instance_variables.include?("@#{association_name}") && instance_variable_get("@#{association_name}")) || begin
|
|
514
|
+
ref_klass = self.class.associations[association_name].klass
|
|
515
|
+
self[ref_klass].nil? ? nil : instance_variable_set("@#{association_name}", self[ref_klass].unref)
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def associate(association_name, value)
|
|
520
|
+
instance_variable_set("@#{association_name}", value)
|
|
521
|
+
ref = value.to_ref
|
|
522
|
+
ref_klass = self.class.associations[association_name].klass
|
|
523
|
+
!ref.nil? ?
|
|
524
|
+
self[ref_klass] = ref :
|
|
525
|
+
self.delete(ref_klass)
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def remove_incorrect_associated(association_name, value)
|
|
529
|
+
instance_variable_set("@#{association_name}", nil) if !self.instance_variables.include?("@#{association_name}") || (associated = instance_variable_get("@#{association_name}") && !associated.nil? && !associated.to_ref.nil? && (associated.to_ref[:ListID] || associated.to_ref[:TxnID] || associated.to_ref[:FullName]) != (value[:ListID] || value[:TxnID] || value[:FullName]))
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def element_klass
|
|
533
|
+
Quickbooks.get_constant(self.class.short_name + (new_record? ? 'Add' : 'Mod'))
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def clean!
|
|
537
|
+
@dirty = false
|
|
538
|
+
attributes.each {|k,v| v.send(:clean!)}
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
def dirty!
|
|
542
|
+
@dirty = true
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def method_missing(method_name, *args)
|
|
546
|
+
if method_name.to_s =~ /^[A-Z]/
|
|
547
|
+
if method_name.to_s =~ /=$/
|
|
548
|
+
# Set property
|
|
549
|
+
self[method_name.to_s.gsub(/\=$/,'')] = *args
|
|
550
|
+
elsif args.empty?
|
|
551
|
+
# Get property
|
|
552
|
+
self[method_name.to_s.gsub(/\=$/,'')]
|
|
553
|
+
else
|
|
554
|
+
raise NoMethodError, "undefined method `#{method_name}' for #{self.inspect}:#{self.class.name}", caller[0..-1]
|
|
555
|
+
end
|
|
174
556
|
else
|
|
175
|
-
self
|
|
557
|
+
raise NoMethodError, "undefined method `#{method_name}' for #{self.inspect}:#{self.class.name}", caller[0..-1]
|
|
176
558
|
end
|
|
177
559
|
end
|
|
178
560
|
end
|