Class: Effective::QbRequest

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/effective/qb_request.rb

Constant Summary collapse

COMPLETED_STATES =

these are the states that signal a request is finished

['Finished', 'Error']
PROCESSING_STATES =
['Processing', 'CustomerQuery', 'CreateCustomer', 'OrderSync']

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.find_first_response_having_a_request_id(xml) ⇒ Object

searches the XML and returns the first element having a requestID attribute. Since the application does not bundle requests (yet), this should work.



68
69
70
71
# File 'app/models/effective/qb_request.rb', line 68

def self.find_first_response_having_a_request_id(xml)
  doc = Nokogiri::XML(xml)
  doc.xpath('//*[@requestID]')[0]
end

.find_using_response_qbxml(xml) ⇒ Object

Finds a QbRequest using response qb_xml. If the response could not be parsed, or if there was no corresponding record, nil will be returned.



51
52
53
54
55
56
# File 'app/models/effective/qb_request.rb', line 51

def self.find_using_response_qbxml(xml)
  return nil if xml.blank?
  element = Effective::QbRequest.find_first_response_having_a_request_id(xml)

  Effective::QbRequest.find_by_id(element.attr('requestID').to_i) if element
end

.new_requests_for_unsynced_items(before: nil, order_ids: nil) ⇒ Object

creates (does not persist) QbRequests for outstanding orders. The caller may choose to persist a request when that request starts communicating with QuickBooks



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'app/models/effective/qb_request.rb', line 33

def self.new_requests_for_unsynced_items(before: nil, order_ids: nil)
  finished_order_ids = Effective::QbRequest.where(state: 'Finished').pluck(:order_id)
  finished_orders = Effective::Order.purchased.includes(order_items: [:purchasable, :qb_order_item]).where.not(id: finished_order_ids)

  if before.present?
    raise('expected before to be a date') unless before.respond_to?(:strftime)
    finished_orders = finished_orders.where('purchased_at < ?', before)
  end

  if order_ids.present?
    finished_orders = finished_orders.where(id: order_ids)
  end

  finished_orders.map { |order| Effective::QbRequest.new(order: order) }
end

Instance Method Details

#consume_response_xml(xml) ⇒ Object

parses the response XML and processes it. returns true if the responseXML indicates success, false otherwise



75
76
77
78
# File 'app/models/effective/qb_request.rb', line 75

def consume_response_xml(xml)
  update!(response_qbxml: xml)
  handle_response_xml(xml)
end

#generate_request_xmlObject

generates the actual request XML that will be wrapped in a qbxml_request



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'app/models/effective/qb_request.rb', line 106

def generate_request_xml
  # safety checks to make sure we are linked in to the order
  raise 'Missing Order' unless order

  if order.order_items.any? { |order_item| order_item.qb_item_name.blank? }
    raise 'expected .qb_item_name() to be present on Effective::OrderItem'
  end

  case self.state
  when 'CustomerQuery'
    generate_customer_query_request_xml
  when 'OrderSync'
    generate_order_sync_request_xml
  when 'CreateCustomer'
    generate_create_customer_request_xml
  else
    raise "Unsupported state for generating request XML: #{state}"
  end
end

#handle_create_customer_response_xml(xml) ⇒ Object

This should be private too, but test needs it



144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'app/models/effective/qb_request.rb', line 144

def handle_create_customer_response_xml(xml)
  queryResponse = Nokogiri::XML(xml).xpath('//CustomerAddRs').first['statusCode']
  statusMessage = Nokogiri::XML(xml).xpath('//CustomerAddRs').first['statusMessage']

  if '0' == queryResponse
    # the customer was created
    log "Customer #{order.billing_name} created successfully"
    transition_state 'OrderSync'
  else
    raise "[Order ##{order.id}] Customer #{order.billing_name} could not be created in QuickBooks: #{statusMessage}"
  end

  true # indicate success
end

#handle_response_xml(xml) ⇒ Object

handle response xml



81
82
83
84
85
86
87
88
89
90
91
92
# File 'app/models/effective/qb_request.rb', line 81

def handle_response_xml(xml)
  case state
  when 'CustomerQuery'
    handle_customer_query_response_xml(xml)
  when 'CreateCustomer'
    handle_create_customer_response_xml(xml)
  when 'OrderSync'
    handle_order_sync_response_xml(xml)
  else
    raise "Request in state #{state} was not expecting a response from the server"
  end
end

#has_more_work?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'app/models/effective/qb_request.rb', line 58

def has_more_work?
  PROCESSING_STATES.include?(state)
end

#stateObject



62
63
64
# File 'app/models/effective/qb_request.rb', line 62

def state
  self[:state] || 'Processing'
end

#to_qb_xmlObject

outputs this request in qb_xml_format



95
96
97
98
99
100
101
102
103
# File 'app/models/effective/qb_request.rb', line 95

def to_qb_xml
  if state == 'Processing'
    # this is a dummy state -- we need to transition to the CustomerQuery state before any XML goes out.
    transition_state 'CustomerQuery'
  end

  xml = generate_request_xml
  wrap_qbxml_request(xml)
end

#transition_state(state) ⇒ Object

transitions the request state and also outputs a log statement



127
128
129
130
131
# File 'app/models/effective/qb_request.rb', line 127

def transition_state(state)
  old_state = self.state
  update!(state: state)
  log "Transitioned request state from [#{old_state}] to [#{state}]"
end

#transition_to_finishedObject



133
134
135
136
137
138
139
140
141
# File 'app/models/effective/qb_request.rb', line 133

def transition_to_finished
  # We create one QbOrderItem for each OrderItem here.
  order.order_items.each do |order_item|
    order_item.qb_item_name
    order_item.qb_order_item.save
  end

  transition_state('Finished')
end