active_fulfillment 2.1.9 → 3.0.0.pre2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +68 -0
 - data/lib/active_fulfillment.rb +5 -5
 - data/lib/active_fulfillment/base.rb +10 -0
 - data/lib/active_fulfillment/response.rb +26 -0
 - data/lib/active_fulfillment/service.rb +56 -0
 - data/lib/active_fulfillment/services.rb +5 -0
 - data/lib/active_fulfillment/services/amazon_mws.rb +473 -0
 - data/lib/active_fulfillment/services/james_and_james.rb +122 -0
 - data/lib/active_fulfillment/services/shipwire.rb +266 -0
 - data/lib/active_fulfillment/services/shopify_api.rb +125 -0
 - data/lib/active_fulfillment/services/webgistix.rb +334 -0
 - data/lib/active_fulfillment/version.rb +4 -0
 - data/test/remote/amazon_mws_test.rb +20 -17
 - data/test/remote/james_and_james_test.rb +77 -0
 - data/test/remote/shipwire_test.rb +25 -25
 - data/test/remote/webgistix_test.rb +21 -21
 - data/test/test_helper.rb +27 -52
 - data/test/unit/base_test.rb +4 -4
 - data/test/unit/services/amazon_mws_test.rb +56 -26
 - data/test/unit/services/james_and_james_test.rb +90 -0
 - data/test/unit/services/shipwire_test.rb +18 -18
 - data/test/unit/services/shopify_api_test.rb +7 -20
 - data/test/unit/services/webgistix_test.rb +35 -35
 - metadata +32 -114
 - data/CHANGELOG +0 -62
 - data/lib/active_fulfillment/fulfillment/base.rb +0 -12
 - data/lib/active_fulfillment/fulfillment/response.rb +0 -28
 - data/lib/active_fulfillment/fulfillment/service.rb +0 -58
 - data/lib/active_fulfillment/fulfillment/services.rb +0 -5
 - data/lib/active_fulfillment/fulfillment/services/amazon.rb +0 -389
 - data/lib/active_fulfillment/fulfillment/services/amazon_mws.rb +0 -454
 - data/lib/active_fulfillment/fulfillment/services/shipwire.rb +0 -268
 - data/lib/active_fulfillment/fulfillment/services/shopify_api.rb +0 -125
 - data/lib/active_fulfillment/fulfillment/services/webgistix.rb +0 -338
 - data/lib/active_fulfillment/fulfillment/version.rb +0 -6
 - data/test/fixtures.yml +0 -16
 - data/test/fixtures/xml/amazon/inventory_get_response.xml +0 -17
 - data/test/fixtures/xml/amazon/inventory_list_response.xml +0 -29
 - data/test/fixtures/xml/amazon/inventory_list_response_with_next_1.xml +0 -30
 - data/test/fixtures/xml/amazon/inventory_list_response_with_next_2.xml +0 -29
 - data/test/fixtures/xml/amazon/tracking_response_1.xml +0 -56
 - data/test/fixtures/xml/amazon/tracking_response_2.xml +0 -38
 - data/test/fixtures/xml/amazon/tracking_response_error.xml +0 -13
 - data/test/fixtures/xml/amazon/tracking_response_not_found.xml +0 -13
 - data/test/fixtures/xml/amazon_mws/fulfillment_get_fulfillment_order.xml +0 -114
 - data/test/fixtures/xml/amazon_mws/fulfillment_get_fulfillment_order_2.xml +0 -90
 - data/test/fixtures/xml/amazon_mws/fulfillment_get_fullfillment_order_with_multiple_tracking_numbers.xml +0 -121
 - data/test/fixtures/xml/amazon_mws/fulfillment_list_all_fulfillment_orders.xml +0 -70
 - data/test/fixtures/xml/amazon_mws/inventory_list_inventory_item_supply.xml +0 -32
 - data/test/fixtures/xml/amazon_mws/inventory_list_inventory_supply.xml +0 -75
 - data/test/fixtures/xml/amazon_mws/inventory_list_inventory_supply_by_next_token.xml +0 -38
 - data/test/fixtures/xml/amazon_mws/tracking_response_error.xml +0 -9
 - data/test/fixtures/xml/amazon_mws/tracking_response_not_found.xml +0 -9
 - data/test/fixtures/xml/shipwire/fulfillment_failure_response.xml +0 -7
 - data/test/fixtures/xml/shipwire/invalid_login_response.xml +0 -7
 - data/test/fixtures/xml/shipwire/inventory_get_response.xml +0 -44
 - data/test/fixtures/xml/shipwire/successful_empty_tracking_response.xml +0 -8
 - data/test/fixtures/xml/shipwire/successful_live_tracking_response.xml +0 -53
 - data/test/fixtures/xml/shipwire/successful_tracking_response.xml +0 -16
 - data/test/fixtures/xml/shipwire/successful_tracking_response_with_tracking_urls.xml +0 -31
 - data/test/fixtures/xml/webgistix/multiple_tracking_response.xml +0 -21
 - data/test/fixtures/xml/webgistix/tracking_response.xml +0 -14
 - data/test/remote/amazon_test.rb +0 -124
 - data/test/unit/services/amazon_test.rb +0 -271
 
| 
         @@ -1,268 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'cgi'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            module ActiveMerchant
         
     | 
| 
       4 
     | 
    
         
            -
              module Fulfillment
         
     | 
| 
       5 
     | 
    
         
            -
                class ShipwireService < Service
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
                  SERVICE_URLS = { :fulfillment  => 'https://api.shipwire.com/exec/FulfillmentServices.php',
         
     | 
| 
       8 
     | 
    
         
            -
                                   :inventory    => 'https://api.shipwire.com/exec/InventoryServices.php',
         
     | 
| 
       9 
     | 
    
         
            -
                                   :tracking     => 'https://api.shipwire.com/exec/TrackingServices.php'
         
     | 
| 
       10 
     | 
    
         
            -
                                 }
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
                  SCHEMA_URLS = { :fulfillment => 'http://www.shipwire.com/exec/download/OrderList.dtd',
         
     | 
| 
       13 
     | 
    
         
            -
                                  :inventory   => 'http://www.shipwire.com/exec/download/InventoryUpdate.dtd',
         
     | 
| 
       14 
     | 
    
         
            -
                                  :tracking    => 'http://www.shipwire.com/exec/download/TrackingUpdate.dtd'
         
     | 
| 
       15 
     | 
    
         
            -
                                }
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                  POST_VARS = { :fulfillment => 'OrderListXML',
         
     | 
| 
       18 
     | 
    
         
            -
                                :inventory   => 'InventoryUpdateXML',
         
     | 
| 
       19 
     | 
    
         
            -
                                :tracking    => 'TrackingUpdateXML'
         
     | 
| 
       20 
     | 
    
         
            -
                              }
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                  WAREHOUSES = { 'CHI' => 'Chicago',
         
     | 
| 
       23 
     | 
    
         
            -
                                 'LAX' => 'Los Angeles',
         
     | 
| 
       24 
     | 
    
         
            -
                                 'REN' => 'Reno',
         
     | 
| 
       25 
     | 
    
         
            -
                                 'VAN' => 'Vancouver',
         
     | 
| 
       26 
     | 
    
         
            -
                                 'TOR' => 'Toronto',
         
     | 
| 
       27 
     | 
    
         
            -
                                 'UK'  => 'United Kingdom'
         
     | 
| 
       28 
     | 
    
         
            -
                               }
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
                  INVALID_LOGIN = /(Error with Valid Username\/EmailAddress and Password Required)|(Could not verify Username\/EmailAddress and Password combination)/
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                  class_attribute :affiliate_id
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
                  # The first is the label, and the last is the code
         
     | 
| 
       35 
     | 
    
         
            -
                  def self.shipping_methods
         
     | 
| 
       36 
     | 
    
         
            -
                    [ ['1 Day Service',   '1D'],
         
     | 
| 
       37 
     | 
    
         
            -
                      ['2 Day Service',   '2D'],
         
     | 
| 
       38 
     | 
    
         
            -
                      ['Ground Service',  'GD'],
         
     | 
| 
       39 
     | 
    
         
            -
                      ['Freight Service', 'FT'],
         
     | 
| 
       40 
     | 
    
         
            -
                      ['International', 'INTL']
         
     | 
| 
       41 
     | 
    
         
            -
                    ].inject(ActiveSupport::OrderedHash.new){|h, (k,v)| h[k] = v; h}
         
     | 
