Class: FreightquoteVoidConfirmationWorker

Inherits:
Object
  • Object
show all
Includes:
Sidekiq::Job
Defined in:
app/workers/freightquote_void_confirmation_worker.rb

Overview

Scheduled 1 hour after a Shipping::Freightquote#void call to confirm
CHR actually cancelled the order. CHR's DELETE returns a requestId
(an async-job ack), not a synchronous cancel confirmation — see the
DE787078 forensic: our void at 2026-05-19 15:55 didn't reflect in
CHR's portal until 2026-05-20 12:02 (after a manual rep cleanup
prompted by an email exchange).

This worker checks two sources for a LOAD CANCELLED or ORDER CANCELED
echo of our void:

  1. FAST PATH — the local FreightEvent projection (populated by
    Webhooks::V1::FreightquoteController when CHR pushes us a
    callback). Cheap DB query; the canonical source once CHR's
    Customer Integration Team has configured our webhook subscription.
  2. FALLBACK — direct GET against CHR's /v2/events polling endpoint
    via WyShipping.freightquote_cancel_event_present?. Works
    regardless of webhook configuration; uses the same OAuth bearer
    we already mint for rate/label/void calls. Lets the worker do
    useful work before the webhook subscription is live AND adds
    defense-in-depth against missed webhook events afterwards.

Alerts only if BOTH sources say "no cancel event in the window".

Non-blocking: order/delivery flows do NOT wait on this. Same
operational pattern as the existing Canadapost / Purolator manual
void email fallback in Delivery#void_shipments — alert and move on.

Scheduled by Delivery#void_shipments after a successful FQ void.

Constant Summary collapse

EVENT_TIME_TOLERANCE =

Tolerance for clock skew between our void timestamp and CHR's
event_time — accept events emitted up to 5 minutes before we
called DELETE.

5.minutes
CONFIRMATION_EVENT_TYPES =
['LOAD CANCELLED', 'ORDER CANCELED'].freeze

Instance Method Summary collapse

Instance Method Details

#perform(delivery_id, freight_order_number, voided_at_iso) ⇒ Object

Parameters:

  • delivery_id (Integer)
  • freight_order_number (String, Integer)

    CHR's orderNumber as we
    passed it to the DELETE call

  • voided_at_iso (String)

    ISO8601 timestamp of the DELETE call



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
82
83
84
85
86
87
88
# File 'app/workers/freightquote_void_confirmation_worker.rb', line 47

def perform(delivery_id, freight_order_number, voided_at_iso)
  voided_at = Time.zone.parse(voided_at_iso.to_s)
  order_number = freight_order_number.to_i

  # An unparseable voided_at_iso (corrupted job arg, time-format
  # regression) would otherwise let confirmed? drop the event_time
  # filter and silently match an old cancellation from a prior
  # booking cycle — suppressing the alert we want. Alert immediately
  # in that case with explicit context.
  if voided_at.nil?
    msg = "Freightquote void confirmation could not verify (invalid voided_at_iso=#{voided_at_iso.inspect}) " \
          "for DE#{delivery_id} (CHR order=#{order_number})."
    Rails.logger.error "[FreightquoteVoidConfirmationWorker] #{msg}"
    ErrorReporting.warning(msg, {
      delivery_id: delivery_id,
      chr_order_number: order_number,
      voided_at_iso: voided_at_iso,
      source: :background
    }.compact)
    return
  end

  if confirmed?(order_number, voided_at)
    Rails.logger.info "[FreightquoteVoidConfirmationWorker] DE#{delivery_id} order=#{order_number} void confirmed via FreightEvent"
    return
  end

  delivery = Delivery.find_by(id: delivery_id)
  msg = "Freightquote void NOT confirmed within 1 hour for DE#{delivery_id} (CHR order=#{order_number}). " \
        'CHR has not emitted LOAD CANCELLED or ORDER CANCELED — the order may still be live in their system; ' \
        'contact the CHR account team to confirm cleanup.'
  context = {
    delivery_id: delivery_id,
    delivery_state: delivery&.state,
    chr_order_number: order_number,
    voided_at: voided_at&.iso8601,
    source: :background
  }.compact

  Rails.logger.warn "[FreightquoteVoidConfirmationWorker] #{msg} #{context.to_json}"
  ErrorReporting.warning(msg, context)
end