jm81-qbfc 0.3.0

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 (91) hide show
  1. data/.gitignore +11 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +85 -0
  4. data/Rakefile +81 -0
  5. data/VERSION +1 -0
  6. data/init.rb +3 -0
  7. data/install.rb +1 -0
  8. data/lib/qbfc.rb +41 -0
  9. data/lib/qbfc/base.rb +82 -0
  10. data/lib/qbfc/element.rb +243 -0
  11. data/lib/qbfc/entities/generated.rb +8 -0
  12. data/lib/qbfc/entity.rb +11 -0
  13. data/lib/qbfc/info.rb +42 -0
  14. data/lib/qbfc/infos/generated.rb +9 -0
  15. data/lib/qbfc/item.rb +29 -0
  16. data/lib/qbfc/items/generated.rb +11 -0
  17. data/lib/qbfc/list.rb +84 -0
  18. data/lib/qbfc/lists/account.rb +24 -0
  19. data/lib/qbfc/lists/generated.rb +15 -0
  20. data/lib/qbfc/lists/qb_class.rb +25 -0
  21. data/lib/qbfc/modifiable.rb +31 -0
  22. data/lib/qbfc/ole_wrapper.rb +201 -0
  23. data/lib/qbfc/qb_collection.rb +26 -0
  24. data/lib/qbfc/qb_types.rb +18 -0
  25. data/lib/qbfc/qbfc_const.rb +14 -0
  26. data/lib/qbfc/report.rb +95 -0
  27. data/lib/qbfc/reports/aging.rb +13 -0
  28. data/lib/qbfc/reports/budget_summary.rb +13 -0
  29. data/lib/qbfc/reports/custom_detail.rb +9 -0
  30. data/lib/qbfc/reports/custom_summary.rb +9 -0
  31. data/lib/qbfc/reports/general_detail.rb +44 -0
  32. data/lib/qbfc/reports/general_summary.rb +33 -0
  33. data/lib/qbfc/reports/job.rb +14 -0
  34. data/lib/qbfc/reports/payroll_detail.rb +13 -0
  35. data/lib/qbfc/reports/payroll_summary.rb +13 -0
  36. data/lib/qbfc/reports/rows.rb +51 -0
  37. data/lib/qbfc/reports/time.rb +12 -0
  38. data/lib/qbfc/request.rb +295 -0
  39. data/lib/qbfc/session.rb +147 -0
  40. data/lib/qbfc/terms.rb +10 -0
  41. data/lib/qbfc/terms/generated.rb +10 -0
  42. data/lib/qbfc/transaction.rb +110 -0
  43. data/lib/qbfc/transactions/generated.rb +25 -0
  44. data/lib/qbfc/voidable.rb +11 -0
  45. data/spec/fixtures/test.lgb +0 -0
  46. data/spec/fixtures/test.qbw +0 -0
  47. data/spec/fixtures/test.qbw.TLG +0 -0
  48. data/spec/integration/add_spec.rb +31 -0
  49. data/spec/integration/base_spec.rb +18 -0
  50. data/spec/integration/belongs_to_spec.rb +64 -0
  51. data/spec/integration/company_spec.rb +30 -0
  52. data/spec/integration/conditions_spec.rb +59 -0
  53. data/spec/integration/customer_spec.rb +46 -0
  54. data/spec/integration/element_finders_spec.rb +20 -0
  55. data/spec/integration/quick_test.rb +31 -0
  56. data/spec/integration/request_options_spec.rb +68 -0
  57. data/spec/rcov.opts +1 -0
  58. data/spec/spec.opts +6 -0
  59. data/spec/spec_helper.rb +62 -0
  60. data/spec/unit/base_spec.rb +138 -0
  61. data/spec/unit/element_finder_spec.rb +185 -0
  62. data/spec/unit/element_spec.rb +108 -0
  63. data/spec/unit/entities/generated_spec.rb +18 -0
  64. data/spec/unit/entity_spec.rb +18 -0
  65. data/spec/unit/info/generated_spec.rb +12 -0
  66. data/spec/unit/info_spec.rb +48 -0
  67. data/spec/unit/item_spec.rb +33 -0
  68. data/spec/unit/items/generated_spec.rb +16 -0
  69. data/spec/unit/list_finders_spec.rb +129 -0
  70. data/spec/unit/list_spec.rb +86 -0
  71. data/spec/unit/lists/account_spec.rb +20 -0
  72. data/spec/unit/lists/generated_spec.rb +15 -0
  73. data/spec/unit/lists/qb_class_spec.rb +9 -0
  74. data/spec/unit/modifiable_spec.rb +84 -0
  75. data/spec/unit/ole_wrapper_spec.rb +337 -0
  76. data/spec/unit/qb_collection_spec.rb +13 -0
  77. data/spec/unit/qbfc_const_spec.rb +10 -0
  78. data/spec/unit/qbfc_spec.rb +10 -0
  79. data/spec/unit/report_spec.rb +12 -0
  80. data/spec/unit/request_query_survey.txt +48 -0
  81. data/spec/unit/request_spec.rb +486 -0
  82. data/spec/unit/session_spec.rb +144 -0
  83. data/spec/unit/terms/generated_spec.rb +14 -0
  84. data/spec/unit/terms_spec.rb +18 -0
  85. data/spec/unit/transaction_finders_spec.rb +125 -0
  86. data/spec/unit/transaction_spec.rb +94 -0
  87. data/spec/unit/transactions/generated_spec.rb +20 -0
  88. data/spec/unit/voidable_spec.rb +32 -0
  89. data/tasks/qbfc_tasks.rake +4 -0
  90. data/uninstall.rb +1 -0
  91. metadata +180 -0
