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 =
'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, #include_first_class_mail_options, #insured_value, #is_construction_site, #is_trade_show, #label_type, #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, #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, #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.



32
33
34
35
36
37
38
39
# File 'app/services/shipping/walmart_seller.rb', line 32

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



30
31
32
# File 'app/services/shipping/walmart_seller.rb', line 30

def api_log
  @api_log
end

#deliver_by_dateObject

Returns the value of attribute deliver_by_date.



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

def deliver_by_date
  @deliver_by_date
end

#purchase_order_idObject

Returns the value of attribute purchase_order_id.



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

def purchase_order_id
  @purchase_order_id
end

#ship_by_dateObject

Returns the value of attribute ship_by_date.



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

def ship_by_date
  @ship_by_date
end

#walmart_partnerObject

Returns the value of attribute walmart_partner.



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

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



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'app/services/shipping/walmart_seller.rb', line 80

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



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'app/services/shipping/walmart_seller.rb', line 120

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

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

  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



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'app/services/shipping/walmart_seller.rb', line 43

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

  return build_error_response('Walmart partner configuration is required') unless @walmart_partner.present?

  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



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'app/services/shipping/walmart_seller.rb', line 184

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' } unless @walmart_partner.present?

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

  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



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'app/services/shipping/walmart_seller.rb', line 157

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

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

  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