Class: Quickbooks::Base

Inherits:
Model show all
Defined in:
lib/quickbooks/base.rb

Overview

Base is just base for ListItem and Transaction. It inherits from Model, just as Ref does.

Direct Known Subclasses

Deleted, ListItem, Transaction

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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

Returns a new instance of Base.



186
187
188
189
# File 'lib/quickbooks/base.rb', line 186

def initialize(*args)
  super # from Quickbooks::Model - sets the *args into attributes
  @new_record = true
end

Instance Attribute Details

#response_logObject

:nodoc:



80
81
82
# File 'lib/quickbooks/base.rb', line 80

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.



169
170
171
172
# File 'lib/quickbooks/base.rb', line 169

def all(filters={})
  filters.reverse_merge!(:active_status => 'All')
  [query(self, :query, filters)].flatten
end

.connectionObject

Returns the current Connection



107
108
109
# File 'lib/quickbooks/base.rb', line 107

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')

Raises:

  • (ArgumentError)


116
117
118
119
# File 'lib/quickbooks/base.rb', line 116

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.



181
182
183
# File 'lib/quickbooks/base.rb', line 181

def create(*args)
  new(*args).save
end

.establish_connection(*args) ⇒ Object

Establishes a connection to the Quickbooks RDS Server for all Model Classes



101
102
103
104
# File 'lib/quickbooks/base.rb', line 101

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.



175
176
177
178
# File 'lib/quickbooks/base.rb', line 175

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



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/quickbooks/base.rb', line 152

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.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/quickbooks/base.rb', line 126

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).



143
144
145
146
147
148
149
# File 'lib/quickbooks/base.rb', line 143

def request(*args)
  Qbxml::ResponseSet.new(
    self.connection.send_xml(
      Qbxml::Request.new(*args).to_xml
    )
  )
end

.use_adapter(adapter) ⇒ Object



94
95
96
97
98
# File 'lib/quickbooks/base.rb', line 94

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.



244
245
246
247
248
# File 'lib/quickbooks/base.rb', line 244

def ==(other)
  return false unless other.is_a?(self.class)
  return false if self.new_record? || other.new_record?
  super
end

#destroyObject

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.



239
240
241
# File 'lib/quickbooks/base.rb', line 239

def destroy
  self.class.query(self, :delete).success?
end

#inspectObject

:nodoc:



89
90
91
# File 'lib/quickbooks/base.rb', line 89

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).

Returns:

  • (Boolean)


192
193
194
# File 'lib/quickbooks/base.rb', line 192

def new_record?
  @new_record
end

#reloadObject

Reloads the record from Quickbooks, discarding any changes that have been made to it.



233
234
235
# File 'lib/quickbooks/base.rb', line 233

def reload
  self.class.query(self, :query)
end

#saveObject

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.



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/quickbooks/base.rb', line 201

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.

Returns:

  • (Boolean)


86
87
88
# File 'lib/quickbooks/base.rb', line 86

def success?
  @response_log.last.success?
end