Class: Shipping::WalmartSeller

Inherits:
Base
  • Object
show all
Defined in:
app/services/shipping/walmart_seller.rb

Overview

Shipping carrier class for Walmart's "Ship with Walmart" (SWW) service
Provides discounted shipping labels for Walmart marketplace orders

This class follows the same interface as other Shipping::* carriers
and integrates with the WyShipping rate comparison system.

Usage:
shipper = Shipping::WalmartSeller.new(
walmart_partner: :walmart_seller_us,
purchase_order_id: '1234567890',
sender_address: '123 Warehouse St',
# ... other address fields
)
response = shipper.find_rates
if response[:success]
response[:rates].each { |rate| puts rate[:service_name] }
end

Constant Summary collapse

CARRIER_NAME =

Carrier name.

'WalmartSeller'
SERVICE_CODE_PREFIX =

Service code prefix to distinguish SWW rates from direct carrier rates

'SWW_'

Instance Attribute Summary collapse

Attributes inherited from Base

#address, #address2, #address3, #address_residential, #attention_name, #billing_account, #billing_country, #billing_zip, #ci_comments, #city, #close_report_only, #cod_amount, #cod_collection_type, #company, #country, #currency_code, #data, #debug, #declared_value, #delivery_instructions, #delivery_total_value, #description, #discount_price, #dropoff_type, #email, #eta, #export_reason, #freight_class, #freightquote_authorization_url, #freightquote_client_id, #freightquote_client_secret, #freightquote_customer_code, #freightquote_events_url, #freightquote_rating_url, #freightquote_shipping_url, #freightquote_voiding_url, #handling_instructions, #has_loading_dock, #image_type, #importer_of_record, #include_first_class_mail_options, #insured_value, #is_construction_site, #is_trade_show, #label_type, #last_request_payload, #last_request_quote_id, #last_response_payload, #limited_access, #line_items, #master_tracking_number, #measure_height, #measure_length, #measure_units, #measure_width, #media_mail, #multiple_piece_shipping, #negotiated_rates, #package, #package_count, #package_sequence_number, #package_total, #packages, #packaging_type, #paperless, #pay_type, #phone, #pickup_datetime, #pickup_instructions, #plain_response, #price, #rate_data, #reference_number_1, #reference_number_2, #reference_number_3, #reference_number_code_1, #reference_number_code_2, #required, #requires_appointment, #requires_inside_delivery, #requires_liftgate, #response, #response_headers, #response_status, #return_to_address, #return_to_address2, #return_to_address3, #return_to_address_residential, #return_to_attention_name, #return_to_city, #return_to_company, #return_to_country, #return_to_email, #return_to_has_loading_dock, #return_to_is_construction_site, #return_to_is_trade_show, #return_to_limited_access, #return_to_name, #return_to_phone, #return_to_requires_appointment, #return_to_requires_inside_delivery, #return_to_requires_liftgate, #return_to_state, #return_to_zip, #rl_carriers_api_key, #rl_carriers_shipping_url, #saturday_delivery, #sender_address, #sender_address2, #sender_address3, #sender_address_residential, #sender_attention_name, #sender_city, #sender_company, #sender_country, #sender_email, #sender_has_loading_dock, #sender_is_construction_site, #sender_is_trade_show, #sender_limited_access, #sender_name, #sender_phone, #sender_requires_appointment, #sender_requires_inside_delivery, #sender_requires_liftgate, #sender_state, #sender_tax_identification_number, #sender_zip, #service_code, #service_type, #services, #ship_date, #shipengine_api_key, #shipengine_canadapost_account_id, #shipengine_canadapost_parent_account_number, #shipengine_canpar_account_id, #shipengine_dhl_express_account_id, #shipengine_fed_ex_account_id, #shipengine_fed_ex_ca_account_id, #shipengine_purolator_account_id, #shipengine_ups_account_id, #shipengine_ups_ca_account_id, #shipengine_usps_account_id, #shipper_address, #shipper_address2, #shipper_address3, #shipper_address_residential, #shipper_attention_name, #shipper_city, #shipper_company, #shipper_country, #shipper_email, #shipper_has_loading_dock, #shipper_is_construction_site, #shipper_is_trade_show, #shipper_limited_access, #shipper_name, #shipper_phone, #shipper_requires_appointment, #shipper_requires_inside_delivery, #shipper_requires_liftgate, #shipper_state, #shipper_zip, #signature_confirmation, #skip_png_download, #skip_rate_test, #special_instructions, #state, #tax_identification_number, #time_in_transit, #total_shipment_weight, #transaction_type, #weight, #weight_units, #zip

