Class: Shipping::ReturnShippingInsurance
- Inherits:
-
BaseService
- Object
- BaseService
- Shipping::ReturnShippingInsurance
- Defined in:
- app/services/shipping/return_shipping_insurance.rb
Overview
Handles Shipsurance insurance for RMA return shipments.
Two paths based on label type:
External labels (Amazon / 3rd-party tracking numbers entered by staff):
- start_external_tracking(rma) — registers each untracked tracking number with
ShipEngine so we receive webhook events. Called on every RMA save that has
insure_return_shipping: true. - book_external_insurance(rma, tracking_number, occurred_at) — books Shipsurance
using the webhook event's occurred_at as the ship date. Called by
WebhookProcessors::ShipengineProcessor when the first real carrier movement
event arrives (status code != AC).
Internal labels (labels we generated via our return delivery):
3. book_internal_insurance(rma) — delegates to PackageShippingInsurance for each
uninsured shipment in the return delivery. Called after label generation.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- CARRIER_OPTIONS =
Mirrors PackageShippingInsurance::SHIPSURANCE_CARRIER_DATA_BY_CARRIER keys so the
UI can show a carrier dropdown for external returns. Shipping::PackageShippingInsurance::SHIPSURANCE_CARRIER_DATA_BY_CARRIER .keys .map { |k| [k.to_s, k.to_s] } .freeze
- TRACKING_NUMBER_GEM_TO_SHIPSURANCE_CARRIER =
Maps tracking_number gem courier codes → Shipsurance carrier name strings.
Derived from CARRIER_CODE_MAP in config/initializers/carrier_codes.rb. ::TRACKING_NUMBER_GEM_TO_SHIPSURANCE_CARRIER
- INSURED_VALUE_MIN =
Minimum declared value (cents) below which we skip insurance — same threshold as
PackageShippingInsurance::INSURED_VALUE_MIN. Shipping::PackageShippingInsurance::INSURED_VALUE_MIN
- UNSUCCESFUL_STRING =
Shipping::PackageShippingInsurance::UNSUCCESFUL_STRING
Instance Method Summary collapse
-
#book_external_insurance(rma, tracking_number, occurred_at) ⇒ Object
── Entry point B ────────────────────────────────────────────────────────── Books Shipsurance when ShipEngine reports the first real carrier movement.
-
#book_internal_insurance(rma) ⇒ Object
── Entry point C ────────────────────────────────────────────────────────── Books Shipsurance for internally generated return shipments via PackageShippingInsurance (which handles the delivery qualifies? check).
-
#start_external_tracking(rma) ⇒ Object
── Entry point A ────────────────────────────────────────────────────────── Registers external tracking numbers with ShipEngine for webhook delivery.
Methods inherited from BaseService
#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #options, #process, #tagged_logger
Constructor Details
This class inherits a constructor from BaseService
Instance Method Details
#book_external_insurance(rma, tracking_number, occurred_at) ⇒ Object
── Entry point B ──────────────────────────────────────────────────────────
Books Shipsurance when ShipEngine reports the first real carrier movement.
Carrier is auto-detected from the tracking number; return_shipping_carrier
is used as a fallback for unrecognised formats.
Safe to call multiple times — skips numbers already insured.
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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'app/services/shipping/return_shipping_insurance.rb', line 91 def book_external_insurance(rma, tracking_number, occurred_at) return Result.new(status: :skip, status_message: 'Insurance not opted in') unless rma.insure_return_shipping? return Result.new(status: :skip, status_message: 'Already insured') if rma.return_insurance_data[tracking_number].present? declared_value = calculate_declared_value(rma) if declared_value < INSURED_VALUE_MIN return Result.new(status: :skip, status_message: "Declared value #{declared_value} below minimum #{INSURED_VALUE_MIN}") end carrier = effective_carrier(tracking_number, rma.return_shipping_carrier) carrier_id_value = carrier_id(carrier) unless carrier_id_value.present? return Result.new(status: :error, status_message: "Cannot determine Shipsurance carrier for #{tracking_number} (detected: #{carrier.inspect})") end shipment_date = occurred_at.strftime('%m/%d/%Y') form_params = { recordSourceIdentifier: "rma_#{rma.id}_#{tracking_number}", extShipmentTypeId: '1', extCarrierId: carrier_id_value, referenceNumber: rma.rma_number, trackingNumber: tracking_number, declaredValue: declared_value.to_s, shipmentDate: shipment_date, dsiRatePer100: carrier_percentage_charge(carrier, false).to_s } status = :ok = '' begin Retryable.retryable(tries: 2, sleep: lambda { |n| 4**n }, matching: [/#{UNSUCCESFUL_STRING}/, UNSUCCESFUL_STRING]) do |retries, exception| Rails.logger.debug { "[ReturnShippingInsurance] book_external_insurance retries: #{retries}, exception: #{exception}" } response_arr = shipsurance_client.record_shipment(form_params).split(',') status = response_arr[0] == '1' ? :ok : :error = response_arr[1].to_s raise UNSUCCESFUL_STRING unless status == :ok record_id = response_arr.last rma.update_column(:return_insurance_data, rma.return_insurance_data.merge(tracking_number => record_id)) Rails.logger.info "[ReturnShippingInsurance] Insured RMA #{rma.rma_number} tracking #{tracking_number} — Shipsurance ID #{record_id}" end rescue StandardError => e raise unless e. == UNSUCCESFUL_STRING end Result.new(status:, status_message:) end |
#book_internal_insurance(rma) ⇒ Object
── Entry point C ──────────────────────────────────────────────────────────
Books Shipsurance for internally generated return shipments via
PackageShippingInsurance (which handles the delivery qualifies? check).
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'app/services/shipping/return_shipping_insurance.rb', line 144 def book_internal_insurance(rma) return Result.new(status: :skip, status_message: 'Insurance not opted in') unless rma.insure_return_shipping? return Result.new(status: :skip, status_message: 'No return delivery') unless rma.return_delivery.present? uninsured_shipments = rma.return_delivery.shipments.label_complete.reject(&:is_ship_insured) return Result.new(status: :ok, status_message: 'All shipments already insured') if uninsured_shipments.empty? psi = Shipping::PackageShippingInsurance.new errors = [] uninsured_shipments.each do |shipment| result = psi.book_shipping_insurance_for_shipment(shipment, {}) if result.status != :ok errors << "Shipment #{shipment.id}: #{result.}" end end if errors.any? Result.new(status: :error, status_message: errors.join('; ')) else Result.new(status: :ok, status_message: "Insured #{uninsured_shipments.size} shipment(s)") end end |
#start_external_tracking(rma) ⇒ Object
── Entry point A ──────────────────────────────────────────────────────────
Registers external tracking numbers with ShipEngine for webhook delivery.
Carrier is auto-detected from the tracking number via the tracking_number gem;
rma.return_shipping_carrier is used as a fallback for unrecognised formats.
Safe to call multiple times — always attempts registration for all numbers
(ShipEngine is idempotent for duplicate registrations).
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 82 83 84 |
# File 'app/services/shipping/return_shipping_insurance.rb', line 48 def start_external_tracking(rma) return Result.new(status: :skip, status_message: 'Insurance not opted in') unless rma.insure_return_shipping? return Result.new(status: :skip, status_message: 'No tracking numbers present') if rma.tracking_numbers.blank? errors = [] skipped = [] started = [] rma.tracking_numbers.reject(&:blank?).each do |tracking_number| carrier = effective_carrier(tracking_number, rma.return_shipping_carrier) unless carrier.present? skipped << tracking_number Rails.logger.warn "[ReturnShippingInsurance] Cannot detect carrier for #{tracking_number} and no fallback set — skipping ShipEngine registration for RMA #{rma.rma_number}" next end begin WyShipping.start_tracking_for_carrier(tracking_number, carrier) started << tracking_number Rails.logger.info "[ReturnShippingInsurance] Started ShipEngine tracking: #{tracking_number} (#{carrier}) for RMA #{rma.rma_number}" rescue StandardError => e msg = "Failed to start tracking for #{tracking_number}: #{e.}" Rails.logger.error "[ReturnShippingInsurance] #{msg}" ErrorReporting.error(e, msg, rma_id: rma.id, tracking_number: tracking_number) errors << msg end end if errors.any? Result.new(status: :error, status_message: errors.join('; ')) else msg_parts = [] msg_parts << "Started tracking for #{started.size} number(s)" if started.any? msg_parts << "Skipped #{skipped.size} (carrier undetectable)" if skipped.any? Result.new(status: :ok, status_message: msg_parts.join('; ')) end end |