Class: Quickbooks::Base
Overview
Base is just base for ListItem and Transaction. It inherits from Model, just as Ref does.
Some Qbxml specifications require certain finder-options to be placed inside a containing entity, such as:
...
<DeletedDateRangeFilter>
<FromDeletedDate>#{(Time.now - 5*60*60).xmlschema}</FromDeletedDate>
<ToDeletedDate>#{(Time.now - 3*60*60).xmlschema}</ToDeletedDate>
</DeletedDateRangeFilter>
...
The Quickbooks Models define aliases to these “inside” options. The equivalent to the above [partial] request:
Quickbooks::Deleted.all(:deleted_after => (Time.now - 5*60*60).xmlschema, :deleted_before => (Time.now - 3*60*60).xmlschema)
(Type-casting hasn’t made it in yet.)
Qbxml makes a special allowance for Deleted items. Much of the time, we’d rather access a model’s deleted items through that model class instead of through the deleted class. So Qbxml allows any class to ask for its deleted items through a call like this:
Quickbooks::Customer.deleted(:deleted_after => Time.now - 3*60*60)
Direct Known Subclasses
Instance Attribute Summary collapse
-
#response_log ⇒ Object
:nodoc:.
Class Method Summary collapse
-
.all(filters = {}) ⇒ Object
Queries Quickbooks for all of the objects of the current class’s type.
-
.connection ⇒ Object
Returns the current Connection.
-
.connection=(conn) ⇒ Object
Sets the current Connection.
-
.create(*args) ⇒ Object
Creates a new object of the current class’s type.
-
.deleted(filters = {}) ⇒ Object
Still in testing…
-
.establish_connection(*args) ⇒ Object
Establishes a connection to the Quickbooks RDS Server for all Model Classes.
-
.first(filters = {}) ⇒ Object
Queries Quickbooks for the first object of the current class’s type.
-
.instantiate(obj_or_attrs = {}, attrs = {}) ⇒ Object
Instantiate a new object with just attributes, or an existing object replacing the attributes.
-
.query(obj_or_args, *args) ⇒ Object
Generates a request by sending *args to Qbxml::Request.new, sends the request over the current connection, and interprets the response using Qbxml::ResponseSet.
-
.request(*args) ⇒ Object
Generates a request using Qbxml::Request, sends it, and returns a Qbxml::ResponseSet object containing the response(s).
- .use_adapter(adapter) ⇒ Object
Instance Method Summary collapse
-
#==(other) ⇒ Object
Usual comparison (super), but add in false if either is a new record.
-
#destroy ⇒ Object
Destroys a record in Quickbooks.
-
#initialize(*args) ⇒ Base
constructor
Generates a new object that can be saved into Quickbooks once the required attributes are set.
-
#inspect ⇒ Object
:nodoc:.
-
#new_record? ⇒ Boolean
Returns true if the object is a new object (that doesn’t represent an existing object in Quickbooks).
-
#reload ⇒ Object
Reloads the record from Quickbooks, discarding any changes that have been made to it.
-
#save ⇒ Object
Saves the attributes that have changed.
-
#success? ⇒ Boolean
Returns success (true/false) status of the last quickbooks communication called from this object.
Methods inherited from Model
#===, #attributes, #attributes=, camelized_valid_filters, #dirty?, #dirty_attributes, filter_aliases, filter_aliases=, inherited, #original_values, properties, read_only, read_write, #to_dirty_hash, #to_hash, valid_filters, valid_filters=
Constructor Details
#initialize(*args) ⇒ Base
Generates a new object that can be saved into Quickbooks once the required attributes are set.
213 214 215 216 |
# File 'lib/quickbooks/base.rb', line 213 def initialize(*args) super # from Quickbooks::Model - sets the *args into attributes @new_record = true end |
Instance Attribute Details
#response_log ⇒ Object
:nodoc:
100 101 102 |
# File 'lib/quickbooks/base.rb', line 100 def response_log @response_log end |
Class Method Details
.all(filters = {}) ⇒ Object
Queries Quickbooks for all of the objects of the current class’s type. For example, Quickbooks::Customer.all will return an array of Quickbooks::Customer objects representing all customers.
189 190 191 192 |
# File 'lib/quickbooks/base.rb', line 189 def all(filters={}) filters.reverse_merge!(:active_status => 'All') [query(self, :query, filters)].flatten end |
.connection ⇒ Object
Returns the current Connection
127 128 129 |
# File 'lib/quickbooks/base.rb', line 127 def connection @connection || (@@connection ||= self.establish_connection()) end |
.connection=(conn) ⇒ Object
Sets the current Connection.
This is normally not needed, but in the case that you may want to connect a separate connection to Quickbooks, you can use this method to explicitly set the connection in a class that inherits from Quickbooks::Base.
Quickbooks::Models::Base.connection = Quickbooks::Connection.new('My Test App', 'C:\\Some File.QBW', 'user', 'pass')
136 137 138 139 |
# File 'lib/quickbooks/base.rb', line 136 def connection=(conn) raise ArgumentError, "Cannot set connection to anything but a (*)Adapter::Connection object" unless conn.class.name =~ /Adapter::Connection$/ @connection = conn end |
.create(*args) ⇒ Object
Creates a new object of the current class’s type. For example, Quickbooks::Customer.create(:name => ‘Tommy’) will create a customer object with a Name of Tommy.
207 208 209 |
# File 'lib/quickbooks/base.rb', line 207 def create(*args) new(*args).save end |
.deleted(filters = {}) ⇒ Object
Still in testing… these should be equivalent:
Quickbooks::Customer.deleted(:deleted_before => Time.now) == Quickbooks::Deleted.all(:type => 'Customer', :deleted_before => Time.now)
202 203 204 |
# File 'lib/quickbooks/base.rb', line 202 def deleted(filters={}) query(self, :deleted, filters) end |
.establish_connection(*args) ⇒ Object
Establishes a connection to the Quickbooks RDS Server for all Model Classes
121 122 123 124 |
# File 'lib/quickbooks/base.rb', line 121 def establish_connection(*args) @@connection_adapter ||= use_adapter(:ole) @@connection = @@connection_adapter.new(*args) end |
.first(filters = {}) ⇒ Object
Queries Quickbooks for the first object of the current class’s type. For example, Quickbooks::Customer.first will return a Quickbooks::Customer object representing the first customer.
195 196 197 198 |
# File 'lib/quickbooks/base.rb', line 195 def first(filters={}) (filters.merge!(:max_returned => 1) unless filters.keys.include?(:list_id) || filters.keys.include?(:txn_id) || filters.keys.include?(:full_name)) if filters.is_a?(Hash) query(self, :query, filters) end |
.instantiate(obj_or_attrs = {}, attrs = {}) ⇒ Object
Instantiate a new object with just attributes, or an existing object replacing the attributes
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/quickbooks/base.rb', line 172 def instantiate(obj_or_attrs={},attrs={}) if obj_or_attrs.is_a?(Quickbooks::Base) obj = obj_or_attrs else obj = allocate attrs = obj_or_attrs end attrs.each do |key,value| if obj.respond_to?(key.to_s.underscore+'=') obj.send(key.to_s.underscore+'=', value) obj.original_values[key.to_s.underscore] = obj.instance_variable_get('@' + key.to_s.underscore).dup end end if attrs obj # Will be either a nice object, or a Qbxml::Error object. end |
.query(obj_or_args, *args) ⇒ Object
Generates a request by sending *args to Qbxml::Request.new, sends the request over the current connection, and interprets the response using Qbxml::ResponseSet. The response is then instantiated into an object or an array of objects.
This method is used mostly internally, but it is the yoke of this library - use it to perform custom requests.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/quickbooks/base.rb', line 146 def query(obj_or_args,*args) # If an object is sent, we need to reinstantiate the response into that object reinstantiate = if obj_or_args.is_a?(Quickbooks::Base) obj_or_args elsif obj_or_args.is_a?(Class) nil else args.unshift(obj_or_args) nil end objects = [] # This will hold and return the instantiated objects from the quickbooks response # The following is subject to bugginess, IF the response contains more than one object: it will instantiate only the last one. self.request(reinstantiate || self, *args).each { |response| objects << response.instantiate(reinstantiate) } # Does not instantiate if it's an error, but simply records response into response_log objects.length == 1 ? objects[0] : objects end |
.request(*args) ⇒ Object
Generates a request using Qbxml::Request, sends it, and returns a Qbxml::ResponseSet object containing the response(s).
163 164 165 166 167 168 169 |
# File 'lib/quickbooks/base.rb', line 163 def request(*args) Qbxml::ResponseSet.new( self.connection.send_xml( Qbxml::Request.new(*args).to_xml ) ) end |
.use_adapter(adapter) ⇒ Object
114 115 116 117 118 |
# File 'lib/quickbooks/base.rb', line 114 def use_adapter(adapter) # Should complain if the adapter doesn't exist. require "#{File.dirname(__FILE__)}/adapters/#{adapter.to_s}_adapter" @@connection_adapter = Object.module_eval("::Quickbooks::#{adapter.to_s.camelize}Adapter::Connection", __FILE__, __LINE__) end |
Instance Method Details
#==(other) ⇒ Object
Usual comparison (super), but add in false if either is a new record.
271 272 273 274 275 |
# File 'lib/quickbooks/base.rb', line 271 def ==(other) return false unless other.is_a?(self.class) return false if self.new_record? || other.new_record? super end |
#destroy ⇒ Object
Destroys a record in Quickbooks. Note that even though Quickbooks will destroy the record, the record’s ListID and DeletedTime can be accessed by doing a query for deleted objects of the appropriate type.
266 267 268 |
# File 'lib/quickbooks/base.rb', line 266 def destroy self.class.query(self, :delete).nil? end |
#inspect ⇒ Object
:nodoc:
109 110 111 |
# File 'lib/quickbooks/base.rb', line 109 def inspect #:nodoc: "#<#{self.class.name}:#{self.object_id} #{instance_variables.reject {|i| i.is_one_of?('@response_log', '@original_values')}.map {|i| "#{i}=#{instance_variable_get(i).inspect}"}.join(' ')}>" end |
#new_record? ⇒ Boolean
Returns true if the object is a new object (that doesn’t represent an existing object in Quickbooks).
219 220 221 |
# File 'lib/quickbooks/base.rb', line 219 def new_record? @new_record end |
#reload ⇒ Object
Reloads the record from Quickbooks, discarding any changes that have been made to it.
260 261 262 |
# File 'lib/quickbooks/base.rb', line 260 def reload self.class.query(self, :query) end |
#save ⇒ Object
Saves the attributes that have changed.
If the EditSequence is out of date but none of the changes conflict, the object will be saved to Quickbooks. But if there are conflicts, the updated values from Quickbooks will be written to the original_values, and false is returned. This way you can deal with the differences (conflicts), if you so desire, and simply call save again to commit your changes.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/quickbooks/base.rb', line 228 def save return false unless dirty? self.errors.clear # Clear out any errors: start with a clean slate! if new_record? self.class.query(self, :add) ret = !dirty? @new_record = false if ret else # Smart system that respects EditSequences and other people's changes. # 1) Try to save # 2) When we get a status of 3200, that means our EditSequence is not up to date # 3) Replace self's original_attributes with those just retrieved, and update the automatic attributes, like EditSequence and TimeModified # 4) Return false, return the object dirty but ready to save old_originals = original_values.dup # Save the old_originals so we can detect any attributes that changed since we last loaded the object ret = self.class.query(self, :mod).error? ? false : true # Saves if possible, if EditSequence is out of date, it will read the up-to-date object into original_values # If save failed (dirty?) because of old record (status 3200), but none of the fields conflict (attributes I've modified and are still different aren't the same attributes as any of the attributes someone else updated), then re-save! if dirty? && self.response_log.last.status == 3200 && (dirty_attributes.only(dirty_attributes(old_originals).keys).keys - self.class.read_only.stringify_values).length == (dirty_attributes(old_originals).keys - old_originals.diff(original_values).keys - self.class.read_only.stringify_values).length # 'Revert' fields I didn't modify to equal the values of the more up-to-date record just loaded. # Fields I didn't modify: dirty_attributes - dirty_attributes(old_originals).keys (dirty_attributes - dirty_attributes(old_originals).keys).each_key do |at| self.send(at + '=', original_values[at]) if respond_to?(at + '=') end ret = self.save end end # ret should be a false value if self has errors. Either way, ret gets the errors too. ret.errors << self.errors if self.error? # Should be true or false with an error attached. ret end |
#success? ⇒ Boolean
Returns success (true/false) status of the last quickbooks communication called from this object.
106 107 108 |
# File 'lib/quickbooks/base.rb', line 106 def success? @response_log.last.success? end |