Skip to content

Instantly share code, notes, and snippets.

@jefflunt
Created May 15, 2013 18:24
Show Gist options
  • Save jefflunt/5586134 to your computer and use it in GitHub Desktop.
Save jefflunt/5586134 to your computer and use it in GitHub Desktop.
Order model - after
class Order < ActiveRecord::Base
# This array includes the list of possible states that an Order can be in. These reflect the steps
# that an order goes through from placement to fulfilment.
#
# nil The initial state of any Order record
# checking_terminals Checking if the terminals involved in this order are online
# terminals_offline One of more of the printers/terminals for this order are offline
# charging Attempting to charge the customer's credit card via BrainTree
# failed_to_charge BrainTree returned an error
# printing The order has been sent to the printers/terminals
# complete The order appear to be complete, as far as we can tell
#
# ALL orders are saved in the database, even the ones that fail. This is for tracking reasons and
# to give us a better chance of troubleshooting a problem when an order doesn't reach the :completed
# state. ONLY completed orders should be shown on the Company dashboard. Orders in any state OTHER
# than completed are for internal troubleshooting.
#
# With the states noted above, there are three states in which a typical order can end:
# 1. terminals_offline
# 2. failed_to_charge
# 3. complete
#
# If an order ends in any OTHER state than one of these three, then that means something is wrong.
# To say it another way, EVERY order should either result in an error that we catch and store in
# the order's `failure_details` field, or the order should complete successfully. It only AFTER a
# record lands in one of these three states that we return a response to the user's mobile device
# with either a success or failure message.
#
# Finally, any order in a failure status wihout a `failure_details` value that has more information
# also indicates that someting went wrong and the details of the error were not captured.
VALID_STATES = [
nil,
'checking_terminals',
'terminals_offline',
'charging',
'failed_to_charge',
'printing',
'complete'
]
...
validates :state, :inclusion => { in: VALID_STATES, message: "state - %{value} - invalid. Must be one of the VALID_STATES." }
...
scope :complete, where("state = 'complete'")
scope :terminals_offline, where("state = 'terminals_offline'")
scope :failed_to_charge, where("state = 'failed_to_charge'")
...
def can_be_placed?
return false if order_items.empty?
return terminals_involved_including_items.all? {|t| t.online? }
end
def complete?
self.state == "complete"
end
def process_transaction
self.update_attribute(:state, 'checking_terminals')
unless can_be_placed?
self.update_attribute(:state, 'terminals_offline')
self.update_attribute(:failure_details, "One or more terminals for this order are offline. DETAILS: #{terminals_involved_including_items.inspect}")
self.errors[:base] << 'Unable to place order. Please try again later.'
return false
end
begin
self.update_attribute(:state, 'charging')
rescue Braintree::AuthenticationError => e
self.update_attributes({state: 'failed to charge', failure_details: 'Your Braintree keys failed to authenticate. Check your Braintree initializer file.'})
return false
rescue Exception => e
self.update_attributes({state: 'failed to charge', failure_details: "Unhandled exception: #{e.inspect}"})
return false
end
sale_hash = {
amount: format_total,
payment_method_token: credit_card.bt_token,
options: {
submit_for_settlement: true
}
}
Rails.logger.info("Attempting to charge order to BrainTree...")
sale_hash[:merchant_account_id] = company.merchant_account_id unless company.merchant_account_id.nil?
result = Braintree::Transaction.sale(sale_hash)
# Documentation on BrainTree results:
# https://www.braintreepayments.com/docs/ruby/transactions/result_handling
# https://www.braintreepayments.com/docs/ruby/general/result_objects#params
if result.success?
self.update_attribute(:state, 'printing')
self.update_attribute(:transaction_number, result.transaction.id)
send_to_printers
self.update_attribute(:state, 'complete')
else
self.update_attribute(:state, 'failed_to_charge')
self.update_attribute(:failure_details, result.message)
self.errors[:base] << 'Charging your credit card failed. Please check your card and try again.'
return false
end
end
def terminals_involved_including_items
Rails.logger.info 'Figuring out which printers to use to print this order...'
terminals = self.order_items.collect {|oi| oi.menu_item.terminal}.uniq
terminals.each do |t|
t.items = []
self.order_items.each{|oi| t.items << oi if oi.terminal == t}
end
terminals
end
def send_to_printers(reprint=false)
Rails.logger.info 'Order#send_to_printers...'
terminals_involved_including_items.each {|t| post_to_twisted(t.id, reprint) }
end
def post_to_twisted(terminal_number, reprint=false)
Rails.logger.info 'printing receipt to console'
Rails.logger.info 'Posting json to twisted...'
::RestClient.post(TWISTED_DOMAIN_ADDRESS, { 'id' => terminal_number.to_s, 'order' => print_receipt(reprint) })
end
...
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment