Class: SpreeDelhivery::ShipmentSender

Inherits:
Object
  • Object
show all
Defined in:
app/services/spree_delhivery/shipment_sender.rb

Instance Method Summary collapse

Constructor Details

#initialize(shipment) ⇒ ShipmentSender

Returns a new instance of ShipmentSender.



3
4
5
6
7
8
9
# File 'app/services/spree_delhivery/shipment_sender.rb', line 3

def initialize(shipment)
  @shipment = shipment
  @order = shipment.order
  @address = @order.ship_address
  @client = SpreeDelhivery::Client.new
  @integration = @client.integration
end

Instance Method Details

#callObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
75
76
77
78
79
80
81
# File 'app/services/spree_delhivery/shipment_sender.rb', line 11

def call
  return error("Shipment already has a Waybill") if @shipment.delhivery_waybill.present?

  payload = build_payload
  
  # Send API Request
  response = @client.create_shipment(payload)
  
  # Check for explicit success or package success status
  success_status = response['success'] || 
                   (response['packages'].present? && response['packages'][0]['status'] == 'Success')

  if success_status
    pkg_data = response['packages'].first
    
    # 1. Update Data Columns Safely 
    # Using update_columns to bypass Spree callbacks that might confuse the hash response with a model
    @shipment.update_columns(
      tracking: pkg_data['waybill'],
      delhivery_waybill: pkg_data['waybill'],
      delhivery_ref_id: pkg_data['refnum'], 
      delhivery_response_data: response # Rails handles JSON serialization automatically
    )
    
    # 2. Reload object to ensure fresh state
    @shipment.reload 

    # 3. Fire Spree Shipment State Machine
    # This moves state from 'ready' -> 'shipped'
    if @shipment.can_ship?
      @shipment.ship! 
    end

    # 4. Fetch Label URL immediately
    begin
      label_res = @client.fetch_label(pkg_data['waybill'])
      if label_res['packages'] && label_res['packages'][0]['pdf_download_link']
         @shipment.update_column(:delhivery_label_url, label_res['packages'][0]['pdf_download_link'])
      end
    rescue => e
      Rails.logger.error("Delhivery Label Fetch Failed: #{e.message}")
    end

    return success(@shipment)
  else
    # --- IMPROVED ERROR HANDLING ---
    # 1. Extract the deep error message first (it's more accurate than 'rmk')
    raw_error = response.dig('packages', 0, 'remarks')&.join(', ') || response['rmk'] || "Unknown Error"
    
    # 2. Map known API errors to friendly user messages
    friendly_error = case raw_error.to_s.downcase
                     when /insufficient balance/
                       # Fetch live balance to show the user
                       current_bal = @client.fetch_balance rescue nil
                       msg = "Authorization Failed: Insufficient Delhivery Wallet Balance."
                       msg += " Current Balance: ₹#{current_bal}." if current_bal
                       msg + " Please recharge."
                     when /duplicate/
                       "Duplicate Order: This order ID has already been processed."
                     when /pincode/
                       "Serviceability Error: Pincode (#{@address.zipcode}) not serviceable."
                     else
                       "Delhivery Error: #{raw_error}"
                     end

    return error(friendly_error)
  end
rescue StandardError => e
  Rails.logger.error(e.backtrace.join("\n"))
  return error(e.message)
end