Instance Method Summary collapse

Methods inherited from Base

#fedex, #purolator, state_from_zip, #ups, #ups_freight

Constructor Details

#initialize(options = {}) ⇒ WalmartSeller

Returns a new instance of WalmartSeller.

Parameters:

  • options (Hash) (defaults to: {})

    shipment options

Options Hash (options):

  • walmart_partner (Object)

    the Walmart partner record

  • purchase_order_id (String)

    the Walmart purchase order id to ship

  • deliver_by_date (Date)

    promised delivery date

  • ship_by_date (Date)

    promised ship date

  • box_items (Array<Hash>)

    explicit box contents for the shipment



40
41
42
43
44
45
46
47
# File 'app/services/shipping/walmart_seller.rb', line 40

def initialize(options = {})
  super
  @walmart_partner = options[:walmart_partner]
  @purchase_order_id = options[:purchase_order_id]
  @deliver_by_date = options[:deliver_by_date]
  @ship_by_date = options[:ship_by_date]
  @api_log = []
end

Instance Attribute Details

#api_logObject (readonly)

Accumulated API call log from all SWW operations on this shipper instance



32
33
34
# File 'app/services/shipping/walmart_seller.rb', line 32

def api_log
  @api_log
end

#deliver_by_dateObject

Returns the value of attribute deliver_by_date.



23
24
25
# File 'app/services/shipping/walmart_seller.rb', line 23

def deliver_by_date
  @deliver_by_date
end

#purchase_order_idObject

Returns the value of attribute purchase_order_id.



23
24
25
# File 'app/services/shipping/walmart_seller.rb', line 23

def purchase_order_id
  @purchase_order_id
end

#ship_by_dateObject

Returns the value of attribute ship_by_date.



23
24
25
# File 'app/services/shipping/walmart_seller.rb', line 23

def ship_by_date
  @ship_by_date
end

#walmart_partnerObject

Returns the value of attribute walmart_partner.



23
24
25
# File 'app/services/shipping/walmart_seller.rb', line 23

def walmart_partner
  @walmart_partner
end

Instance Method Details

#create_label(rate, logger = nil) ⇒ Hash

Create a shipping label via Ship with Walmart

Parameters:

  • rate (Hash)

    The rate estimate to use (from find_rates)

  • logger (Object, nil) (defaults to: nil)

Returns:

  • (Hash)

    Result with :success, :label_id, :tracking_number, :carrier, :error



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/services/shipping/walmart_seller.rb', line 88

def create_label(rate, logger = nil)
  logger ||= Rails.logger

  return { success: false, error: 'Walmart partner and purchase order ID are required' } unless @walmart_partner.present? && @purchase_order_id.present?

  begin
    orchestrator = Edi::Walmart::Orchestrator.new(@walmart_partner)
    sww_client = Edi::Walmart::ShipWithWalmart.new(orchestrator)

    options = build_label_options(rate)
    logger.info("[SWW] Creating label for PO: #{@purchase_order_id}, carrier: #{rate[:carrier_id]}")

    result = sww_client.create_label(options)
    @api_log.concat(sww_client.api_log)

    if result.success
      {
        success: true,
        label_id: result.label_id,
        tracking_number: result.tracking_number,
        carrier: result.carrier,
        service_type: result.service_type,
        error: nil
      }
    else
      { success: false, error: result.error }
    end
  rescue StandardError => e
    logger.error("[SWW] create_label error: #{e.message}")
    { success: false, error: e.message }
  end
end

#download_label(carrier, tracking_number, logger = nil) ⇒ Hash

Download a shipping label PDF
Per Walmart API, labels are downloaded using carrier + tracking number

Parameters:

  • carrier (String)

    Carrier short name (e.g., 'USPS', 'FEDEX')

  • tracking_number (String)

    The tracking number from create_label

  • logger (Object, nil) (defaults to: nil)

Returns:

  • (Hash)

    Result with :success, :label_data, :content_type, :error



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'app/services/shipping/walmart_seller.rb', line 128

def download_label(carrier, tracking_number, logger = nil)
  logger ||= Rails.logger

  return { success: false, error: 'Walmart partner configuration is required' } if @walmart_partner.blank?

  return { success: false, error: 'Carrier and tracking number are required' } unless carrier.present? && tracking_number.present?

  begin
    orchestrator = Edi::Walmart::Orchestrator.new(@walmart_partner)
    sww_client = Edi::Walmart::ShipWithWalmart.new(orchestrator)

    result = sww_client.download_label(carrier, tracking_number)
    @api_log.concat(sww_client.api_log)

    if result.success
      {
        success: true,
        label_data: result.label_data,
        content_type: result.content_type,
        error: nil
      }
    else
      { success: false, error: result.error }
    end
  rescue StandardError => e
    logger.error("[SWW] download_label error: #{e.message}")
    { success: false, error: e.message }
  end
