quickbooks 0.4.0 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|