@@ -0,0 +1,8 @@
1
+ module QBFC
2
+ # Generated Entity Types (Inherit from List)
3
+ ENTITY_TYPES = %w{Customer Employee OtherName Vendor}
4
+
5
+ # Generate Entity subclasses
6
+ # NB: All Entities are Modifiable
7
+ generate(ENTITY_TYPES, Entity, {Modifiable => ENTITY_TYPES})
8
+ end
@@ -0,0 +1,11 @@
1
+ module QBFC
2
+ # Entity objects are Customers, Employees, Vendors and OtherNames
3
+ class Entity < List
4
+ is_base_class
5
+ end
6
+ end
7
+
8
+ # Require subclass files
9
+ Dir.new(File.dirname(__FILE__) + '/entities').each do |file|
10
+ require('qbfc/entities/' + File.basename(file)) if File.extname(file) == ".rb"
11
+ end
data/lib/qbfc/info.rb ADDED
@@ -0,0 +1,42 @@
1
+ module QBFC
2
+ # Info objects are those which have only one instance within Quickbooks,
3
+ # which are: Company, CompanyActivity, Host and Preferences.
4
+ #
5
+ # Access through QBFC for these objects is read-only.
6
+ #
7
+ # QBFC::Info can be accessed via session::company, for example, or
8
+ # through QBFC::Company::get(session).
9
+ class Info < Base
10
+ class << self
11
+
12
+ # Get the Info object for the given session.
13
+ # session.[qb_name] aliases this functionality.
14
+ # For example QBFC::Company.get(session) and
15
+ # session.company are equivalent.
16
+ #
17
+ # It accepts the follow options as a hash:
18
+ # - <tt>:owner_id</tt>: One or more OwnerIDs, used in accessing
19
+ # custom fields (aka private data extensions).
20
+ def get(sess, *args)
21
+ # Setup q, options and base_options arguments
22
+ q, options, base_options = parse_find_args(*args)
23
+ q ||= create_query(sess)
24
+ q.apply_options(options)
25
+
26
+ new(sess, q.response)
27
+ end
28
+
29
+ # This is a convenience alias for +get+.
30
+ # It exists solely so that I don't have to modify
31
+ # Session#method_missing.
32
+ def find(sess, what, *args) #:nodoc:
33
+ get(sess, *args)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ # Require subclass files
40
+ Dir.new(File.dirname(__FILE__) + '/infos').each do |file|
41
+ require('qbfc/infos/' + File.basename(file)) if File.extname(file) == ".rb"
42
+ end
@@ -0,0 +1,9 @@
1
+ module QBFC
2
+
3
+ # Generated Info types
4
+ INFO_TYPES = %w{Company CompanyActivity Host Preferences}
5
+
6
+ # Generate List subclasses
7
+ generate(INFO_TYPES, Info)
8
+
9
+ end
data/lib/qbfc/item.rb ADDED
@@ -0,0 +1,29 @@
1
+ module QBFC
2
+ class Item < List
3
+ is_base_class
4
+
5
+ class << self
6
+
7
+ # Adds a SpecialItem.
8
+ #
9
+ # +item_type+ should be a constent, for example:
10
+ #
11
+ # Account.add_special(sess, QBFC_CONST::SitFinanceCharge)
12
+ #
13
+ # See SDK docs for SpecialItemAdd for more details.
14
+ def add_special(sess, item_type)
15
+ rq = QBFC::Request.new(sess, "SpecialItemAdd")
16
+ rq.special_item_type = item_type
17
+
18
+ # Insofar as I never actually plan to use this method, just return
19
+ # response.
20
+ return rq.response
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ # Require subclass files
27
+ Dir.new(File.dirname(__FILE__) + '/items').each do |file|
28
+ require('qbfc/items/' + File.basename(file)) if File.extname(file) == ".rb"
29
+ end
@@ -0,0 +1,11 @@
1
+ module QBFC
2
+
3
+ # Generated Item Types (Inherit from List)
4
+ ITEM_TYPES = %w{ItemService ItemNonInventory ItemOtherCharge ItemInventory ItemInventoryAssembly ItemFixedAsset
5
+ ItemSubtotal ItemDiscount ItemPayment ItemSalesTax ItemSalesTaxGroup ItemGroup}
6
+
7
+ # Generate Item subclasses
8
+ # NB: All Items are Modifiable
9
+ generate(ITEM_TYPES, Item, {Modifiable => ITEM_TYPES})
10
+
11
+ end
data/lib/qbfc/list.rb ADDED
@@ -0,0 +1,84 @@
1
+ module QBFC
2
+ # List objects are those with names, such as Accounts, Entities, and Items.
3
+ #
4
+ # Note on the name: it doesn't make sense since a List is actually a single object,
5
+ # but it fits with the SDK's naming scheme, and I couldn't think of a better one.
6
+ class List < Element
7
+ is_base_class
8
+ ID_NAME = "ListID"
9
+
10
+ class << self
11
+
12
+ # Find by name (actually, FullName) of List record.
13
+ # +options+ are the same as those for in +find+.
14
+ def find_by_name(sess, full_name, options = {})
15
+ q = create_query(sess)
16
+ q.query.FullNameList.Add(full_name)
17
+ find(sess, :first, q, options)
18
+ end
19
+
20
+ alias_method :find_by_full_name, :find_by_name
21
+
22
+ # Find by ListID of List record.
23
+ # +options+ are the same as those for in +find+.
24
+ def find_by_id(sess, id, options = {})
25
+ q = create_query(sess)
26
+ q.query.ListIDList.Add(id)
27
+ find(sess, :first, q, options)
28
+ end
29
+
30
+ # Find by either name or id. Tries id first, then name.
31
+ def find_by_name_or_id(*args)
32
+ find_by_id(*args) || find_by_name(*args)
33
+ end
34
+
35
+ alias_method :find_by_unique_id, :find_by_name_or_id
36
+
37
+ end
38
+
39
+ # Alias of ListID for this record. This is a unique within each type of List.
40
+ def id
41
+ @ole.list_id
42
+ end
43
+
44
+ # If an entity has a Name field but not a FullName field,
45
+ # use Name (which, by implication, is the FullName)
46
+ def full_name
47
+ respond_to_ole?("FullName") ?
48
+ @ole.full_name :
49
+ @ole.name
50
+ end
51
+
52
+ # Delete this List record.
53
+ def delete
54
+ req = QBFC::Request.new(@sess, "ListDel")
55
+ req.list_del_type = QBFC_CONST::const_get("Ldt#{qb_name}")
56
+ req.list_id = id
57
+ req.submit
58
+ return true
59
+ end
60
+
61
+ # Display the Transaction add (for new records) or edit dialog box
62
+ def display
63
+ if new_record?
64
+ req = QBFC::Request.new(@sess, "ListDisplayAdd")
65
+ req.list_display_add_type = QBFC_CONST::const_get("Ldat#{qb_name}")
66
+ else
67
+ req = QBFC::Request.new(@sess, "ListDisplayMod")
68
+ req.list_display_mod_type = QBFC_CONST::const_get("Ldmt#{qb_name}")
69
+ req.list_id = id
70
+ end
71
+ req.submit
72
+ return true
73
+ end
74
+ end
75
+ end
76
+
77
+ # Require subclass files
78
+ %w{ entity item terms }.each do |file|
79
+ require 'qbfc/' + file
80
+ end
81
+
82
+ Dir.new(File.dirname(__FILE__) + '/lists').each do |file|
83
+ require('qbfc/lists/' + File.basename(file)) if File.extname(file) == ".rb"
84
+ end
@@ -0,0 +1,24 @@
1
+ module QBFC
2
+ class Account < List
3
+ include Modifiable
4
+
5
+ class << self
6
+
7
+ # Adds a SpecialAccount, per SDK documentation:
8
+ # "An account normally created automatically as needed within the
9
+ # QuickBooks UI, or in the SDK via the SpecialAccountAdd request."
10
+ #
11
+ # +account_type+ should be a constent, for example:
12
+ #
13
+ # Account.add_special(sess, QBFC_CONST::SatAccountsPayable)
14
+ #
15
+ # See SDK docs for SpecialAccountAdd for more details.
16
+ def add_special(sess, account_type)
17
+ rq = QBFC::Request.new(sess, "SpecialAccountAdd")
18
+ rq.special_account_type = account_type
19
+
20
+ new(sess, rq.response)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module QBFC
2
+
3
+ # Generated List types that will inherit directly from QBFC::List
4
+ LIST_TYPES = %w{ BillingRate CustomerMsg CustomerType JobType PaymentMethod
5
+ PayrollItemNonWage PayrollItemWage PriceLevel SalesRep SalesTaxCode
6
+ ShipMethod ToDo Vehicle VendorType}
7
+
8
+ # Generated List types that do accept Mod Requests
9
+ # (most direct subclasses of List do not)
10
+ LIST_MOD_TYPES = %w{ PriceLevel SalesRep Vehicle }
11
+
12
+ # Generate List subclasses
13
+ generate(LIST_TYPES, List, {Modifiable => LIST_MOD_TYPES})
14
+
15
+ end
@@ -0,0 +1,25 @@
1
+ module QBFC
2
+ # QBClass objects represent QuickBooks SDK's <tt>Class</tt> objects.
3
+ # As naming this Class <tt>Class</tt> would be impractical, it is
4
+ # instead called QBClass. It is otherwise similar to the other List
5
+ # classes.
6
+ #
7
+ # From QBFC6 SDK docs:
8
+ #
9
+ # Classes can be used to separate transactions into meaningful categories.
10
+ # (For example, transactions could be classified according to department,
11
+ # business location, or type of work.) In QuickBooks, class tracking is
12
+ # off by default.
13
+ class QBClass < List
14
+ class << self
15
+
16
+ # The QuickBooks SDK class is called 'Class'.
17
+ # Calling this class QBClass avoids making ruby
18
+ # very angry; the qb_name method ensures that calls
19
+ # to QBFC use the correct name.
20
+ def qb_name
21
+ "Class"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ module QBFC
2
+ module Modifiable
3
+
4
+ # Extend initialize of including classes to set up a Mod Request
5
+ # if the record is existing.
6
+ def initialize(*args)
7
+ super
8
+
9
+ unless @new_record
10
+ setup_mod_request
11
+ end
12
+ end
13
+
14
+ # Setup a Mod Request for this object and attach it
15
+ # to the ole object.
16
+ def setup_mod_request
17
+ @setter = QBFC::Request.new(@sess, "#{self.qb_name}Mod")
18
+
19
+ if self.kind_of?(List)
20
+ @setter.list_id = id
21
+ else # Transaction
22
+ @setter.txn_id = id
23
+ end
24
+
25
+ @setter.edit_sequence = @ole.edit_sequence
26
+ @ole.setter = @setter.ole_object
27
+ end
28
+
29
+ private :setup_mod_request
30
+ end
31
+ end
@@ -0,0 +1,201 @@
1
+ module QBFC
2
+
3
+ # OLEWrapper is more or less the centerpiece of RubyQBFC. (Nearly) every
4
+ # WIN32OLE object accessed within the library is wrapped in this class, which
5
+ # is responsible for allowing Ruby-esque methods in place of the OLE methods.
6
+ #
7
+ # customer.full_name # => Customer.FullName.GetValue
8
+ # customer.full_name=(val) # => Customer.FullName.SetValue(val)
9
+ #
10
+ # It also creates referenced objects when accessed.
11
+ #
12
+ # check.payee # => Entity.find_by_list_id(check.PayeeEntityRef.ListID.GetValue)
13
+ # check.account # => Account.find_by_list_id(check.AccountRef.ListID.GetValue)
14
+ #
15
+ # When an OLE method called via OLEWrapper returns a WIN32OLE object, a new
16
+ # OLEWrapper object is created with the WIN32OLE object and returned.
17
+ #
18
+ # Now, the fun (and +really+ hackish) part. In many cases within the QBFC
19
+ # library, the wrapper is actually wrapping two WIN32OLE objects, the additional
20
+ # being a 'setter' object. This object is used when creating a ModRequest. In
21
+ # such cases, a method ending in '=' is always sent to both the primary and the
22
+ # setter objects. To facilitate this, traversing child ole_objects also
23
+ # traverses the child setter objects.
24
+ class OLEWrapper
25
+ attr_reader :ole_object
26
+ attr_accessor :setter
27
+
28
+ # Set up wrapped object, by passing a WIN32OLE object
29
+ # (or a String with the name of a WIN32OLE server)
30
+ # Optionally, pass a +setter+ WIN32OLE object.
31
+ def initialize(ole_object, setter = nil)
32
+ ole_object = WIN32OLE.new(ole_object) if ole_object.kind_of?(String)
33
+ @ole_object = ole_object
34
+ @setter = setter
35
+ end
36
+
37
+ # Return Array of ole_methods for request WIN32OLE object.
38
+ def ole_methods
39
+ @ole_object.ole_methods
40
+ end
41
+
42
+ # Does this OLE object respond to the given ole method?
43
+ def respond_to_ole?(symbol)
44
+ detect_ole_method?(@ole_object, symbol)
45
+ end
46
+
47
+ # Use [idx] syntax for objects that respond to <tt>GetAt(idx)</tt>
48
+ def [](idx)
49
+ if idx.kind_of? Integer
50
+ self.class.new(@ole_object.GetAt(idx))
51
+ else
52
+ @ole_object[idx]
53
+ end
54
+ end
55
+
56
+ # Called by #method_missing of other classes. Initiates the OLEWrapper#method_missing
57
+ # method which is responsible for the various method conversions.
58
+ # +sess+ argument is a QBFC::Session.
59
+ def qbfc_method_missing(sess, symbol, *params)
60
+ @sess = sess
61
+ method_missing(symbol, *params)
62
+ end
63
+
64
+ # If the method name is capitalized, send directly to ole_object; if
65
+ # a WIN32OLE is returned, wrap it.
66
+ # If the method name starts with a lower-case letter, send to +lower_method_missing+
67
+ # for conversion.
68
+ def method_missing(symbol, *params) #:nodoc:
69
+ if (('a'..'z') === symbol.to_s[0].chr)
70
+ lower_case_method_missing(symbol, *params)
71
+ else
72
+ resp = @ole_object.send(symbol, *params)
73
+ return( resp.kind_of?(WIN32OLE) ?
74
+ self.class.new(resp) :
75
+ resp )
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ # Decide which conversion method needs to handle this method and send.
82
+ def lower_case_method_missing(symbol, *params)
83
+ if '=' == symbol.to_s[-1].chr
84
+ set_value(symbol.to_s[0..-2], *params)
85
+ elsif symbol.to_s =~ /\A(\w+)_(full_name|id)\Z/ && ref_name($1)
86
+ get_ref_name_or_id(ref_name($1), $2)
87
+ elsif detect_ole_method?(@ole_object, ole_sym(symbol))
88
+ get_value(symbol, *params)
89
+ elsif detect_ole_method?(@ole_object, (s = symbol.to_s.singularize.camelize + "RetList"))
90
+ setup_array(s)
91
+ elsif detect_ole_method?(@ole_object, (s = "OR" + symbol.to_s.singularize.camelize + "RetList"))
92
+ setup_array(s, true)
93
+ elsif detect_ole_method?(@ole_object, (s = symbol.to_s.singularize.camelize + "List"))
94
+ setup_array(s)
95
+ elsif detect_ole_method?(@ole_object, (s = "OR" + symbol.to_s.singularize.camelize + "List"))
96
+ setup_array(s, true)
97
+ elsif ref_name(symbol)
98
+ create_ref(ref_name(symbol), *params)
99
+ elsif detect_ole_method?(@ole_object, symbol)
100
+ # Occassionally, QBFC uses a lower case method
101
+ @ole_object.send(symbol, *params)
102
+ else
103
+ raise NoMethodError, symbol.to_s
104
+ end
105
+ end
106
+
107
+ # Sets a value by calling OLEMethodName.SetValue(*params)
108
+ def set_value(ole_method_name, *params)
109
+ ole_method_name = ole_sym(ole_method_name)
110
+ obj = @ole_object.send(ole_method_name)
111
+
112
+ if detect_ole_method?(obj, "SetValue")
113
+ params[0] = params[0].strftime("%Y-%m-%d") if params[0].kind_of?(Date)
114
+ obj.SetValue(*params)
115
+ if @setter && detect_ole_method?(@setter, ole_method_name)
116
+ @setter.send(ole_method_name).SetValue(*params)
117
+ end
118
+ else
119
+ raise SetValueMissing, "SetValue is expected, but missing, for #{ole_method_name}"
120
+ end
121
+ end
122
+
123
+ # Gets a value by calling OLEMethodName.GetValue
124
+ def get_value(ole_method_name, *params)
125
+ ole_method_name = ole_sym(ole_method_name)
126
+ obj = @ole_object.send(ole_method_name, *params)
127
+ if detect_ole_method?(obj, "GetValue")
128
+ if ole_method_name =~ /date/i || ole_method_name.to_s =~ /time/i
129
+ Time.parse(obj.GetValue)
130
+ else
131
+ obj.GetValue
132
+ end
133
+ else
134
+ if obj.kind_of?(WIN32OLE)
135
+ if @setter && detect_ole_method?(@setter, ole_method_name)
136
+ self.class.new(obj, @setter.send(ole_method_name, *params))
137
+ else
138
+ self.class.new(obj)
139
+ end
140
+ else
141
+ obj
142
+ end
143
+ end
144
+ end
145
+
146
+ # Sets up an array to return if the return of OLEMethodName appears
147
+ # to be a list structure.
148
+ # <tt>is_OR_list</tt> indicates the list is an OR*RetList which
149
+ # is structured differently.
150
+ def setup_array(ole_method_name, is_OR_list = false)
151
+ list = @ole_object.send(ole_method_name)
152
+ ary = []
153
+ 0.upto(list.Count - 1) do |i|
154
+ if is_OR_list
155
+ ary << self.class.new(list.GetAt(i)).send(ole_method_name.match(/\AOR(.*)List\Z/)[1])
156
+ else
157
+ ary << self.class.new(list.GetAt(i))
158
+ end
159
+ end
160
+ return ary
161
+ end
162
+
163
+ def ref_name(symbol)
164
+ if detect_ole_method?(@ole_object, symbol.to_s.camelize + "Ref")
165
+ symbol.to_s.camelize + "Ref"
166
+ elsif detect_ole_method?(@ole_object, symbol.to_s.camelize + "EntityRef")
167
+ symbol.to_s.camelize + "EntityRef"
168
+ else
169
+ nil
170
+ end
171
+ end
172
+
173
+ # Creates a QBFC::Base inherited object if the return of
174
+ # OLEMethodName appears to be a reference to such an object.
175
+ def create_ref(ref_ole_name, *options)
176
+ ref_ole_object = @ole_object.send(ref_ole_name)
177
+ if ref_ole_object
178
+ ref_ole_name =~ /EntityRef/ ?
179
+ QBFC::Entity.find_by_id(@sess, ref_ole_object.ListID.GetValue(), *options) :
180
+ QBFC::const_get(ref_ole_name.gsub(/Ref/,"")).find_by_id(@sess, ref_ole_object.ListID.GetValue(), *options)
181
+ else
182
+ return nil
183
+ end
184
+ end
185
+
186
+ def get_ref_name_or_id(symbol, field)
187
+ field = (field == "id" ? "ListID" : "FullName")
188
+ @ole_object.send(symbol).send(field).GetValue()
189
+ end
190
+
191
+ # Check if the obj has an ole_method matching the symbol.
192
+ def detect_ole_method?(obj, symbol)
193
+ obj && obj.respond_to?(:ole_methods) && obj.ole_methods.detect{|m| m.to_s == symbol.to_s}
194
+ end
195
+
196
+ # Helper method to convert 'Ruby-ish' method name to WIN32OLE method name
197
+ def ole_sym(symbol)
198
+ symbol.to_s.camelize.gsub(/Id/, 'ID')
199
+ end
200
+ end
201
+ end