| 
       42 
     | 
    
         
            -
                  end
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                  # Pass in the login and password for the shipwire account.
         
     | 
| 
       45 
     | 
    
         
            -
                  # Optionally pass in the :test => true to force test mode
         
     | 
| 
       46 
     | 
    
         
            -
                  def initialize(options = {})
         
     | 
| 
       47 
     | 
    
         
            -
                    requires!(options, :login, :password)
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                    super
         
     | 
| 
       50 
     | 
    
         
            -
                  end
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
                  def fulfill(order_id, shipping_address, line_items, options = {})
         
     | 
| 
       53 
     | 
    
         
            -
                    commit :fulfillment, build_fulfillment_request(order_id, shipping_address, line_items, options)
         
     | 
| 
       54 
     | 
    
         
            -
                  end
         
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
                  def fetch_stock_levels(options = {})
         
     | 
| 
       57 
     | 
    
         
            -
                    commit :inventory, build_inventory_request(options)
         
     | 
| 
       58 
     | 
    
         
            -
                  end
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                  def fetch_tracking_data(order_ids, options = {})
         
     | 
| 
       61 
     | 
    
         
            -
                    commit :tracking, build_tracking_request(order_ids)
         
     | 
| 
       62 
     | 
    
         
            -
                  end
         
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                  def valid_credentials?
         
     | 
| 
       65 
     | 
    
         
            -
                    response = fetch_tracking_numbers([])
         
     | 
| 
       66 
     | 
    
         
            -
                    response.message !~ INVALID_LOGIN
         
     | 
| 
       67 
     | 
    
         
            -
                  end
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                  def test_mode?
         
     | 
| 
       70 
     | 
    
         
            -
                    true
         
     | 
| 
       71 
     | 
    
         
            -
                  end
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
                  def include_pending_stock?
         
     | 
| 
       74 
     | 
    
         
            -
                    @options[:include_pending_stock]
         
     | 
| 
       75 
     | 
    
         
            -
                  end
         
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
                  def include_empty_stock?
         
     | 
| 
       78 
     | 
    
         
            -
                    @options[:include_empty_stock]
         
     | 
| 
       79 
     | 
    
         
            -
                  end
         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
                  private
         
     | 
| 
       82 
     | 
    
         
            -
                  def build_fulfillment_request(order_id, shipping_address, line_items, options)
         
     | 
| 
       83 
     | 
    
         
            -
                    xml = Builder::XmlMarkup.new :indent => 2
         
     | 
| 
       84 
     | 
    
         
            -
                    xml.instruct!
         
     | 
| 
       85 
     | 
    
         
            -
                    xml.declare! :DOCTYPE, :OrderList, :SYSTEM, SCHEMA_URLS[:fulfillment]
         
     | 
| 
       86 
     | 
    
         
            -
                    xml.tag! 'OrderList' do
         
     | 
| 
       87 
     | 
    
         
            -
                      add_credentials(xml)
         
     | 
| 
       88 
     | 
    
         
            -
                      xml.tag! 'Referer', 'Active Fulfillment'
         
     | 
| 
       89 
     | 
    
         
            -
                      add_order(xml, order_id, shipping_address, line_items, options)
         
     | 
| 
       90 
     | 
    
         
            -
                    end
         
     | 
| 
       91 
     | 
    
         
            -
                    xml.target!
         
     | 
| 
       92 
     | 
    
         
            -
                  end
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
                  def build_inventory_request(options)
         
     | 
| 
       95 
     | 
    
         
            -
                    xml = Builder::XmlMarkup.new :indent => 2
         
     | 
| 
       96 
     | 
    
         
            -
                    xml.instruct!
         
     | 
| 
       97 
     | 
    
         
            -
                    xml.declare! :DOCTYPE, :InventoryStatus, :SYSTEM, SCHEMA_URLS[:inventory]
         
     | 
| 
       98 
     | 
    
         
            -
                    xml.tag! 'InventoryUpdate' do
         
     | 
| 
       99 
     | 
    
         
            -
                      add_credentials(xml)
         
     | 
| 
       100 
     | 
    
         
            -
                      xml.tag! 'Warehouse', WAREHOUSES[options[:warehouse]]
         
     | 
| 
       101 
     | 
    
         
            -
                      xml.tag! 'ProductCode', options[:sku]
         
     | 
| 
       102 
     | 
    
         
            -
                      xml.tag! 'IncludeEmpty' if include_empty_stock?
         
     | 
| 
       103 
     | 
    
         
            -
                    end
         
     | 
| 
       104 
     | 
    
         
            -
                  end
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
                  def build_tracking_request(order_ids)
         
     | 
| 
       107 
     | 
    
         
            -
                    xml = Builder::XmlMarkup.new
         
     | 
| 
       108 
     | 
    
         
            -
                    xml.instruct!
         
     | 
| 
       109 
     | 
    
         
            -
                    xml.declare! :DOCTYPE, :InventoryStatus, :SYSTEM, SCHEMA_URLS[:inventory]
         
     | 
| 
       110 
     | 
    
         
            -
                    xml.tag! 'TrackingUpdate' do
         
     | 
| 
       111 
     | 
    
         
            -
                      add_credentials(xml)
         
     | 
| 
       112 
     | 
    
         
            -
                      xml.tag! 'Server', test? ? 'Test' : 'Production'
         
     | 
| 
       113 
     | 
    
         
            -
                      order_ids.each do |o_id|
         
     | 
| 
       114 
     | 
    
         
            -
                        xml.tag! 'OrderNo', o_id
         
     | 
| 
       115 
     | 
    
         
            -
                      end
         
     | 
| 
       116 
     | 
    
         
            -
                    end
         
     | 
| 
       117 
     | 
    
         
            -
                  end
         
     | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
       119 
     | 
    
         
            -
                  def add_credentials(xml)
         
     | 
| 
       120 
     | 
    
         
            -
                    xml.tag! 'EmailAddress', @options[:login]
         
     | 
| 
       121 
     | 
    
         
            -
                    xml.tag! 'Password', @options[:password]
         
     | 
| 
       122 
     | 
    
         
            -
                    xml.tag! 'Server', test? ? 'Test' : 'Production'
         
     | 
| 
       123 
     | 
    
         
            -
                    xml.tag! 'AffiliateId', affiliate_id if affiliate_id.present?
         
     | 
| 
       124 
     | 
    
         
            -
                  end
         
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
                  def add_order(xml, order_id, shipping_address, line_items, options)
         
     | 
| 
       127 
     | 
    
         
            -
                    xml.tag! 'Order', :id => order_id do
         
     | 
| 
       128 
     | 
    
         
            -
                      xml.tag! 'Warehouse', options[:warehouse] || '00'
         
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
                      add_address(xml, shipping_address, options)
         
     | 
| 
       131 
     | 
    
         
            -
                      xml.tag! 'Shipping', options[:shipping_method] unless options[:shipping_method].blank?
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
                      Array(line_items).each_with_index do |line_item, index|
         
     | 
| 
       134 
     | 
    
         
            -
                        add_item(xml, line_item, index)
         
     | 
| 
       135 
     | 
    
         
            -
                      end
         
     | 
| 
       136 
     | 
    
         
            -
                      xml.tag! 'Note' do
         
     | 
| 
       137 
     | 
    
         
            -
                        xml.cdata! options[:note] unless options[:note].blank?
         
     | 
| 
       138 
     | 
    
         
            -
                      end
         
     | 
| 
       139 
     | 
    
         
            -
                    end
         
     | 
| 
       140 
     | 
    
         
            -
                  end
         
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
                  def add_address(xml, address, options)
         
     | 
| 
       143 
     | 
    
         
            -
                    xml.tag! 'AddressInfo', :type => 'Ship' do
         
     | 
| 
       144 
     | 
    
         
            -
                      xml.tag! 'Name' do
         
     | 
| 
       145 
     | 
    
         
            -
                        xml.tag! 'Full', address[:name]
         
     | 
| 
       146 
     | 
    
         
            -
                      end
         
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
                      xml.tag! 'Address1', address[:address1]
         
     | 
| 
       149 
     | 
    
         
            -
                      xml.tag! 'Address2', address[:address2]
         
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
                      xml.tag! 'Company', address[:company]
         
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                      xml.tag! 'City', address[:city]
         
     | 
| 
       154 
     | 
    
         
            -
                      xml.tag! 'State', address[:state] unless address[:state].blank?
         
     | 
| 
       155 
     | 
    
         
            -
                      xml.tag! 'Country', address[:country]
         
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
       157 
     | 
    
         
            -
                      xml.tag! 'Zip', address[:zip]
         
     | 
| 
       158 
     | 
    
         
            -
                      xml.tag! 'Phone', address[:phone] unless address[:phone].blank?
         
     | 
| 
       159 
     | 
    
         
            -
                      xml.tag! 'Email', options[:email] unless options[:email].blank?
         
     | 
| 
       160 
     | 
    
         
            -
                    end
         
     | 
| 
       161 
     | 
    
         
            -
                  end
         
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
                  # Code is limited to 12 characters
         
     | 
| 
       164 
     | 
    
         
            -
                  def add_item(xml, item, index)
         
     | 
| 
       165 
     | 
    
         
            -
                    xml.tag! 'Item', :num => index do
         
     | 
| 
       166 
     | 
    
         
            -
                      xml.tag! 'Code', item[:sku]
         
     | 
| 
       167 
     | 
    
         
            -
                      xml.tag! 'Quantity', item[:quantity]
         
     | 
| 
       168 
     | 
    
         
            -
                    end
         
     | 
| 
       169 
     | 
    
         
            -
                  end
         
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
       171 
     | 
    
         
            -
                  def commit(action, request)
         
     | 
| 
       172 
     | 
    
         
            -
                    data = ssl_post(SERVICE_URLS[action], "#{POST_VARS[action]}=#{CGI.escape(request)}")
         
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
     | 
    
         
            -
                    response = parse_response(action, data)
         
     | 
| 
       175 
     | 
    
         
            -
                    Response.new(response[:success], response[:message], response, :test => test?)
         
     | 
| 
       176 
     | 
    
         
            -
                  end
         
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
                  def parse_response(action, data)
         
     | 
| 
       179 
     | 
    
         
            -
                    case action
         
     | 
| 
       180 
     | 
    
         
            -
                    when :fulfillment
         
     | 
| 
       181 
     | 
    
         
            -
                      parse_fulfillment_response(data)
         
     | 
| 
       182 
     | 
    
         
            -
                    when :inventory
         
     | 
| 
       183 
     | 
    
         
            -
                      parse_inventory_response(data)
         
     | 
| 
       184 
     | 
    
         
            -
                    when :tracking
         
     | 
| 
       185 
     | 
    
         
            -
                      parse_tracking_response(data)
         
     | 
| 
       186 
     | 
    
         
            -
                    else
         
     | 
| 
       187 
     | 
    
         
            -
                      raise ArgumentError, "Unknown action #{action}"
         
     | 
| 
       188 
     | 
    
         
            -
                    end
         
     | 
| 
       189 
     | 
    
         
            -
                  end
         
     | 
| 
       190 
     | 
    
         
            -
             
     | 
| 
       191 
     | 
    
         
            -
                  def parse_fulfillment_response(xml)
         
     | 
| 
       192 
     | 
    
         
            -
                    response = {}
         
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
                    document = REXML::Document.new(xml)
         
     | 
| 
       195 
     | 
    
         
            -
                    document.root.elements.each do |node|
         
     | 
| 
       196 
     | 
    
         
            -
                      response[node.name.underscore.to_sym] = text_content(node)
         
     | 
| 
       197 
     | 
    
         
            -
                    end
         
     | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
       199 
     | 
    
         
            -
                    response[:success] = response[:status] == '0'
         
     | 
| 
       200 
     | 
    
         
            -
                    response[:message] = response[:success] ? "Successfully submitted the order" : message_from(response[:error_message])
         
     | 
| 
       201 
     | 
    
         
            -
                    response
         
     | 
| 
       202 
     | 
    
         
            -
                  end
         
     | 
| 
       203 
     | 
    
         
            -
             
     | 
| 
       204 
     | 
    
         
            -
                  def parse_inventory_response(xml)
         
     | 
| 
       205 
     | 
    
         
            -
                    response = {}
         
     | 
| 
       206 
     | 
    
         
            -
                    response[:stock_levels] = {}
         
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
       208 
     | 
    
         
            -
                    document = REXML::Document.new(xml)
         
     | 
| 
       209 
     | 
    
         
            -
                    document.root.elements.each do |node|
         
     | 
| 
       210 
     | 
    
         
            -
                      if node.name == 'Product'
         
     | 
| 
       211 
     | 
    
         
            -
                        to_check = ['quantity']
         
     | 
| 
       212 
     | 
    
         
            -
                        to_check << 'pending' if include_pending_stock?
         
     | 
| 
       213 
     | 
    
         
            -
             
     | 
| 
       214 
     | 
    
         
            -
                        amount = to_check.sum { |a| node.attributes[a].to_i }
         
     | 
| 
       215 
     | 
    
         
            -
                        response[:stock_levels][node.attributes['code']] = amount
         
     | 
| 
       216 
     | 
    
         
            -
                      else
         
     | 
| 
       217 
     | 
    
         
            -
                        response[node.name.underscore.to_sym] = text_content(node)
         
     | 
| 
       218 
     | 
    
         
            -
                      end
         
     | 
| 
       219 
     | 
    
         
            -
                    end
         
     | 
| 
       220 
     | 
    
         
            -
             
     | 
| 
       221 
     | 
    
         
            -
                    response[:success] = test? ? response[:status] == 'Test' : response[:status] == '0'
         
     | 
| 
       222 
     | 
    
         
            -
                    response[:message] = response[:success] ? "Successfully received the stock levels" : message_from(response[:error_message])
         
     | 
| 
       223 
     | 
    
         
            -
             
     | 
| 
       224 
     | 
    
         
            -
                    response
         
     | 
| 
       225 
     | 
    
         
            -
                  end
         
     | 
| 
       226 
     | 
    
         
            -
             
     | 
| 
       227 
     | 
    
         
            -
                  def parse_tracking_response(xml)
         
     | 
| 
       228 
     | 
    
         
            -
                    response = {}
         
     | 
| 
       229 
     | 
    
         
            -
                    response[:tracking_numbers] = {}
         
     | 
| 
       230 
     | 
    
         
            -
                    response[:tracking_companies] = {}
         
     | 
| 
       231 
     | 
    
         
            -
                    response[:tracking_urls] = {}
         
     | 
| 
       232 
     | 
    
         
            -
             
     | 
| 
       233 
     | 
    
         
            -
                    document = REXML::Document.new(xml)
         
     | 
| 
       234 
     | 
    
         
            -
                    document.root.elements.each do |node|
         
     | 
| 
       235 
     | 
    
         
            -
                      if node.name == 'Order'
         
     | 
| 
       236 
     | 
    
         
            -
                        if node.attributes["shipped"] == "YES" && node.elements['TrackingNumber']
         
     | 
| 
       237 
     | 
    
         
            -
                          tracking_number = node.elements['TrackingNumber'].text.strip
         
     | 
| 
       238 
     | 
    
         
            -
                          response[:tracking_numbers][node.attributes['id']] = [tracking_number]
         
     | 
| 
       239 
     | 
    
         
            -
             
     | 
| 
       240 
     | 
    
         
            -
                          tracking_company = node.elements['TrackingNumber'].attributes['carrier']
         
     | 
| 
       241 
     | 
    
         
            -
                          response[:tracking_companies][node.attributes['id']] = [tracking_company.strip] if tracking_company
         
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
       243 
     | 
    
         
            -
                          tracking_url = node.elements['TrackingNumber'].attributes['href']
         
     | 
| 
       244 
     | 
    
         
            -
                          response[:tracking_urls][node.attributes['id']] = [tracking_url.strip] if tracking_url
         
     | 
| 
       245 
     | 
    
         
            -
                        end
         
     | 
| 
       246 
     | 
    
         
            -
                      else
         
     | 
| 
       247 
     | 
    
         
            -
                        response[node.name.underscore.to_sym] = text_content(node)
         
     | 
| 
       248 
     | 
    
         
            -
                      end
         
     | 
| 
       249 
     | 
    
         
            -
                    end
         
     | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
                    response[:success] = test? ? (response[:status] == '0' || response[:status] == 'Test') : response[:status] == '0'
         
     | 
| 
       252 
     | 
    
         
            -
                    response[:message] = response[:success] ? "Successfully received the tracking numbers" : message_from(response[:error_message])
         
     | 
| 
       253 
     | 
    
         
            -
                    response
         
     | 
| 
       254 
     | 
    
         
            -
                  end
         
     | 
| 
       255 
     | 
    
         
            -
             
     | 
| 
       256 
     | 
    
         
            -
                  def message_from(string)
         
     | 
| 
       257 
     | 
    
         
            -
                    return if string.blank?
         
     | 
| 
       258 
     | 
    
         
            -
                    string.gsub("\n", '').squeeze(" ")
         
     | 
| 
       259 
     | 
    
         
            -
                  end
         
     | 
| 
       260 
     | 
    
         
            -
             
     | 
| 
       261 
     | 
    
         
            -
                  def text_content(xml_node)
         
     | 
| 
       262 
     | 
    
         
            -
                    text = xml_node.text
         
     | 
| 
       263 
     | 
    
         
            -
                    text = xml_node.cdatas.join if text.blank?
         
     | 
| 
       264 
     | 
    
         
            -
                    text
         
     | 
| 
       265 
     | 
    
         
            -
                  end
         
     | 
| 
       266 
     | 
    
         
            -
                end
         
     | 
| 
       267 
     | 
    
         
            -
              end
         
     | 
| 
       268 
     | 
    
         
            -
            end
         
     | 
| 
         @@ -1,125 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module ActiveMerchant
         
     | 
| 
       2 
     | 
    
         
            -
              module Fulfillment
         
     | 
| 
       3 
     | 
    
         
            -
                class ShopifyAPIService < Service
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
                  OrderIdCutoffDate = Date.iso8601("2015-03-01")
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
                  RESCUABLE_CONNECTION_ERRORS = [
         
     | 
| 
       8 
     | 
    
         
            -
                    Net::ReadTimeout,
         
     | 
| 
       9 
     | 
    
         
            -
                    Net::OpenTimeout,
         
     | 
| 
       10 
     | 
    
         
            -
                    TimeoutError,
         
     | 
| 
       11 
     | 
    
         
            -
                    Errno::ETIMEDOUT,
         
     | 
| 
       12 
     | 
    
         
            -
                    Timeout::Error,
         
     | 
| 
       13 
     | 
    
         
            -
                    IOError,
         
     | 
| 
       14 
     | 
    
         
            -
                    EOFError,
         
     | 
| 
       15 
     | 
    
         
            -
                    SocketError,
         
     | 
| 
       16 
     | 
    
         
            -
                    Errno::ECONNRESET,
         
     | 
| 
       17 
     | 
    
         
            -
                    Errno::ECONNABORTED,
         
     | 
| 
       18 
     | 
    
         
            -
                    Errno::EPIPE,
         
     | 
| 
       19 
     | 
    
         
            -
                    Errno::ECONNREFUSED,
         
     | 
| 
       20 
     | 
    
         
            -
                    Errno::EAGAIN,
         
     | 
| 
       21 
     | 
    
         
            -
                    Errno::EHOSTUNREACH,
         
     | 
| 
       22 
     | 
    
         
            -
                    Errno::ENETUNREACH,
         
     | 
| 
       23 
     | 
    
         
            -
                    Resolv::ResolvError,
         
     | 
| 
       24 
     | 
    
         
            -
                    Net::HTTPBadResponse,
         
     | 
| 
       25 
     | 
    
         
            -
                    Net::HTTPHeaderSyntaxError,
         
     | 
| 
       26 
     | 
    
         
            -
                    Net::ProtocolError,
         
     | 
| 
       27 
     | 
    
         
            -
                    ActiveMerchant::ConnectionError,
         
     | 
| 
       28 
     | 
    
         
            -
                    ActiveMerchant::ResponseError,
         
     | 
| 
       29 
     | 
    
         
            -
                    ActiveMerchant::InvalidResponseError
         
     | 
| 
       30 
     | 
    
         
            -
                  ]
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                  def initialize(options = {})
         
     | 
| 
       33 
     | 
    
         
            -
                    @name = options[:name]
         
     | 
| 
       34 
     | 
    
         
            -
                    @callback_url = options[:callback_url]
         
     | 
| 
       35 
     | 
    
         
            -
                    @format = options[:format]
         
     | 
| 
       36 
     | 
    
         
            -
                  end
         
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                  def fulfill(order_id, shipping_address, line_items, options = {})
         
     | 
| 
       39 
     | 
    
         
            -
                    raise NotImplementedError.new("Shopify API Service must listen to fulfillment/create Webhooks")
         
     | 
| 
       40 
     | 
    
         
            -
                  end
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                  def fetch_stock_levels(options = {})
         
     | 
| 
       43 
     | 
    
         
            -
                    response = send_app_request('fetch_stock', options.delete(:headers), options)
         
     | 
| 
       44 
     | 
    
         
            -
                    if response
         
     | 
| 
       45 
     | 
    
         
            -
                      stock_levels = parse_response(response, 'StockLevels', 'Product', 'Sku', 'Quantity') { |p| p.to_i }
         
     | 
| 
       46 
     | 
    
         
            -
                      Response.new(true, "API stock levels", {:stock_levels => stock_levels})
         
     | 
| 
       47 
     | 
    
         
            -
                    else
         
     | 
| 
       48 
     | 
    
         
            -
                      Response.new(false, "Unable to fetch remote stock levels")
         
     | 
| 
       49 
     | 
    
         
            -
                    end
         
     | 
| 
       50 
     | 
    
         
            -
                  end
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
                  def fetch_tracking_data(order_numbers, options = {})
         
     | 
| 
       53 
     | 
    
         
            -
                    options.merge!({:order_ids => order_numbers, :order_names => order_numbers})
         
     | 
| 
       54 
     | 
    
         
            -
                    response = send_app_request('fetch_tracking_numbers', options.delete(:headers), options)
         
     | 
| 
       55 
     | 
    
         
            -
                    if response
         
     | 
| 
       56 
     | 
    
         
            -
                      tracking_numbers = parse_response(response, 'TrackingNumbers', 'Order', 'ID', 'Tracking') { |o| o }
         
     | 
| 
       57 
     | 
    
         
            -
                      Response.new(true, "API tracking_numbers", {:tracking_numbers => tracking_numbers,
         
     | 
| 
       58 
     | 
    
         
            -
                                                                  :tracking_companies => {},
         
     | 
| 
       59 
     | 
    
         
            -
                                                                  :tracking_urls => {}})
         
     | 
| 
       60 
     | 
    
         
            -
                    else
         
     | 
| 
       61 
     | 
    
         
            -
                      Response.new(false, "Unable to fetch remote tracking numbers #{order_numbers.inspect}")
         
     | 
| 
       62 
     | 
    
         
            -
                    end
         
     | 
| 
       63 
     | 
    
         
            -
                  end
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                  private
         
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
                  def request_uri(action, data)
         
     | 
| 
       68 
     | 
    
         
            -
                    URI.parse "#{@callback_url}/#{action}.#{@format}?#{data.to_param}"
         
     | 
| 
       69 
     | 
    
         
            -
                  end
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                  def send_app_request(action, headers, data)
         
     | 
| 
       72 
     | 
    
         
            -
                    uri = request_uri(action, data)
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
                    logger.info "[" + @name.upcase + " APP] Post #{uri}"
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
                    response = nil
         
     | 
| 
       77 
     | 
    
         
            -
                    realtime = Benchmark.realtime do
         
     | 
| 
       78 
     | 
    
         
            -
                      begin
         
     | 
| 
       79 
     | 
    
         
            -
                        Timeout.timeout(20.seconds) do
         
     | 
| 
       80 
     | 
    
         
            -
                          response = ssl_get(uri, headers)
         
     | 
| 
       81 
     | 
    
         
            -
                        end
         
     | 
| 
       82 
     | 
    
         
            -
                      rescue *(RESCUABLE_CONNECTION_ERRORS) => e
         
     | 
| 
       83 
     | 
    
         
            -
                        logger.warn "[#{self}] Error while contacting fulfillment service error =\"#{e.message}\""
         
     | 
| 
       84 
     | 
    
         
            -
                      end
         
     | 
| 
       85 
     | 
    
         
            -
                    end
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
                    line = "[" + @name.upcase + "APP] Response from #{uri} --> "
         
     | 
| 
       88 
     | 
    
         
            -
                    line << "#{response} #{"%.4fs" % realtime}"
         
     | 
| 
       89 
     | 
    
         
            -
                    logger.info line
         
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
                    response
         
     | 
| 
       92 
     | 
    
         
            -
                  end
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
                  def parse_response(response, root, type, key, value)
         
     | 
| 
       95 
     | 
    
         
            -
                    case @format
         
     | 
| 
       96 
     | 
    
         
            -
                    when 'json'
         
     | 
| 
       97 
     | 
    
         
            -
                      response_data = ActiveSupport::JSON.decode(response)
         
     | 
| 
       98 
     | 
    
         
            -
                      return {} unless response_data.is_a?(Hash)
         
     | 
| 
       99 
     | 
    
         
            -
                      response_data[root.underscore] || response_data
         
     | 
| 
       100 
     | 
    
         
            -
                    when 'xml'
         
     | 
| 
       101 
     | 
    
         
            -
                      response_data = {}
         
     | 
| 
       102 
     | 
    
         
            -
                      document = REXML::Document.new(response)
         
     | 
| 
       103 
     | 
    
         
            -
                      document.elements[root].each do |node|
         
     | 
| 
       104 
     | 
    
         
            -
                        if node.name == type
         
     | 
| 
       105 
     | 
    
         
            -
                          response_data[node.elements[key].text] = node.elements[value].text
         
     | 
| 
       106 
     | 
    
         
            -
                        end
         
     | 
| 
       107 
     | 
    
         
            -
                      end
         
     | 
| 
       108 
     | 
    
         
            -
                      response_data
         
     | 
| 
       109 
     | 
    
         
            -
                    end
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                  rescue ActiveSupport::JSON.parse_error, REXML::ParseException
         
     | 
| 
       112 
     | 
    
         
            -
                    {}
         
     | 
| 
       113 
     | 
    
         
            -
                  end
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                  def encode_payload(payload, root)
         
     | 
| 
       116 
     | 
    
         
            -
                    case @format
         
     | 
| 
       117 
     | 
    
         
            -
                    when 'json'
         
     | 
| 
       118 
     | 
    
         
            -
                      {root => payload}.to_json
         
     | 
| 
       119 
     | 
    
         
            -
                    when 'xml'
         
     | 
| 
       120 
     | 
    
         
            -
                      payload.to_xml(:root => root)
         
     | 
| 
       121 
     | 
    
         
            -
                    end
         
     | 
| 
       122 
     | 
    
         
            -
                  end
         
     | 
| 
       123 
     | 
    
         
            -
                end
         
     | 
| 
       124 
     | 
    
         
            -
              end
         
     | 
| 
       125 
     | 
    
         
            -
            end
         
     | 
| 
         @@ -1,338 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module ActiveMerchant
         
     | 
| 
       2 
     | 
    
         
            -
              module Fulfillment
         
     | 
| 
       3 
     | 
    
         
            -
                class WebgistixService < Service
         
     | 
| 
       4 
     | 
    
         
            -
                  SERVICE_URLS = {
         
     | 
| 
       5 
     | 
    
         
            -
                    :fulfillment => 'https://www.webgistix.com/XML/CreateOrder.asp',
         
     | 
| 
       6 
     | 
    
         
            -
                    :inventory   => 'https://www.webgistix.com/XML/GetInventory.asp',
         
     | 
| 
       7 
     | 
    
         
            -
                    :tracking    => 'https://www.webgistix.com/XML/GetTracking.asp'
         
     | 
| 
       8 
     | 
    
         
            -
                  }
         
     | 
| 
       9 
     | 
    
         
            -
                  TEST_URLS = SERVICE_URLS.merge({
         
     | 
| 
       10 
     | 
    
         
            -
                    :fulfillment => 'https://www.webgistix.com/XML/CreateOrderTest.asp'
         
     | 
| 
       11 
     | 
    
         
            -
                  })
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                  SUCCESS, DUPLICATE, FAILURE = 'True', 'Duplicate', 'False'
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                  SUCCESS_MESSAGE = 'Successfully submitted the order'
         
     | 
| 
       16 
     | 
    
         
            -
                  FAILURE_MESSAGE = 'Failed to submit the order'
         
     | 
| 
       17 
     | 
    
         
            -
                  DUPLICATE_MESSAGE = 'This order has already been successfully submitted'
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                  INVALID_LOGIN = 'Invalid Credentials'
         
     | 
| 
       20 
     | 
    
         
            -
                  NOT_SHIPPED = 'Not Shipped'
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                  TRACKING_COMPANIES = %w(UPS FedEx USPS)
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                  # If a request is detected as a duplicate only the original data will be
         
     | 
| 
       25 
     | 
    
         
            -
                  # used by Webgistix, and the subsequent responses will have a
         
     | 
| 
       26 
     | 
    
         
            -
                  # :duplicate parameter set in the params hash.
         
     | 
| 
       27 
     | 
    
         
            -
                  self.retry_safe = true
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
                  # The first is the label, and the last is the code
         
     | 
| 
       30 
     | 
    
         
            -
                  def self.shipping_methods
         
     | 
| 
       31 
     | 
    
         
            -
                    [
         
     | 
| 
       32 
     | 
    
         
            -
                      ["UPS Ground Shipping", "Ground"],
         
     | 
| 
       33 
     | 
    
         
            -
                      ["UPS Ground", "Ground"],
         
     | 
| 
       34 
     | 
    
         
            -
                      ["UPS Standard Shipping (Canada Only)", "Standard"],
         
     | 
| 
       35 
     | 
    
         
            -
                      ["UPS Standard Shipping (CA & MX Only)", "Standard"],
         
     | 
| 
       36 
     | 
    
         
            -
                      ["UPS 3-Business Day", "3-Day Select"],
         
     | 
| 
       37 
     | 
    
         
            -
                      ["UPS 2-Business Day", "2nd Day Air"],
         
     | 
| 
       38 
     | 
    
         
            -
                      ["UPS 2-Business Day AM", "2nd Day Air AM"],
         
     | 
| 
       39 
     | 
    
         
            -
                      ["UPS Next Day", "Next Day Air"],
         
     | 
| 
       40 
     | 
    
         
            -
                      ["UPS Next Day Saver", "Next Day Air Saver"],
         
     | 
| 
       41 
     | 
    
         
            -
                      ["UPS Next Day Early AM", "Next Day Air Early AM"],
         
     | 
| 
       42 
     | 
    
         
            -
                      ["UPS Worldwide Express (Next Day)", "Worldwide Express"],
         
     | 
| 
       43 
     | 
    
         
            -
                      ["UPS Worldwide Expedited (2nd Day)", "Worldwide Expedited"],
         
     | 
| 
       44 
     | 
    
         
            -
                      ["UPS Worldwide Express Saver", "Worldwide Express Saver"],
         
     | 
| 
       45 
     | 
    
         
            -
                      ["FedEx Priority Overnight", "FedEx Priority Overnight"],
         
     | 
| 
       46 
     | 
    
         
            -
                      ["FedEx Standard Overnight", "FedEx Standard Overnight"],
         
     | 
| 
       47 
     | 
    
         
            -
                      ["FedEx First Overnight", "FedEx First Overnight"],
         
     | 
| 
       48 
     | 
    
         
            -
                      ["FedEx 2nd Day", "FedEx 2nd Day"],
         
     | 
| 
       49 
     | 
    
         
            -
                      ["FedEx Express Saver", "FedEx Express Saver"],
         
     | 
| 
       50 
     | 
    
         
            -
                      ["FedEx International Priority", "FedEx International Priority"],
         
     | 
| 
       51 
     | 
    
         
            -
                      ["FedEx International Economy", "FedEx International Economy"],
         
     | 
| 
       52 
     | 
    
         
            -
                      ["FedEx International First", "FedEx International First"],
         
     | 
| 
       53 
     | 
    
         
            -
                      ["FedEx Ground", "FedEx Ground"],
         
     | 
| 
       54 
     | 
    
         
            -
                      ["USPS Priority Mail", "Priority Mail"],
         
     | 
| 
       55 
     | 
    
         
            -
                      ["USPS Priority Mail International", "Priority Mail International"],
         
     | 
| 
       56 
     | 
    
         
            -
                      ["USPS Priority Mail Small Flat Rate Box", "Priority Mail Small Flat Rate Box"],
         
     | 
| 
       57 
     | 
    
         
            -
                      ["USPS Priority Mail Medium Flat Rate Box", "Priority Mail Medium Flat Rate Box"],
         
     | 
| 
       58 
     | 
    
         
            -
                      ["USPS Priority Mail Large Flat Rate Box", "Priority Mail Large Flat Rate Box"],
         
     | 
| 
       59 
     | 
    
         
            -
                      ["USPS Priority Mail Flat Rate Envelope", "Priority Mail Flat Rate Envelope"],
         
     | 
| 
       60 
     | 
    
         
            -
                      ["USPS First Class Mail", "First Class"],
         
     | 
| 
       61 
     | 
    
         
            -
                      ["USPS First Class International", "First Class International"],
         
     | 
| 
       62 
     | 
    
         
            -
                      ["USPS Express Mail", "Express"],
         
     | 
| 
       63 
     | 
    
         
            -
                      ["USPS Express Mail International", "Express Mail International"],
         
     | 
| 
       64 
     | 
    
         
            -
                      ["USPS Parcel Post", "Parcel"],
         
     | 
| 
       65 
     | 
    
         
            -
                      ["USPS Media Mail", "Media Mail"]
         
     | 
| 
       66 
     | 
    
         
            -
                    ].inject(ActiveSupport::OrderedHash.new){|h, (k,v)| h[k] = v; h}
         
     | 
| 
       67 
     | 
    
         
            -
                  end
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                  # Pass in the login and password for the shipwire account.
         
     | 
| 
       70 
     | 
    
         
            -
                  # Optionally pass in the :test => true to force test mode
         
     | 
| 
       71 
     | 
    
         
            -
                  def initialize(options = {})
         
     | 
| 
       72 
     | 
    
         
            -
                    requires!(options, :login, :password)
         
     | 
| 
       73 
     | 
    
         
            -
                    super
         
     | 
| 
       74 
     | 
    
         
            -
                  end
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
                  def fulfill(order_id, shipping_address, line_items, options = {})
         
     | 
| 
       77 
     | 
    
         
            -
                    requires!(options, :shipping_method)
         
     | 
| 
       78 
     | 
    
         
            -
                    commit :fulfillment, build_fulfillment_request(order_id, shipping_address, line_items, options)
         
     | 
| 
       79 
     | 
    
         
            -
                  end
         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
                  def fetch_stock_levels(options = {})
         
     | 
| 
       82 
     | 
    
         
            -
                    commit :inventory, build_inventory_request(options)
         
     | 
| 
       83 
     | 
    
         
            -
                  end
         
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
                  def fetch_tracking_data(order_ids, options = {})
         
     | 
| 
       86 
     | 
    
         
            -
                    commit :tracking, build_tracking_request(order_ids, options)
         
     | 
| 
       87 
     | 
    
         
            -
                  end
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                  def valid_credentials?
         
     | 
| 
       90 
     | 
    
         
            -
                    response = fulfill('', {}, [], :shipping_method => '')
         
     | 
| 
       91 
     | 
    
         
            -
                    response.message != INVALID_LOGIN
         
     | 
| 
       92 
     | 
    
         
            -
                  end
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
                  def test_mode?
         
     | 
| 
       95 
     | 
    
         
            -
                    true
         
     | 
| 
       96 
     | 
    
         
            -
                  end
         
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
                  private
         
     | 
| 
       99 
     | 
    
         
            -
                  #<?xml version="1.0"?>
         
     | 
| 
       100 
     | 
    
         
            -
                  # <OrderXML>
         
     | 
| 
       101 
     | 
    
         
            -
                  #   <Password>Webgistix</Password>
         
     | 
| 
       102 
     | 
    
         
            -
                  #   <CustomerID>3</CustomerID>
         
     | 
| 
       103 
     | 
    
         
            -
                  #   <Order>
         
     | 
| 
       104 
     | 
    
         
            -
                  #     <ReferenceNumber></ReferenceNumber>
         
     | 
| 
       105 
     | 
    
         
            -
                  #     <Company>Test Company</Company>
         
     | 
| 
       106 
     | 
    
         
            -
                  #     <Name>Joe Smith</Name>
         
     | 
| 
       107 
     | 
    
         
            -
                  #     <Address1>123 Main St.</Address1>
         
     | 
| 
       108 
     | 
    
         
            -
                  #     <Address2></Address2>
         
     | 
| 
       109 
     | 
    
         
            -
                  #     <Address3></Address3>
         
     | 
| 
       110 
     | 
    
         
            -
                  #     <City>Olean</City>
         
     | 
| 
       111 
     | 
    
         
            -
                  #     <State>NY</State>
         
     | 
| 
       112 
     | 
    
         
            -
                  #     <ZipCode>14760</ZipCode>
         
     | 
| 
       113 
     | 
    
         
            -
                  #     <Country>United States</Country>
         
     | 
| 
       114 
     | 
    
         
            -
                  #     <Email>info@webgistix.com</Email>
         
     | 
| 
       115 
     | 
    
         
            -
                  #     <Phone>1-123-456-7890</Phone>
         
     | 
| 
       116 
     | 
    
         
            -
                  #     <ShippingInstructions>Ground</ShippingInstructions>
         
     | 
| 
       117 
     | 
    
         
            -
                  #     <OrderComments>Test Order</OrderComments>
         
     | 
| 
       118 
     | 
    
         
            -
                  #     <Approve>0</Approve>
         
     | 
| 
       119 
     | 
    
         
            -
                  #     <Item>
         
     | 
| 
       120 
     | 
    
         
            -
                  #      <ItemID>testitem</ItemID>
         
     | 
| 
       121 
     | 
    
         
            -
                  #      <ItemQty>2</ItemQty>
         
     | 
| 
       122 
     | 
    
         
            -
                  #     </Item>
         
     | 
| 
       123 
     | 
    
         
            -
                  #   </Order>
         
     | 
| 
       124 
     | 
    
         
            -
                  # </OrderXML>
         
     | 
| 
       125 
     | 
    
         
            -
                  def build_fulfillment_request(order_id, shipping_address, line_items, options)
         
     | 
| 
       126 
     | 
    
         
            -
                    xml = Builder::XmlMarkup.new :indent => 2
         
     | 
| 
       127 
     | 
    
         
            -
                    xml.instruct!
         
     | 
| 
       128 
     | 
    
         
            -
                    xml.tag! 'OrderXML' do
         
     | 
| 
       129 
     | 
    
         
            -
                      add_credentials(xml)
         
     | 
| 
       130 
     | 
    
         
            -
                      add_order(xml, order_id, shipping_address, line_items, options)
         
     | 
| 
       131 
     | 
    
         
            -
                    end
         
     | 
| 
       132 
     | 
    
         
            -
                    xml.target!
         
     | 
| 
       133 
     | 
    
         
            -
                  end
         
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
                  #<?xml version="1.0"?>
         
     | 
| 
       136 
     | 
    
         
            -
                  # <InventoryXML>
         
     | 
| 
       137 
     | 
    
         
            -
                  #   <Password>Webgistix</Password>
         
     | 
| 
       138 
     | 
    
         
            -
                  #   <CustomerID>3</CustomerID>
         
     | 
| 
       139 
     | 
    
         
            -
                  # </InventoryXML>
         
     | 
| 
       140 
     | 
    
         
            -
                  def build_inventory_request(options)
         
     | 
| 
       141 
     | 
    
         
            -
                    xml = Builder::XmlMarkup.new :indent => 2
         
     | 
| 
       142 
     | 
    
         
            -
                    xml.instruct!
         
     | 
| 
       143 
     | 
    
         
            -
                    xml.tag! 'InventoryXML' do
         
     | 
| 
       144 
     | 
    
         
            -
                      add_credentials(xml)
         
     | 
| 
       145 
     | 
    
         
            -
                    end
         
     | 
| 
       146 
     | 
    
         
            -
                  end
         
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
                  #<?xml version="1.0"?>
         
     | 
| 
       149 
     | 
    
         
            -
                  # <TrackingXML>
         
     | 
| 
       150 
     | 
    
         
            -
                  #   <Password>Webgistix</Password>
         
     | 
| 
       151 
     | 
    
         
            -
                  #   <CustomerID>3</CustomerID>
         
     | 
| 
       152 
     | 
    
         
            -
                  #   <Tracking>
         
     | 
| 
       153 
     | 
    
         
            -
                  #     <Order>AB12345</Order>
         
     | 
| 
       154 
     | 
    
         
            -
                  #   </Tracking>
         
     | 
| 
       155 
     | 
    
         
            -
                  #   <Tracking>
         
     | 
| 
       156 
     | 
    
         
            -
                  #     <Order>XY4567</Order>
         
     | 
| 
       157 
     | 
    
         
            -
                  #   </Tracking>
         
     | 
| 
       158 
     | 
    
         
            -
                  # </TrackingXML>
         
     | 
| 
       159 
     | 
    
         
            -
                  def build_tracking_request(order_ids, options)
         
     | 
| 
       160 
     | 
    
         
            -
                    xml = Builder::XmlMarkup.new :indent => 2
         
     | 
| 
       161 
     | 
    
         
            -
                    xml.instruct!
         
     | 
| 
       162 
     | 
    
         
            -
                    xml.tag! 'TrackingXML' do
         
     | 
| 
       163 
     | 
    
         
            -
                      add_credentials(xml)
         
     | 
| 
       164 
     | 
    
         
            -
             
     | 
| 
       165 
     | 
    
         
            -
                      order_ids.each do |o_id|
         
     | 
| 
       166 
     | 
    
         
            -
                        xml.tag! 'Tracking' do
         
     | 
| 
       167 
     | 
    
         
            -
                          xml.tag! 'Order', o_id
         
     | 
| 
       168 
     | 
    
         
            -
                        end
         
     | 
| 
       169 
     | 
    
         
            -
                      end
         
     | 
| 
       170 
     | 
    
         
            -
                    end
         
     | 
| 
       171 
     | 
    
         
            -
                  end
         
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
       173 
     | 
    
         
            -
                  def add_credentials(xml)
         
     | 
| 
       174 
     | 
    
         
            -
                    xml.tag! 'CustomerID', @options[:login]
         
     | 
| 
       175 
     | 
    
         
            -
                    xml.tag! 'Password', @options[:password]
         
     | 
| 
       176 
     | 
    
         
            -
                  end
         
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
                  def add_order(xml, order_id, shipping_address, line_items, options)
         
     | 
| 
       179 
     | 
    
         
            -
                    xml.tag! 'Order' do
         
     | 
| 
       180 
     | 
    
         
            -
                      xml.tag! 'ReferenceNumber', order_id
         
     | 
| 
       181 
     | 
    
         
            -
                      xml.tag! 'ShippingInstructions', options[:shipping_method]
         
     | 
| 
       182 
     | 
    
         
            -
                      xml.tag! 'Approve', 1
         
     | 
| 
       183 
     | 
    
         
            -
                      xml.tag! 'OrderComments', options[:comment] unless options[:comment].blank?
         
     | 
| 
       184 
     | 
    
         
            -
             
     | 
| 
       185 
     | 
    
         
            -
                      add_address(xml, shipping_address, options)
         
     | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
       187 
     | 
    
         
            -
                      Array(line_items).each_with_index do |line_item, index|
         
     | 
| 
       188 
     | 
    
         
            -
                        add_item(xml, line_item, index)
         
     | 
| 
       189 
     | 
    
         
            -
                      end
         
     | 
| 
       190 
     | 
    
         
            -
                    end
         
     | 
| 
       191 
     | 
    
         
            -
                  end
         
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
       193 
     | 
    
         
            -
                  def add_address(xml, address, options)
         
     | 
| 
       194 
     | 
    
         
            -
                    xml.tag! 'Name', address[:name]
         
     | 
| 
       195 
     | 
    
         
            -
                    xml.tag! 'Address1', address[:address1]
         
     | 
| 
       196 
     | 
    
         
            -
                    xml.tag! 'Address2', address[:address2] unless address[:address2].blank?
         
     | 
| 
       197 
     | 
    
         
            -
                    xml.tag! 'Address3', address[:address3] unless address[:address3].blank?
         
     | 
| 
       198 
     | 
    
         
            -
                    xml.tag! 'City', address[:city]
         
     | 
| 
       199 
     | 
    
         
            -
                    xml.tag! 'State', address[:state]
         
     | 
| 
       200 
     | 
    
         
            -
                    xml.tag! 'ZipCode', address[:zip]
         
     | 
| 
       201 
     | 
    
         
            -
                    xml.tag! 'Company', address[:company]
         
     | 
| 
       202 
     | 
    
         
            -
             
     | 
| 
       203 
     | 
    
         
            -
                    unless address[:country].blank?
         
     | 
| 
       204 
     | 
    
         
            -
                      country = Country.find(address[:country])
         
     | 
| 
       205 
     | 
    
         
            -
                      xml.tag! 'Country', country.name
         
     | 
| 
       206 
     | 
    
         
            -
                    end
         
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
       208 
     | 
    
         
            -
                    xml.tag! 'Phone', address[:phone]
         
     | 
| 
       209 
     | 
    
         
            -
                    xml.tag! 'Email', options[:email] unless options[:email].blank?
         
     | 
| 
       210 
     | 
    
         
            -
                  end
         
     | 
| 
       211 
     | 
    
         
            -
             
     | 
| 
       212 
     | 
    
         
            -
                  def add_item(xml, item, index)
         
     | 
| 
       213 
     | 
    
         
            -
                    xml.tag! 'Item' do
         
     | 
| 
       214 
     | 
    
         
            -
                      xml.tag! 'ItemID', item[:sku] unless item[:sku].blank?
         
     | 
| 
       215 
     | 
    
         
            -
                      xml.tag! 'ItemQty', item[:quantity] unless item[:quantity].blank?
         
     | 
| 
       216 
     | 
    
         
            -
                    end
         
     | 
| 
       217 
     | 
    
         
            -
                  end
         
     | 
| 
       218 
     | 
    
         
            -
             
     | 
| 
       219 
     | 
    
         
            -
                  def commit(action, request)
         
     | 
| 
       220 
     | 
    
         
            -
                    url = test? ? TEST_URLS[action] : SERVICE_URLS[action]
         
     | 
| 
       221 
     | 
    
         
            -
             
     | 
| 
       222 
     | 
    
         
            -
                    data = ssl_post(url, request,
         
     | 
| 
       223 
     | 
    
         
            -
                      'EndPointURL'  => url,
         
     | 
| 
       224 
     | 
    
         
            -
                      'Content-Type' => 'text/xml; charset="utf-8"'
         
     | 
| 
       225 
     | 
    
         
            -
                    )
         
     | 
| 
       226 
     | 
    
         
            -
             
     | 
| 
       227 
     | 
    
         
            -
                    response = parse_response(action, data)
         
     | 
| 
       228 
     | 
    
         
            -
                    Response.new(success?(response), message_from(response), response, :test => test?)
         
     | 
| 
       229 
     | 
    
         
            -
                  end
         
     | 
| 
       230 
     | 
    
         
            -
             
     | 
| 
       231 
     | 
    
         
            -
                  def success?(response)
         
     | 
| 
       232 
     | 
    
         
            -
                    response[:success] == SUCCESS || response[:success] == DUPLICATE
         
     | 
| 
       233 
     | 
    
         
            -
                  end
         
     | 
| 
       234 
     | 
    
         
            -
             
     | 
| 
       235 
     | 
    
         
            -
                  def message_from(response)
         
     | 
| 
       236 
     | 
    
         
            -
                    if response[:duplicate]
         
     | 
| 
       237 
     | 
    
         
            -
                      DUPLICATE_MESSAGE
         
     | 
| 
       238 
     | 
    
         
            -
                    elsif success?(response)
         
     | 
| 
       239 
     | 
    
         
            -
                      SUCCESS_MESSAGE
         
     | 
| 
       240 
     | 
    
         
            -
                    elsif response[:error_0] == INVALID_LOGIN
         
     | 
| 
       241 
     | 
    
         
            -
                      INVALID_LOGIN
         
     | 
| 
       242 
     | 
    
         
            -
                    else
         
     | 
| 
       243 
     | 
    
         
            -
                      FAILURE_MESSAGE
         
     | 
