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.



64
65
66
67
# File 'app/models/effective/qb_request.rb', line 64

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.



47
48
49
50
51
52
# File 'app/models/effective/qb_request.rb', line 47

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) ⇒ 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
# File 'app/models/effective/qb_request.rb', line 33

def self.new_requests_for_unsynced_items(before: 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

  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



71
72
73
74
# File 'app/models/effective/qb_request.rb', line 71

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



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'app/models/effective/qb_request.rb', line 102

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



140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'app/models/effective/qb_request.rb', line 140

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



77
78
79
80
81
82
83
84
85
86
87
88
# File 'app/models/effective/qb_request.rb', line 77

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)


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

def has_more_work?
  PROCESSING_STATES.include?(state)
end

#stateObject



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

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

#to_qb_xmlObject

outputs this request in qb_xml_format



91
92
93
94
95
96
97
98
99
# File 'app/models/effective/qb_request.rb', line 91

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



123
124
125
126
127
# File 'app/models/effective/qb_request.rb', line 123

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

#transition_to_finishedObject



129
130
131
132
133
134
135
136
137
# File 'app/models/effective/qb_request.rb', line 129

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