Class: SpreeCmCommissioner::TransitOrder::Create

Inherits:
Object
  • Object
show all
Includes:
Spree::ServiceModule::Base
Defined in:
app/services/spree_cm_commissioner/transit_order/create.rb

Instance Method Summary collapse

Instance Method Details

#build_guests_for!(line_item, seat_selections) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 124

def build_guests_for!(line_item, seat_selections)
  block_ids = seat_selections.flat_map(&:block_ids)

  if block_ids.any? && block_ids.size != line_item.quantity
    raise StandardError, "Number of blocks (#{block_ids.size}) does not match quantity (#{line_item.quantity})"
  end

  Array.new(line_item.quantity) do |index|
    line_item.guests.new(block_id: block_ids[index])
  end

  line_item
end

#build_line_items_for!(leg, order, date, connected_trip_id = nil) ⇒ Object



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
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 75

def build_line_items_for!(leg, order, date, connected_trip_id = nil)
  trip = SpreeCmCommissioner::Trip.find(leg.trip_id)
  from_date, to_date = resolve_leg_dates(trip, leg, date)

  leg.seat_selections.group_by(&:variant_id).map do |variant_id, seat_selections|
     = {
      direction: leg.direction,
      trip_id: leg.trip_id.to_s,
      boarding_trip_stop_id: leg.boarding_trip_stop_id&.to_s,
      drop_off_trip_stop_id: leg.drop_off_trip_stop_id&.to_s,
      connected_trip_id: connected_trip_id.presence&.to_s,
      service_origin_id: trip.service_origin_id.presence&.to_s
    }.compact

    line_item = order.line_items.new(
      product_type: :transit,
      from_date: from_date,
      to_date: to_date,
      variant_id: variant_id,
      quantity: seat_selections.sum(&:quantity),
      public_metadata: { is_open_dated: trip.open_dated? },
      private_metadata: 
    )

    build_guests_for!(line_item, seat_selections)
  end
end

#build_line_items_for_legs!(order:, legs:, initial_date:) ⇒ Object

Build line items for each leg, using the previous leg’s to_date as the next leg’s date, or the initial date for the first leg.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 57

def build_line_items_for_legs!(order:, legs:, initial_date:)
  all_line_items = []
  current_leg_date = initial_date

  # Use main_trip_id for connected trips (when legs have multiple trips)
  connected_trip_id = legs.size > 1 ? legs.first.main_trip_id&.to_s : nil

  legs.each do |leg|
    leg_line_items = build_line_items_for!(leg, order, current_leg_date, connected_trip_id)
    leg_line_items = insert_saved_guests_per_line_items_leg(leg_line_items)

    all_line_items.concat(leg_line_items)
    current_leg_date = leg_line_items.last.to_date.to_date if leg_line_items.any?
  end

  all_line_items
end

#calculate_line_item_duration!(date:, departure_time:, arrival_time:) ⇒ Object

Raises:

  • (StandardError)


149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 149

def calculate_line_item_duration!(date:, departure_time:, arrival_time:)
  raise StandardError, 'Departure or arrival time in trip stop is missing' if departure_time.blank? || arrival_time.blank?
  raise StandardError, 'Arrival time cannot be before departure time' if arrival_time < departure_time

  trip_duration_in_seconds = arrival_time - departure_time

  # The input `date` is in local time (e.g., 10-10-2022).
  # Combine it with the departure_time in Rails default timezone so that
  # the resulting from_date stays on the intended local calendar day when stored in UTC.
  from_date = Time.zone.local(date.year, date.month, date.day, departure_time.hour, departure_time.min, departure_time.sec)
  to_date = from_date + trip_duration_in_seconds.seconds

  [from_date, to_date]
end

#call(outbound_date:, inbound_date:, outbound_legs:, inbound_legs: [], user: nil, order_options: []) ⇒ Object

rubocop:disable Metrics/ParameterLists



19
20
21
22
23
24
25
26
27
28
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 19

def call(outbound_date:, inbound_date:, outbound_legs:, inbound_legs: [], user: nil, order_options: []) # rubocop:disable Metrics/ParameterLists
  return failure(nil, 'Outbound legs are missing') if outbound_legs.blank?

  begin
    order = create_order!(outbound_date, inbound_date, outbound_legs, inbound_legs, user, order_options)
    success(order: order)
  rescue StandardError => e
    failure(nil, e.message)
  end
end

#create_order!(outbound_date, inbound_date, outbound_legs, inbound_legs, user, order_options = []) ⇒ Object

rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/ParameterLists

Raises:

  • (StandardError)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 30

def create_order!(outbound_date, inbound_date, outbound_legs, inbound_legs, user, order_options = []) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/ParameterLists
  order = Spree::Order.new(state: 'cart', use_billing: true, user: user)

  outbound_line_items = build_line_items_for_legs!(order: order, legs: outbound_legs, initial_date: outbound_date)
  inbound_line_items = inbound_legs.blank? ? [] : build_line_items_for_legs!(order: order, legs: inbound_legs, initial_date: inbound_date)

  # For connected trips, sum of quantities will be (passengers * legs).
  # We need to ensure the number of passengers per leg matching between outbound and inbound.
  outbound_qty_per_leg = outbound_legs.any? ? outbound_line_items.sum(&:quantity) / outbound_legs.size : 0
  inbound_qty_per_leg = inbound_legs.any? ? inbound_line_items.sum(&:quantity) / inbound_legs.size : 0

  if inbound_line_items.any? && outbound_qty_per_leg != inbound_qty_per_leg
    raise StandardError, "Outbound & inbound passenger counts do not match (#{outbound_qty_per_leg} vs #{inbound_qty_per_leg})"
  end

  trip_ids = (outbound_legs.map(&:trip_id) + inbound_legs.map(&:trip_id)).uniq
  order.preload_trip_ids = trip_ids
  order.order_options = order_options if order_options.present?

  raise StandardError, order.errors.full_messages.to_sentence unless order.save

  order.update_with_updater!
  order
end

#insert_saved_guests_per_line_items_leg(line_items) ⇒ Object



138
139
140
141
142
143
144
145
146
147
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 138

def insert_saved_guests_per_line_items_leg(line_items)
  line_items.flat_map(&:guests).each_with_index do |guest, index|
    @saved_guests ||= []
    @saved_guests << SpreeCmCommissioner::SavedGuest.new if @saved_guests[index].blank?

    guest.saved_guest = @saved_guests[index]
  end

  line_items
end

#open_dated_leg_dates(trip, _leg, date) ⇒ Object



117
118
119
120
121
122
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 117

def open_dated_leg_dates(trip, _leg, date)
  validity_days = trip.product.open_dated_validity_days
  from_date = date.in_time_zone.beginning_of_day
  to_date = from_date + validity_days.days
  [from_date, to_date]
end

#resolve_leg_dates(trip, leg, date) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'app/services/spree_cm_commissioner/transit_order/create.rb', line 103

def resolve_leg_dates(trip, leg, date)
  return open_dated_leg_dates(trip, leg, date) if trip.open_dated?

  boarding_stop_id = leg.boarding_trip_stop_id.presence || trip.trip_stops.first&.id
  drop_off_stop_id = leg.drop_off_trip_stop_id.presence || trip.trip_stops.last&.id

  trip_stops = trip.trip_stops.where(id: [boarding_stop_id, drop_off_stop_id]).index_by(&:id)
  calculate_line_item_duration!(
    date: date,
    departure_time: trip_stops[boarding_stop_id]&.departure_time,
    arrival_time: trip_stops[drop_off_stop_id]&.arrival_time
  )
end