Class: WebhookProcessors::CurriProcessor
- Inherits:
-
Object
- Object
- WebhookProcessors::CurriProcessor
- Defined in:
- app/services/webhook_processors/curri_processor.rb
Overview
Processor for Curri delivery-status webhook callbacks.
Resolves the Delivery by freight_order_number (the Curri delivery id
"del_…" recorded by Shipping::Curri#label), rolls the mapped status onto
each shipment's tracking columns, and projects one ShipmentEvent row per
status transition so the existing Tracking Events tab and status icon
light up with no new UI.
The webhook refires the same status every ~20s with fresher GPS; the
controller's external_id dedup collapses those, and the ShipmentEvent
idempotency key (keyed on tracking_number + event_code, NOT the volatile
GPS) absorbs any that slip through, so replays are no-ops.
Constant Summary collapse
- CURRI_STATUS_TO_STATUS_CODE =
Curri delivery statuses (docs.curri.com → Statuses, plus 'scheduled'
observed live on the sandbox 2026-07-24 for future-scheduled bookings)
→ our normalized ShipEngine-style status codes
(ShipmentEvent::STATUS_CODE_LABELS). { 'pending' => 'NY', # booked, no driver assigned yet 'scheduled' => 'NY', 'en_route_to_origin' => 'AC', # driver engaged, heading to pickup 'at_origin' => 'AC', 'en_route_to_destination' => 'IT', 'at_destination' => 'IT', 'delivered' => 'DE', 'canceled' => 'EX' }.freeze
Class Method Summary collapse
-
.call(webhook_log) ⇒ Object
Handles the self.call.
Instance Method Summary collapse
-
#call ⇒ Object
Returns the call.
-
#initialize(webhook_log) ⇒ Object
constructor
Initializes.
Constructor Details
#initialize(webhook_log) ⇒ Object
Initializes.
51 52 53 54 |
# File 'app/services/webhook_processors/curri_processor.rb', line 51 def initialize(webhook_log) @webhook_log = webhook_log @data = webhook_log.data end |
Class Method Details
.call(webhook_log) ⇒ Object
Handles the self.call.
43 44 45 |
# File 'app/services/webhook_processors/curri_processor.rb', line 43 def self.call(webhook_log) new(webhook_log).call end |
Instance Method Details
#call ⇒ Object
Returns the call.
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'app/services/webhook_processors/curri_processor.rb', line 59 def call curri_delivery_id = @data['id'] curri_status = extract_status status_code = CURRI_STATUS_TO_STATUS_CODE[curri_status] Rails.logger.info "[CurriProcessor] Processing #{curri_delivery_id} -> #{curri_status}" if status_code.nil? # WebhookLog#process! only retries or alerts on a RAISED exception — a # returned hash marks the log `processed`, i.e. green. Without this # report an unmapped status writes no ShipmentEvent and leaves no # signal anywhere. Curri already carries one status we found only by # probing the sandbox (`scheduled`); assume there are others. report_dropped('Curri webhook: unmapped delivery status', curri_delivery_id: curri_delivery_id, curri_status: curri_status) return { status: 'skipped', reason: "Unknown Curri status: #{curri_status.inspect}", curri_delivery_id: curri_delivery_id } end delivery = Delivery.find_by(freight_order_number: curri_delivery_id) unless delivery # Same silent-success path, and this one is unrecoverable: the ~20s # refire of this status dedups onto the existing WebhookLog # (WebhookLog.resolve_ingest) and never reprocesses, so the transition # is lost for good rather than retried. report_dropped('Curri webhook: no Delivery matches the Curri delivery id', curri_delivery_id: curri_delivery_id, curri_status: curri_status) return { status: 'not_found', reason: 'No Delivery with this freight_order_number', curri_delivery_id: curri_delivery_id } end shipments = tracked_shipments(delivery) events_persisted = persist_shipment_events(shipments, curri_status, status_code) updated = shipments.count { |shipment| update_shipment_tracking(shipment, curri_status, status_code) } { status: 'success', delivery_id: delivery.id, curri_delivery_id: curri_delivery_id, curri_status: curri_status, status_code: status_code, shipments_updated: updated, shipment_events_persisted: events_persisted, cancellation_reason: @data['cancellationReason'] }.compact end |