| 
       244 
     | 
    
         
            -
                    end
         
     | 
| 
       245 
     | 
    
         
            -
                  end
         
     | 
| 
       246 
     | 
    
         
            -
             
     | 
| 
       247 
     | 
    
         
            -
                  def parse_response(action, xml)
         
     | 
| 
       248 
     | 
    
         
            -
                    begin
         
     | 
| 
       249 
     | 
    
         
            -
                      document = REXML::Document.new("<response>#{xml}</response>")
         
     | 
| 
       250 
     | 
    
         
            -
                    rescue REXML::ParseException
         
     | 
| 
       251 
     | 
    
         
            -
                      return {:success => FAILURE}
         
     | 
| 
       252 
     | 
    
         
            -
                    end
         
     | 
| 
       253 
     | 
    
         
            -
             
     | 
| 
       254 
     | 
    
         
            -
                    case action
         
     | 
| 
       255 
     | 
    
         
            -
                    when :fulfillment
         
     | 
| 
       256 
     | 
    
         
            -
                      parse_fulfillment_response(document)
         
     | 
| 
       257 
     | 
    
         
            -
                    when :inventory
         
     | 
| 
       258 
     | 
    
         
            -
                      parse_inventory_response(document)
         
     | 
| 
       259 
     | 
    
         
            -
                    when :tracking
         
     | 
| 
       260 
     | 
    
         
            -
                      parse_tracking_response(document)
         
     | 
| 
       261 
     | 
    
         
            -
                    else
         
     | 
| 
       262 
     | 
    
         
            -
                      raise ArgumentError, "Unknown action #{action}"
         
     | 
| 
       263 
     | 
    
         
            -
                    end
         
     | 
| 
       264 
     | 
    
         
            -
                  end
         
     | 
| 
       265 
     | 
    
         
            -
             
     | 
| 
       266 
     | 
    
         
            -
                  def parse_fulfillment_response(document)
         
     | 
| 
       267 
     | 
    
         
            -
                    response = parse_errors(document)
         
     | 
| 
       268 
     | 
    
         
            -
             
     | 
| 
       269 
     | 
    
         
            -
                    # Check if completed
         
     | 
| 
       270 
     | 
    
         
            -
                    if completed = REXML::XPath.first(document, '//Completed')
         
     | 
| 
       271 
     | 
    
         
            -
                      completed.elements.each do |e|
         
     | 
| 
       272 
     | 
    
         
            -
                        response[e.name.underscore.to_sym] = e.text
         
     | 
| 
       273 
     | 
    
         
            -
                      end
         
     | 
| 
       274 
     | 
    
         
            -
                    else
         
     | 
| 
       275 
     | 
    
         
            -
                      response[:success] = FAILURE
         
     | 
| 
       276 
     | 
    
         
            -
                    end
         
     | 
| 
       277 
     | 
    
         
            -
             
     | 
| 
       278 
     | 
    
         
            -
                    response[:duplicate] = response[:success] == DUPLICATE
         
     | 
| 
       279 
     | 
    
         
            -
             
     | 
| 
       280 
     | 
    
         
            -
                    response
         
     | 
| 
       281 
     | 
    
         
            -
                  end
         
     | 
| 
       282 
     | 
    
         
            -
             
     | 
| 
       283 
     | 
    
         
            -
                  def parse_inventory_response(document)
         
     | 
| 
       284 
     | 
    
         
            -
                    response = parse_errors(document)
         
     | 
| 
       285 
     | 
    
         
            -
                    response[:stock_levels] = {}
         
     | 
| 
       286 
     | 
    
         
            -
             
     | 
| 
       287 
     | 
    
         
            -
                    document.root.each_element('//Item') do |node|
         
     | 
| 
       288 
     | 
    
         
            -
                      # {ItemID => 'SOME-ID', ItemQty => '101'}
         
     | 
| 
       289 
     | 
    
         
            -
                      params = node.elements.to_a.each_with_object({}) {|elem, hash| hash[elem.name] = elem.text}
         
     | 
| 
       290 
     | 
    
         
            -
             
     | 
| 
       291 
     | 
    
         
            -
                      response[:stock_levels][params['ItemID']] = params['ItemQty'].to_i
         
     | 
| 
       292 
     | 
    
         
            -
                    end
         
     | 
| 
       293 
     | 
    
         
            -
             
     | 
| 
       294 
     | 
    
         
            -
                    response
         
     | 
| 
       295 
     | 
    
         
            -
                  end
         
     | 
| 
       296 
     | 
    
         
            -
             
     | 
| 
       297 
     | 
    
         
            -
                  def parse_tracking_response(document)
         
     | 
| 
       298 
     | 
    
         
            -
                    response = parse_errors(document)
         
     | 
| 
       299 
     | 
    
         
            -
                    response[:tracking_numbers] = {}
         
     | 
| 
       300 
     | 
    
         
            -
                    response[:tracking_companies] = {}
         
     | 
| 
       301 
     | 
    
         
            -
                    response[:tracking_urls] = {}
         
     | 
| 
       302 
     | 
    
         
            -
             
     | 
| 
       303 
     | 
    
         
            -
                    document.root.each_element('//Shipment') do |node|
         
     | 
| 
       304 
     | 
    
         
            -
                      # {InvoiceNumber => 'SOME-ID', ShipmentTrackingNumber => 'SOME-TRACKING-NUMBER'}
         
     | 
| 
       305 
     | 
    
         
            -
                      params = node.elements.to_a.each_with_object({}) {|elem, hash| hash[elem.name] = elem.text}
         
     | 
| 
       306 
     | 
    
         
            -
             
     | 
| 
       307 
     | 
    
         
            -
                      tracking = params['ShipmentTrackingNumber']
         
     | 
| 
       308 
     | 
    
         
            -
             
     | 
| 
       309 
     | 
    
         
            -
                      unless tracking == NOT_SHIPPED
         
     | 
| 
       310 
     | 
    
         
            -
                        response[:tracking_numbers][params['InvoiceNumber']] ||= []
         
     | 
| 
       311 
     | 
    
         
            -
                        response[:tracking_numbers][params['InvoiceNumber']] << tracking
         
     | 
| 
       312 
     | 
    
         
            -
                      end
         
     | 
| 
       313 
     | 
    
         
            -
             
     | 
| 
       314 
     | 
    
         
            -
                      company = params['Method'].split[0] if params['Method']
         
     | 
| 
       315 
     | 
    
         
            -
                      if TRACKING_COMPANIES.include? company
         
     | 
| 
       316 
     | 
    
         
            -
                        response[:tracking_companies][params['InvoiceNumber']] ||= []
         
     | 
| 
       317 
     | 
    
         
            -
                        response[:tracking_companies][params['InvoiceNumber']] << company
         
     | 
| 
       318 
     | 
    
         
            -
                      end
         
     | 
| 
       319 
     | 
    
         
            -
                    end
         
     | 
| 
       320 
     | 
    
         
            -
             
     | 
| 
       321 
     | 
    
         
            -
                    response
         
     | 
| 
       322 
     | 
    
         
            -
                  end
         
     | 
| 
       323 
     | 
    
         
            -
             
     | 
| 
       324 
     | 
    
         
            -
                  def parse_errors(document)
         
     | 
| 
       325 
     | 
    
         
            -
                    response = {}
         
     | 
| 
       326 
     | 
    
         
            -
             
     | 
| 
       327 
     | 
    
         
            -
                    REXML::XPath.match(document, "//Errors/Error").to_a.each_with_index do |e, i|
         
     | 
| 
       328 
     | 
    
         
            -
                      response["error_#{i}".to_sym] = e.text
         
     | 
| 
       329 
     | 
    
         
            -
                    end
         
     | 
| 
       330 
     | 
    
         
            -
             
     | 
| 
       331 
     | 
    
         
            -
                    response[:success] = response.empty? ? SUCCESS : FAILURE
         
     | 
| 
       332 
     | 
    
         
            -
                    response
         
     | 
| 
       333 
     | 
    
         
            -
                  end
         
     | 
| 
       334 
     | 
    
         
            -
                end
         
     | 
| 
       335 
     | 
    
         
            -
              end
         
     | 
| 
       336 
     | 
    
         
            -
            end
         
     | 
| 
       337 
     | 
    
         
            -
             
     | 
| 
       338 
     | 
    
         
            -
             
     |