end

#find_rates(logger = nil) ⇒ Hash

Get shipping rate estimates from Ship with Walmart

Returns:

  • (Hash)

    Response hash with :success, :rates, :message, :request, :xml keys



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'app/services/shipping/walmart_seller.rb', line 51

def find_rates(logger = nil)
  logger ||= Rails.logger

  return build_error_response('Walmart partner configuration is required') if @walmart_partner.blank?

  return build_error_response('Valid origin and destination addresses are required') unless valid_addresses?

  begin
    orchestrator = Edi::Walmart::Orchestrator.new(@walmart_partner)
    sww_client = Edi::Walmart::ShipWithWalmart.new(orchestrator)

    options = build_estimate_options
    logger.info("[SWW] Requesting rates for PO: #{@purchase_order_id}")
    logger.info("[SWW] line_items present: #{@line_items.present?}, count: #{@line_items&.size || 0}")
    logger.info("[SWW] box_items from options: #{options[:box_items].inspect}")
    logger.debug("[SWW] Rate request options: #{options.inspect}")

    result = sww_client.get_shipping_estimates(options)
    @api_log.concat(sww_client.api_log)

    if result.success
      rates = build_rate_estimates(result.estimates)
      build_success_response(rates, options)
    else
      build_error_response(result.error || 'Failed to retrieve shipping estimates')
    end
  rescue StandardError => e
    logger.error("[SWW] find_rates error: #{e.message}")
    logger.error(e.backtrace.first(5).join("\n"))
    build_error_response("Error retrieving Ship with Walmart rates: #{e.message}")
  end
end

#get_labels_by_po(purchase_order_id = nil, logger = nil) ⇒ Hash

Get all labels for a purchase order
Useful for finding labels when you only have the PO number

Parameters:

  • purchase_order_id (String) (defaults to: nil)

    The Walmart PO number

  • logger (Object, nil) (defaults to: nil)

Returns:

  • (Hash)

    Result with :success, :labels, :error



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'app/services/shipping/walmart_seller.rb', line 192

def get_labels_by_po(purchase_order_id = nil, logger = nil)
  logger ||= Rails.logger
  po = purchase_order_id || @purchase_order_id

  return { success: false, labels: [], error: 'Walmart partner configuration is required' } if @walmart_partner.blank?

  return { success: false, labels: [], error: 'Purchase order ID is required' } if po.blank?

  begin
    orchestrator = Edi::Walmart::Orchestrator.new(@walmart_partner)
    sww_client = Edi::Walmart::ShipWithWalmart.new(orchestrator)

    result = sww_client.get_labels_by_purchase_order(po)
    @api_log.concat(sww_client.api_log)

    { success: result.success, labels: result.labels, error: result.error }
  rescue StandardError => e
    logger.error("[SWW] get_labels_by_po error: #{e.message}")
    { success: false, labels: [], error: e.message }
  end
end

#void_label(carrier, tracking_number, logger = nil) ⇒ Hash

Void/discard a shipping label
Per Walmart API, labels are voided using carrier + tracking number

Parameters:

  • carrier (String)

    Carrier short name (e.g., 'USPS', 'FEDEX')

  • tracking_number (String)

    The tracking number to void

  • logger (Object, nil) (defaults to: nil)

Returns:

  • (Hash)

    Result with :success, :error



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'app/services/shipping/walmart_seller.rb', line 165

def void_label(carrier, tracking_number, logger = nil)
  logger ||= Rails.logger

  return { success: false, error: 'Walmart partner configuration is required' } if @walmart_partner.blank?

  return { success: false, error: 'Carrier and tracking number are required' } unless carrier.present? && tracking_number.present?

  begin
    orchestrator = Edi::Walmart::Orchestrator.new(@walmart_partner)
    sww_client = Edi::Walmart::ShipWithWalmart.new(orchestrator)

    result = sww_client.discard_label(carrier, tracking_number)
    @api_log.concat(sww_client.api_log)

    { success: result.success, error: result.error }
  rescue StandardError => e
    logger.error("[SWW] void_label error: #{e.message}")
    { success: false, error: e.message }
  end
end