Class: ShipmentEventStatusSummary

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

Overview

Compresses a Shipment's shipment_events rows into a single status badge
rendered next to the tracking number on the delivery's Shipments table
and (optionally) inline elsewhere. Mirrors FreightEventStatusSummary
in shape and conventions, with ShipEngine's per-scan status codes
(AC / IT / DE / EX / AT / SP / UN / NY) substituted for CHR's typed
event strings.

A parcel shipment has at most one badge at a time — unlike a freight
delivery where load / pickup / delivery are independent buckets, parcel
scans are linear and the most-advanced one wins. Order:

delivered (DE/SP) → green shipping-fast
delivery_attempt (AT) → orange shipping-fast
exception (EX) → red triangle-exclamation
in_transit (IT) → blue truck-fast
accepted (AC) → grey box
nothing → no badge

Examples:

ShipmentEventStatusSummary.for(shipment)
# => #<Badge state: :delivered, color: :success, …>

Defined Under Namespace

Classes: Badge

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(shipment = nil, tracking_number: nil, events: nil) ⇒ ShipmentEventStatusSummary

Returns a new instance of ShipmentEventStatusSummary.

Parameters:

  • shipment (Shipment, nil) (defaults to: nil)
  • tracking_number (String, nil) (defaults to: nil)

    explicit tracking number; falls back
    to the shipment's when omitted.

  • events (Array<ShipmentEvent>, nil) (defaults to: nil)

    pre-loaded rows; when provided we
    use them as-is and never query (the batched/list path). nil means query.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'app/services/shipment_event_status_summary.rb', line 69

def initialize(shipment = nil, tracking_number: nil, events: nil)
  @shipment = shipment
  # Scope by tracking_number, NOT by shipment_id. Offbook ST shipments
  # (cross-border back-order fulfillment from an alternate warehouse)
  # legitimately share a single tracking_number across multiple
  # Shipment rows — both shipments must surface the same event
  # history. ShipmentEvent stores tracking_number denormalised for
  # exactly this reason. Returns [] when tracking_number is blank.
  @events = if events
              events.sort_by { |e| e.occurred_at || Time.zone.at(0) }
            else
              tn = tracking_number.presence || shipment&.tracking_number
              if tn.present?
                ShipmentEvent.where(tracking_number: tn)
                             .to_a.sort_by { |e| e.occurred_at || Time.zone.at(0) }
              else
                []
              end
            end
end

Class Method Details

.for(shipment) ⇒ Badge? Also known as: for_shipment

Returns one badge, or nil when no relevant event yet.

Parameters:

Returns:

  • (Badge, nil)

    one badge, or nil when no relevant event yet



32
33
34
# File 'app/services/shipment_event_status_summary.rb', line 32

def self.for(shipment)
  new(shipment).call
end

.for_tracking_number(tracking_number) ⇒ Badge?

Compute the badge directly from a tracking number — used for ShipEngine
LTL freight, which has one PRO per delivery (no per-pallet Shipment row to
hang the events off) but writes to the same ShipmentEvent projection.
Reuses the identical status→icon/tooltip logic so LTL and parcel badges
are indistinguishable.

Parameters:

  • tracking_number (String, nil)

    e.g. the LTL PRO

Returns:



50
51
52
# File 'app/services/shipment_event_status_summary.rb', line 50

def self.for_tracking_number(tracking_number)
  new(nil, tracking_number: tracking_number).call
end

.from_events(events) ⇒ Badge?

Compute the badge from already-loaded ShipmentEvent rows — for list views
(e.g. the warehouse dashboard) that preload every event for the page in one
query and hand each row its slice, avoiding a per-row/per-shipment lookup.

Parameters:

  • events (Array<ShipmentEvent>, nil)

    the rows for one tracking number

Returns:



60
61
62
# File 'app/services/shipment_event_status_summary.rb', line 60

def self.from_events(events)
  new(events: Array(events)).call
end

Instance Method Details

#callBadge?

Returns:



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'app/services/shipment_event_status_summary.rb', line 91

def call
  delivered = events.reverse.find { |e| ShipmentEvent::DELIVERED_STATUS_CODES.include?(e.status_code) }
  return badge_for(:delivered, delivered, color: :success, icon: 'shipping-fast', tooltip_verb: 'Delivered') if delivered

  attempt = events.reverse.find { |e| e.status_code == 'AT' }
  return badge_for(:delivery_attempt, attempt, color: :warning, icon: 'shipping-fast', tooltip_verb: 'Delivery attempted') if attempt

  exception = events.reverse.find { |e| e.status_code == 'EX' }
  return badge_for(:exception, exception, color: :danger, icon: 'triangle-exclamation', tooltip_verb: 'Exception') if exception

  in_transit = events.reverse.find { |e| e.status_code == 'IT' }
  return badge_for(:in_transit, in_transit, color: :primary, icon: 'truck-fast', tooltip_verb: 'In transit') if in_transit

  accepted = events.reverse.find { |e| e.status_code == 'AC' }
  return badge_for(:accepted, accepted, color: :secondary, icon: 'box', tooltip_verb: 'Accepted by carrier') if accepted

  nil
end