spree_cm_commissioner 2.5.1.pre.pre2 → 2.5.1.pre.pre3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/api/chatrace/guests_controller.rb +37 -20
- data/app/controllers/spree/api/v2/operator/check_in_bulks_controller.rb +9 -0
- data/app/controllers/spree/api/v2/storefront/account/qr_data_controller.rb +30 -0
- data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +1 -1
- data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +4 -4
- data/app/controllers/spree/transit/trips_controller.rb +3 -3
- data/app/finders/spree_cm_commissioner/places/find_with_route.rb +12 -12
- data/app/finders/spree_cm_commissioner/routes/find_popular.rb +35 -20
- data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +4 -11
- data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +1 -10
- data/app/{services/spree_cm_commissioner/transit_order/create.rb → interactors/spree_cm_commissioner/transit/draft_order_creator.rb} +16 -13
- data/app/interactors/spree_cm_commissioner/trip_clone_creator.rb +3 -4
- data/app/interactors/spree_cm_commissioner/trip_stops_creator.rb +2 -2
- data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +2 -2
- data/app/jobs/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/transit/route_order_count_incrementer_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_job.rb +13 -0
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_decrementer_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_incrementer_job.rb +10 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +16 -11
- data/app/models/concerns/spree_cm_commissioner/route_order_countable.rb +2 -2
- data/app/models/concerns/spree_cm_commissioner/route_trip_count_callbacks.rb +48 -0
- data/app/models/concerns/spree_cm_commissioner/user_identity.rb +0 -6
- data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +4 -0
- data/app/models/spree_cm_commissioner/guest.rb +4 -4
- data/app/models/spree_cm_commissioner/place.rb +8 -5
- data/app/models/spree_cm_commissioner/product_decorator.rb +0 -1
- data/app/models/spree_cm_commissioner/route.rb +5 -46
- data/app/models/spree_cm_commissioner/trip.rb +33 -8
- data/app/models/spree_cm_commissioner/trip_connection.rb +36 -0
- data/app/models/spree_cm_commissioner/trip_stop.rb +2 -16
- data/app/models/spree_cm_commissioner/user_decorator.rb +30 -18
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +1 -3
- data/app/models/spree_cm_commissioner/vendor_route.rb +9 -0
- data/app/queries/spree_cm_commissioner/guest_searcher_query.rb +25 -2
- data/app/queries/spree_cm_commissioner/trip_query.rb +2 -2
- data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +2 -1
- data/app/serializers/spree_cm_commissioner/v2/operator/guest_serializer.rb +1 -0
- data/app/serializers/spree_cm_commissioner/v2/operator/line_item_serializer.rb +2 -2
- data/app/serializers/spree_cm_commissioner/v2/operator/{line_item_order_serializer.rb → order_serializer.rb} +1 -2
- data/app/serializers/spree_cm_commissioner/v2/operator/user_serializer.rb +11 -0
- data/app/serializers/spree_cm_commissioner/v2/operator/variant_serializer.rb +9 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/route_serializer.rb +1 -3
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +1 -1
- data/app/services/spree_cm_commissioner/{route_metrics/update_route_metrics.rb → routes/base_update_order_metrics.rb} +16 -11
- data/app/services/spree_cm_commissioner/routes/decrement_previous_trip_count.rb +30 -0
- data/app/services/spree_cm_commissioner/routes/decrement_trip_count.rb +33 -0
- data/app/services/spree_cm_commissioner/{route_metrics/increase_fulfilled_order_count.rb → routes/increment_fulfilled_order_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/{route_metrics/increase_order_count.rb → routes/increment_order_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/routes/increment_trip_count.rb +33 -0
- data/app/services/spree_cm_commissioner/seeds/user_usernames.rb +86 -0
- data/app/services/spree_cm_commissioner/users/qr_data/extract_login.rb +18 -0
- data/app/services/spree_cm_commissioner/users/qr_data/generate.rb +14 -0
- data/app/services/spree_cm_commissioner/users/qr_data/invalidate.rb +19 -0
- data/app/services/spree_cm_commissioner/users/qr_data/verify.rb +33 -0
- data/app/services/spree_cm_commissioner/users/username/generate.rb +68 -0
- data/app/views/spree/transit/trip_stops/index.html.erb +2 -4
- data/app/views/spree_cm_commissioner/guest_mailer/send_ticket_to_guest.html.erb +1 -0
- data/config/initializers/spree_permitted_attributes.rb +1 -11
- data/config/routes.rb +2 -6
- data/db/migrate/20260126110528_seed_user_initial_usernames.rb +5 -0
- data/lib/spree_cm_commissioner/test_helper/factories/route_factory.rb +6 -7
- data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +6 -0
- data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +1 -4
- data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +1 -3
- data/lib/spree_cm_commissioner/test_helper/factories/vehicle_type_factory.rb +1 -1
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +1 -2
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +0 -22
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +0 -4
- metadata +29 -38
- data/app/controllers/spree/api/v2/tenant/popular_route_places_controller.rb +0 -60
- data/app/controllers/spree/api/v2/tenant/routes_controller.rb +0 -50
- data/app/controllers/spree/api/v2/tenant/transit/draft_orders_controller.rb +0 -46
- data/app/finders/spree_cm_commissioner/route_metrics/find_popular.rb +0 -44
- data/app/finders/spree_cm_commissioner/routes/find.rb +0 -94
- data/app/jobs/spree_cm_commissioner/route_metrics/decrease_trip_count_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_fulfilled_order_count_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_order_count_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_trip_count_job.rb +0 -10
- data/app/models/spree_cm_commissioner/route_metric.rb +0 -21
- data/app/models/spree_cm_commissioner/route_photo.rb +0 -12
- data/app/serializers/spree/v2/tenant/transit_cart_serializer.rb +0 -11
- data/app/serializers/spree_cm_commissioner/v2/storefront/transit_line_item_serializer.rb +0 -17
- data/app/services/spree_cm_commissioner/route_metrics/decrease_trip_count.rb +0 -31
- data/app/services/spree_cm_commissioner/route_metrics/increase_trip_count.rb +0 -31
- data/app/services/spree_cm_commissioner/routes/create.rb +0 -51
- data/app/services/spree_cm_commissioner/routes/update.rb +0 -25
- data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +0 -123
- data/app/services/spree_cm_commissioner/trips/service_calendars/create_or_update.rb +0 -54
- data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +0 -88
- data/app/services/spree_cm_commissioner/trips/variants/create.rb +0 -103
- data/db/migrate/20251224033103_migrate_cm_routes_to_cm_route_metrics.rb +0 -17
- data/db/migrate/20251224033910_migrate_cm_vendor_routes_to_cm_routes.rb +0 -30
- data/db/migrate/20260105072450_migrate_cm_trip_stops_to_support_trip_connection.rb +0 -12
- data/db/migrate/20260108101406_add_allow_booking_to_cm_trips.rb +0 -5
- data/lib/spree_cm_commissioner/test_helper/factories/route_metric_factory.rb +0 -12
- data/lib/spree_cm_commissioner/test_helper/factories/route_photo_factory.rb +0 -5
- data/lib/spree_cm_commissioner/transit/route_stop.rb +0 -61
- data/lib/spree_cm_commissioner/transit/route_stop_collection.rb +0 -175
- data/lib/spree_cm_commissioner/transit/trip_form.rb +0 -81
- data/lib/spree_cm_commissioner/transit/trip_stop_form.rb +0 -61
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteTripCountCallbacks
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
after_create :increase_trip_count
|
|
7
|
+
before_destroy :decrease_trip_count
|
|
8
|
+
|
|
9
|
+
# When route context may change, enqueue an async job after commit that
|
|
10
|
+
# reads the previous values from `previous_changes` to decrement counts
|
|
11
|
+
# on the old route/vendor. This avoids keeping in-memory state and keeps
|
|
12
|
+
# the Trip update fast.
|
|
13
|
+
after_commit :enqueue_decrement_previous_route_trip_counts, on: :update
|
|
14
|
+
|
|
15
|
+
after_commit :increment_new_route_trip_counts, on: :update, if: :changed_route_context?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def decrease_trip_count
|
|
21
|
+
SpreeCmCommissioner::Transit::RouteTripCountDecrementerJob.perform_now(trip_id: id)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def increase_trip_count
|
|
25
|
+
SpreeCmCommissioner::Transit::RouteTripCountIncrementerJob.perform_later(trip_id: id)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Read previous_changes to find prior route context and enqueue the job.
|
|
29
|
+
def enqueue_decrement_previous_route_trip_counts
|
|
30
|
+
prev_route_id = previous_changes['route_id']&.first
|
|
31
|
+
prev_origin_id = previous_changes['origin_place_id']&.first
|
|
32
|
+
prev_destination_id = previous_changes['destination_place_id']&.first
|
|
33
|
+
|
|
34
|
+
return unless prev_route_id.present? && prev_origin_id.present? && prev_destination_id.present?
|
|
35
|
+
|
|
36
|
+
SpreeCmCommissioner::Transit::RoutePreviousTripCountDecrementerJob.perform_later(
|
|
37
|
+
previous_route_id: prev_route_id
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Safely increment the trip_count for the trip's (new) route after update.
|
|
42
|
+
def increment_new_route_trip_counts
|
|
43
|
+
return if origin_place_id.blank? || destination_place_id.blank? || route_id.blank?
|
|
44
|
+
|
|
45
|
+
SpreeCmCommissioner::Transit::RouteTripCountIncrementerJob.perform_later(trip_id: id)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -51,12 +51,6 @@ module SpreeCmCommissioner
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# override:spree_auth_devise:app/models/spree/user.rb
|
|
55
|
-
def set_login
|
|
56
|
-
self.login ||= phone_number if phone_number
|
|
57
|
-
self.login ||= email if email
|
|
58
|
-
end
|
|
59
|
-
|
|
60
54
|
# override:devise:lib/devise/models/validatable.rb
|
|
61
55
|
def email_required?
|
|
62
56
|
require_login_identity_all_blank_for(:email)
|
|
@@ -62,7 +62,7 @@ module SpreeCmCommissioner
|
|
|
62
62
|
before_validation :assign_seat_number_with_block, if: -> { will_save_change_to_block_id? }
|
|
63
63
|
|
|
64
64
|
before_save :preload_eligible_check_in_session_ids, if: -> { new_record? }
|
|
65
|
-
before_create :
|
|
65
|
+
before_create :generate_bib_if_needed, if: -> { line_item.reload && variant.bib_pre_generation_on_create? }
|
|
66
66
|
after_create :preload_order_block_ids, if: -> { block_id.present? }
|
|
67
67
|
after_update :preload_order_block_ids, if: :saved_change_to_block_id?
|
|
68
68
|
before_destroy :cancel_reserved_block!, if: -> { reserved_block.present? }
|
|
@@ -241,14 +241,14 @@ module SpreeCmCommissioner
|
|
|
241
241
|
end
|
|
242
242
|
|
|
243
243
|
def bib_required?
|
|
244
|
-
line_item.variant.
|
|
244
|
+
line_item.variant.bib_required?
|
|
245
245
|
end
|
|
246
246
|
|
|
247
247
|
def bib_display_prefix?
|
|
248
248
|
line_item.variant.bib_display_prefix?
|
|
249
249
|
end
|
|
250
250
|
|
|
251
|
-
def
|
|
251
|
+
def generate_bib_if_needed
|
|
252
252
|
return if bib_prefix.present?
|
|
253
253
|
return unless bib_required?
|
|
254
254
|
return if event_id.blank?
|
|
@@ -264,7 +264,7 @@ module SpreeCmCommissioner
|
|
|
264
264
|
end
|
|
265
265
|
|
|
266
266
|
def generate_bib!
|
|
267
|
-
|
|
267
|
+
generate_bib_if_needed
|
|
268
268
|
save!
|
|
269
269
|
end
|
|
270
270
|
|
|
@@ -21,12 +21,15 @@ module SpreeCmCommissioner
|
|
|
21
21
|
has_many :taxon_places, class_name: 'SpreeCmCommissioner::TaxonPlace', dependent: :destroy
|
|
22
22
|
has_many :taxons, through: :taxon_places
|
|
23
23
|
|
|
24
|
-
has_many :
|
|
25
|
-
has_many :
|
|
24
|
+
has_many :routes_as_origin, class_name: 'SpreeCmCommissioner::Route', foreign_key: :origin_place_id
|
|
25
|
+
has_many :routes_as_destination, class_name: 'SpreeCmCommissioner::Route', foreign_key: :destination_place_id
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
has_many :trips_as_origin, through: :routes_as_origin, source: :trips
|
|
28
|
+
has_many :trips_as_destination, through: :routes_as_destination, source: :trips
|
|
29
|
+
|
|
30
|
+
# Scopes for route-based filtering
|
|
31
|
+
scope :with_routes_as_origin, -> { joins(:routes_as_origin).distinct }
|
|
32
|
+
scope :with_routes_as_destination, -> { joins(:routes_as_destination).distinct }
|
|
30
33
|
|
|
31
34
|
def self.ransackable_attributes(auth_object = nil)
|
|
32
35
|
super & %w[name code]
|
|
@@ -48,7 +48,6 @@ module SpreeCmCommissioner
|
|
|
48
48
|
base.accepts_nested_attributes_for :product_places, allow_destroy: true
|
|
49
49
|
|
|
50
50
|
base.has_one :trip, class_name: 'SpreeCmCommissioner::Trip', dependent: :destroy
|
|
51
|
-
base.has_one :service_calendar, as: :calendarable, class_name: 'SpreeCmCommissioner::ServiceCalendar', dependent: :destroy
|
|
52
51
|
|
|
53
52
|
base.belongs_to :event, class_name: 'Spree::Taxon', optional: true
|
|
54
53
|
|
|
@@ -1,53 +1,12 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class Route < Base
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
|
|
6
|
-
belongs_to :tenant, class_name: 'SpreeCmCommissioner::Tenant', optional: true
|
|
7
|
-
|
|
8
|
-
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
9
|
-
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
10
|
-
|
|
11
|
-
has_one :primary_photo, -> { order(position: :asc) },
|
|
12
|
-
class_name: 'SpreeCmCommissioner::RoutePhoto',
|
|
13
|
-
as: :viewable,
|
|
14
|
-
dependent: :destroy
|
|
15
|
-
has_many :route_photos, class_name: 'SpreeCmCommissioner::RoutePhoto', as: :viewable, dependent: :destroy
|
|
16
|
-
|
|
3
|
+
has_many :vendor_routes, class_name: 'SpreeCmCommissioner::VendorRoute', dependent: :destroy
|
|
4
|
+
has_many :vendors, through: :vendor_routes
|
|
17
5
|
has_many :trips, inverse_of: :route
|
|
18
6
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
validate :validate_route_stops
|
|
22
|
-
|
|
23
|
-
delegate :multi_leg?, to: :route_stops
|
|
24
|
-
|
|
25
|
-
def self.ransackable_attributes(_auth_object = nil)
|
|
26
|
-
%w[route_type short_name route_name]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def route_stops
|
|
30
|
-
Transit::RouteStopCollection.from_hash(read_attribute(:route_stops))
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def route_stops=(value)
|
|
34
|
-
if value.is_a?(Transit::RouteStopCollection)
|
|
35
|
-
@route_stops = value
|
|
36
|
-
self[:route_stops] = value.to_h
|
|
37
|
-
elsif value.is_a?(Array)
|
|
38
|
-
@route_stops = Transit::RouteStopCollection.from_hash(value)
|
|
39
|
-
self[:route_stops] = @route_stops.to_h
|
|
40
|
-
else
|
|
41
|
-
raise ArgumentError, 'route_stops must be an Array or RouteStopCollection'
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
private
|
|
46
|
-
|
|
47
|
-
def validate_route_stops
|
|
48
|
-
return if route_stops.valid?
|
|
7
|
+
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place'
|
|
8
|
+
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place'
|
|
49
9
|
|
|
50
|
-
|
|
51
|
-
end
|
|
10
|
+
validates :origin_place_id, uniqueness: { scope: :destination_place_id }
|
|
52
11
|
end
|
|
53
12
|
end
|
|
@@ -2,6 +2,7 @@ module SpreeCmCommissioner
|
|
|
2
2
|
class Trip < Base
|
|
3
3
|
include SpreeCmCommissioner::StoreMetadata
|
|
4
4
|
include SpreeCmCommissioner::RouteType
|
|
5
|
+
include SpreeCmCommissioner::RouteTripCountCallbacks
|
|
5
6
|
|
|
6
7
|
attr_accessor :hours, :minutes, :seconds
|
|
7
8
|
|
|
@@ -14,9 +15,7 @@ module SpreeCmCommissioner
|
|
|
14
15
|
|
|
15
16
|
# before create, duplicate seat layout from vehicle_type if present
|
|
16
17
|
before_validation :duplicate_seat_layout_from_vehicle, if: -> { seat_layout.nil? && vehicle_type&.seat_layout.present? }
|
|
17
|
-
|
|
18
|
-
after_create :increment_route_metric_trip_count
|
|
19
|
-
before_destroy :decrement_route_metric_trip_count
|
|
18
|
+
before_validation :assign_route_and_vendor_route, if: :changed_route_attributes?
|
|
20
19
|
|
|
21
20
|
belongs_to :vendor, class_name: 'Spree::Vendor', inverse_of: :trips, optional: false
|
|
22
21
|
belongs_to :product, class_name: 'Spree::Product', inverse_of: :trip, optional: false
|
|
@@ -27,7 +26,7 @@ module SpreeCmCommissioner
|
|
|
27
26
|
|
|
28
27
|
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
29
28
|
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
30
|
-
belongs_to :route,
|
|
29
|
+
belongs_to :route, inverse_of: :trips
|
|
31
30
|
|
|
32
31
|
has_many :trip_blazer_queries, as: :queryable, class_name: 'SpreeCmCommissioner::BlazerQueryable'
|
|
33
32
|
has_many :blazer_queries, through: :trip_blazer_queries, source: :blazer_query, class_name: 'Blazer::Query'
|
|
@@ -49,6 +48,10 @@ module SpreeCmCommissioner
|
|
|
49
48
|
self.whitelisted_ransackable_associations = %w[product vehicle vehicle_type]
|
|
50
49
|
self.whitelisted_ransackable_attributes = %w[origin_place_id destination_place_id]
|
|
51
50
|
|
|
51
|
+
def changed_route_attributes?
|
|
52
|
+
origin_place_id_changed? || destination_place_id_changed? || vendor_id_changed?
|
|
53
|
+
end
|
|
54
|
+
|
|
52
55
|
def convert_duration_to_seconds
|
|
53
56
|
return if hours.blank? && minutes.blank? && seconds.blank?
|
|
54
57
|
|
|
@@ -101,12 +104,34 @@ module SpreeCmCommissioner
|
|
|
101
104
|
)
|
|
102
105
|
end
|
|
103
106
|
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
# Check if any of the route context attributes are changing
|
|
108
|
+
def will_route_context_change?
|
|
109
|
+
# Returns true if any of the route context attributes will change on save.
|
|
110
|
+
will_save_change_to_route_id? ||
|
|
111
|
+
will_save_change_to_origin_place_id? ||
|
|
112
|
+
will_save_change_to_destination_place_id? ||
|
|
113
|
+
will_save_change_to_vendor_id?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def changed_route_context?
|
|
117
|
+
saved_change_to_route_id? ||
|
|
118
|
+
saved_change_to_origin_place_id? ||
|
|
119
|
+
saved_change_to_destination_place_id? ||
|
|
120
|
+
saved_change_to_vendor_id?
|
|
106
121
|
end
|
|
107
122
|
|
|
108
|
-
|
|
109
|
-
|
|
123
|
+
# Find or create the Route for this trip (by origin/destination) and
|
|
124
|
+
# assign it to the trip. Also populate a default route_name from the
|
|
125
|
+
# product when the route is new/blank.
|
|
126
|
+
def assign_route_and_vendor_route
|
|
127
|
+
route = SpreeCmCommissioner::Route.find_or_create_by!(
|
|
128
|
+
origin_place_id: origin_place_id,
|
|
129
|
+
destination_place_id: destination_place_id
|
|
130
|
+
)
|
|
131
|
+
route.route_name = product.name if route.route_name.blank?
|
|
132
|
+
route.save! if route.changed?
|
|
133
|
+
|
|
134
|
+
self.route_id = route.id
|
|
110
135
|
end
|
|
111
136
|
end
|
|
112
137
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class TripConnection < Base
|
|
3
|
+
belongs_to :from_trip, class_name: 'SpreeCmCommissioner::Trip'
|
|
4
|
+
belongs_to :to_trip, class_name: 'SpreeCmCommissioner::Trip'
|
|
5
|
+
|
|
6
|
+
validate :both_trip_cannot_be_the_same
|
|
7
|
+
before_validation :calculate_connection_time_minutes
|
|
8
|
+
validates :from_trip_id, uniqueness: { scope: :to_trip_id }
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def calculate_connection_time_minutes
|
|
13
|
+
return if from_trip.nil? || to_trip.nil?
|
|
14
|
+
|
|
15
|
+
arrival_seconds = from_trip.arrival_time.seconds_since_midnight
|
|
16
|
+
departure_seconds = to_trip.departure_time.seconds_since_midnight
|
|
17
|
+
|
|
18
|
+
layover_seconds = departure_seconds - arrival_seconds
|
|
19
|
+
layover_seconds += 86_400 if layover_seconds.negative?
|
|
20
|
+
|
|
21
|
+
connection_time_in_minutes = layover_seconds / 60
|
|
22
|
+
|
|
23
|
+
if connection_time_in_minutes.between?(0, 180)
|
|
24
|
+
self.connection_time_minutes = connection_time_in_minutes.to_i
|
|
25
|
+
else
|
|
26
|
+
errors.add(:base, 'Connection time must be less than 3 hours')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def both_trip_cannot_be_the_same
|
|
31
|
+
return unless from_trip.id == to_trip.id
|
|
32
|
+
|
|
33
|
+
errors.add(:base, 'Both trip cannot be the same')
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -1,39 +1,25 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class TripStop < Base
|
|
3
3
|
acts_as_list column: :sequence, scope: :trip_id
|
|
4
|
+
enum :stop_type, { boarding: 0, drop_off: 1 }
|
|
4
5
|
|
|
5
6
|
belongs_to :trip, class_name: 'SpreeCmCommissioner::Trip', optional: false
|
|
6
7
|
belongs_to :stop_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
7
8
|
belongs_to :location_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
8
9
|
|
|
9
|
-
belongs_to :board_to_trip, class_name: 'SpreeCmCommissioner::Trip', optional: true
|
|
10
|
-
|
|
11
10
|
before_validation :set_stop_name
|
|
12
|
-
|
|
13
11
|
validates :stop_place_id, uniqueness: { scope: :trip_id }
|
|
14
|
-
validate :boarding_trip_requires_allow_boarding
|
|
15
|
-
|
|
16
|
-
scope :boarding, -> { where(allow_boarding: true) }
|
|
17
|
-
scope :drop_off, -> { where(allow_drop_off: true) }
|
|
18
12
|
|
|
19
13
|
def set_stop_name
|
|
20
14
|
self.stop_name = stop_place.name if stop_place.present?
|
|
21
15
|
end
|
|
22
16
|
|
|
23
17
|
def self.ransackable_attributes(_auth_object = nil)
|
|
24
|
-
%w[stop_name
|
|
18
|
+
%w[stop_name stop_type]
|
|
25
19
|
end
|
|
26
20
|
|
|
27
21
|
def self.ransackable_associations(_auth_object = nil)
|
|
28
22
|
['stop_place']
|
|
29
23
|
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def boarding_trip_requires_allow_boarding
|
|
34
|
-
return unless board_to_trip_id.present? && !allow_boarding?
|
|
35
|
-
|
|
36
|
-
errors.add(:board_to_trip, 'requires allow_boarding to be true')
|
|
37
|
-
end
|
|
38
24
|
end
|
|
39
25
|
end
|
|
@@ -31,6 +31,8 @@ module SpreeCmCommissioner
|
|
|
31
31
|
|
|
32
32
|
base.has_many :wished_items, class_name: 'Spree::WishedItem', through: :wishlists
|
|
33
33
|
base.has_many :promotions, through: :promotion_rules, class_name: 'Spree::Promotion'
|
|
34
|
+
base.has_many :user_places, class_name: 'SpreeCmCommissioner::UserPlace'
|
|
35
|
+
base.has_many :places, through: :user_places, class_name: 'SpreeCmCommissioner::Place'
|
|
34
36
|
|
|
35
37
|
base.has_one :profile, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::UserProfile'
|
|
36
38
|
base.has_one :customer, class_name: 'SpreeCmCommissioner::Customer'
|
|
@@ -43,26 +45,27 @@ module SpreeCmCommissioner
|
|
|
43
45
|
base.scope :by_non_tenant, -> { where(tenant_id: nil) }
|
|
44
46
|
base.scope :vendor_users, -> (vendor_id) { joins(:role_users => :role).where(spree_roles: { vendor_id: vendor_id }).distinct }
|
|
45
47
|
|
|
46
|
-
base.whitelisted_ransackable_attributes = %w[email first_name last_name gender phone_number]
|
|
47
|
-
|
|
48
|
-
base.before_save :update_otp_enabled
|
|
49
|
-
base.attr_accessor :assigned_roles
|
|
50
|
-
base.validates_password_strength :password, if: -> { requires_strong_password? && password.present? }
|
|
51
|
-
|
|
52
|
-
# Store has_incomplete_guest_info in public_metadata for easy frontend access
|
|
53
48
|
base.store :public_metadata, accessors: [:has_incomplete_guest_info], coder: JSON
|
|
49
|
+
base.store :private_metadata, accessors: %i[otp_secret otp_required_for_login consumed_timestep], coder: JSON
|
|
50
|
+
|
|
54
51
|
base.store_public_metadata :primary_service_type, :string
|
|
52
|
+
base.store_public_metadata :qr_data_version, :integer, default: 1
|
|
53
|
+
base.store_public_metadata :qr_data_invalidated_at, :datetime
|
|
54
|
+
base.store_public_metadata :branch_ids, :array, default: []
|
|
55
|
+
|
|
56
|
+
# Validations
|
|
55
57
|
base.validates :primary_service_type,
|
|
56
58
|
inclusion: { in: SpreeCmCommissioner::ServiceType::SERVICE_TYPES.map(&:to_s) },
|
|
57
59
|
allow_nil: true
|
|
58
|
-
base.
|
|
59
|
-
|
|
60
|
-
base.devise :two_factor_authenticatable
|
|
61
|
-
|
|
62
|
-
base.store :private_metadata, accessors: %i[otp_secret otp_required_for_login consumed_timestep], coder: JSON
|
|
60
|
+
base.validates_password_strength :password, if: -> { requires_strong_password? && password.present? }
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
# Login/username affect QR data, so invalidate it on change.
|
|
63
|
+
base.before_validation :invalidate_qr_data, if: :will_save_change_to_login?
|
|
64
|
+
base.before_save :update_otp_enabled
|
|
65
65
|
|
|
66
|
+
base.attr_accessor :assigned_roles
|
|
67
|
+
base.whitelisted_ransackable_attributes = %w[email first_name last_name gender phone_number]
|
|
68
|
+
base.devise :two_factor_authenticatable
|
|
66
69
|
def base.end_users
|
|
67
70
|
joins('LEFT JOIN spree_vendor_users ON spree_users.id = spree_vendor_users.user_id').where(spree_vendor_users: { user_id: nil })
|
|
68
71
|
end
|
|
@@ -78,6 +81,10 @@ module SpreeCmCommissioner
|
|
|
78
81
|
end
|
|
79
82
|
end
|
|
80
83
|
|
|
84
|
+
def qr_data
|
|
85
|
+
SpreeCmCommissioner::Users::QrData::Generate.call(user: self).value
|
|
86
|
+
end
|
|
87
|
+
|
|
81
88
|
def otp_secret
|
|
82
89
|
private_metadata['otp_secret']
|
|
83
90
|
end
|
|
@@ -90,15 +97,15 @@ module SpreeCmCommissioner
|
|
|
90
97
|
private_metadata['consumed_timestep']
|
|
91
98
|
end
|
|
92
99
|
|
|
93
|
-
def self.define_user_places(base)
|
|
94
|
-
base.has_many :user_places, class_name: 'SpreeCmCommissioner::UserPlace'
|
|
95
|
-
base.has_many :places, through: :user_places, class_name: 'SpreeCmCommissioner::Place'
|
|
96
|
-
end
|
|
97
|
-
|
|
98
100
|
def permissions_for_vendor(vendor_id)
|
|
99
101
|
permissions.joins(role_permissions: :role).where(spree_roles: { vendor_id: vendor_id })
|
|
100
102
|
end
|
|
101
103
|
|
|
104
|
+
# override to auto-generate clean usernames (eg. JohnTraveler1 format)
|
|
105
|
+
def set_login
|
|
106
|
+
self.login ||= SpreeCmCommissioner::Users::Username::Generate.call(user: self)
|
|
107
|
+
end
|
|
108
|
+
|
|
102
109
|
# override
|
|
103
110
|
def has_spree_role?(role_name) # rubocop:disable Naming/PredicateName
|
|
104
111
|
spree_roles.non_vendor.exists?(name: role_name)
|
|
@@ -148,6 +155,11 @@ module SpreeCmCommissioner
|
|
|
148
155
|
phone_number
|
|
149
156
|
end
|
|
150
157
|
|
|
158
|
+
def invalidate_qr_data
|
|
159
|
+
self.qr_data_version += 1
|
|
160
|
+
self.qr_data_invalidated_at = Time.zone.now
|
|
161
|
+
end
|
|
162
|
+
|
|
151
163
|
def ensure_unique_database_delivery_method(attributes)
|
|
152
164
|
recipient = self
|
|
153
165
|
|
|
@@ -82,9 +82,7 @@ module SpreeCmCommissioner
|
|
|
82
82
|
base.has_many :subscriptions, through: :customers, class_name: 'SpreeCmCommissioner::Subscription'
|
|
83
83
|
base.has_many :subscription_orders, through: :subscriptions, class_name: 'Spree::Order', source: :orders
|
|
84
84
|
base.has_many :promotion_categories, class_name: 'Spree::PromotionCategory', foreign_key: :vendor_id, dependent: :destroy
|
|
85
|
-
base.has_many :
|
|
86
|
-
base.has_many :popular_routes, -> { order(fulfilled_order_count: :desc, order_count: :desc) },
|
|
87
|
-
class_name: 'SpreeCmCommissioner::Route'
|
|
85
|
+
base.has_many :vendor_routes, class_name: 'SpreeCmCommissioner::VendorRoute'
|
|
88
86
|
|
|
89
87
|
base.has_many :homepage_section_relatables,
|
|
90
88
|
as: :relatable,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class VendorRoute < Base
|
|
3
|
+
belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
|
|
4
|
+
belongs_to :route, class_name: 'SpreeCmCommissioner::Route', optional: false
|
|
5
|
+
|
|
6
|
+
has_many :trips, -> (vr) { where(vendor_id: vr.vendor_id, route_id: vr.route_id) }, class_name: 'SpreeCmCommissioner::Trip'
|
|
7
|
+
validates :vendor_id, uniqueness: { scope: :route_id }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -8,7 +8,7 @@ module SpreeCmCommissioner
|
|
|
8
8
|
|
|
9
9
|
def event_id = params[:event_id]
|
|
10
10
|
|
|
11
|
-
def call # rubocop:disable Metrics/PerceivedComplexity
|
|
11
|
+
def call # rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity
|
|
12
12
|
return SpreeCmCommissioner::Guest.none if event_id.blank?
|
|
13
13
|
|
|
14
14
|
if params[:qr_data].present? && order_qr_data?
|
|
@@ -17,6 +17,11 @@ module SpreeCmCommissioner
|
|
|
17
17
|
search_by_line_item_qr
|
|
18
18
|
elsif params[:qr_data].present? && guest_qr_data?
|
|
19
19
|
search_by_guest_qr
|
|
20
|
+
elsif params[:qr_data].present? && user_qr_data?
|
|
21
|
+
search_by_user_qr
|
|
22
|
+
# QR data provided but didn't match any format, return none
|
|
23
|
+
elsif params[:qr_data].present?
|
|
24
|
+
SpreeCmCommissioner::Guest.none
|
|
20
25
|
elsif params[:term].present?
|
|
21
26
|
search_by_term
|
|
22
27
|
elsif params[:ids].present?
|
|
@@ -45,6 +50,13 @@ module SpreeCmCommissioner
|
|
|
45
50
|
)
|
|
46
51
|
end
|
|
47
52
|
|
|
53
|
+
def search_by_user_qr
|
|
54
|
+
result = SpreeCmCommissioner::Users::QrData::Verify.call(qr_data: params[:qr_data])
|
|
55
|
+
return SpreeCmCommissioner::Guest.none if result.failure?
|
|
56
|
+
|
|
57
|
+
result.value.guests.complete_or_canceled.where(event_id: event_id)
|
|
58
|
+
end
|
|
59
|
+
|
|
48
60
|
def order_qr_data?
|
|
49
61
|
matches = construct_matches
|
|
50
62
|
matches&.size == 2
|
|
@@ -57,7 +69,18 @@ module SpreeCmCommissioner
|
|
|
57
69
|
|
|
58
70
|
def guest_qr_data?
|
|
59
71
|
matches = construct_matches
|
|
60
|
-
|
|
72
|
+
uuid_format = params[:qr_data].match?(/\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/)
|
|
73
|
+
matches.nil? && uuid_format
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# When login is present in the QR payload, we treat it as user QR data.
|
|
77
|
+
# This method only extracts login from qr_data; it does not verify or load any records, so it is fast.
|
|
78
|
+
# Once we have identified it as user QR data, we perform the actual lookup and verification elsewhere.
|
|
79
|
+
def user_qr_data?
|
|
80
|
+
@user_qr_data ||= begin
|
|
81
|
+
result = SpreeCmCommissioner::Users::QrData::ExtractLogin.call(qr_data: params[:qr_data])
|
|
82
|
+
result.success? ? result.value : nil
|
|
83
|
+
end.present?
|
|
61
84
|
end
|
|
62
85
|
|
|
63
86
|
def construct_matches
|
|
@@ -77,8 +77,8 @@ module SpreeCmCommissioner
|
|
|
77
77
|
|
|
78
78
|
scope
|
|
79
79
|
.joins(<<~SQL.squish)
|
|
80
|
-
INNER JOIN cm_trip_stops AS boarding ON boarding.trip_id = cm_trips.id AND boarding.
|
|
81
|
-
INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = cm_trips.id AND drop_off.
|
|
80
|
+
INNER JOIN cm_trip_stops AS boarding ON boarding.trip_id = cm_trips.id AND boarding.stop_type = 0
|
|
81
|
+
INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = cm_trips.id AND drop_off.stop_type = 1
|
|
82
82
|
INNER JOIN cm_places AS origin_places ON origin_places.id = cm_trips.origin_place_id
|
|
83
83
|
INNER JOIN cm_places AS dest_places ON dest_places.id = cm_trips.destination_place_id
|
|
84
84
|
INNER JOIN spree_variants AS master ON master.product_id = cm_trips.product_id AND master.is_master = true
|
|
@@ -5,7 +5,8 @@ module Spree
|
|
|
5
5
|
def self.prepended(base)
|
|
6
6
|
base.attributes :first_name, :last_name, :gender, :phone_number, :intel_phone_number,
|
|
7
7
|
:country_code, :otp_enabled, :otp_email, :otp_phone_number,
|
|
8
|
-
:confirm_pin_code_enabled, :tenant_id, :has_incomplete_guest_info
|
|
8
|
+
:confirm_pin_code_enabled, :tenant_id, :has_incomplete_guest_info,
|
|
9
|
+
:login, :qr_data, :qr_data_version, :qr_data_invalidated_at
|
|
9
10
|
|
|
10
11
|
base.has_one :profile, serializer: ::Spree::V2::Storefront::UserProfileSerializer
|
|
11
12
|
base.has_many :device_tokens, serializer: Spree::V2::Storefront::UserDeviceTokenSerializer
|
|
@@ -12,6 +12,7 @@ module SpreeCmCommissioner
|
|
|
12
12
|
attribute :require_kyc_field, &:require_kyc_field?
|
|
13
13
|
|
|
14
14
|
belongs_to :occupation, serializer: Spree::V2::Storefront::TaxonSerializer
|
|
15
|
+
belongs_to :user, serializer: SpreeCmCommissioner::V2::Operator::UserSerializer
|
|
15
16
|
|
|
16
17
|
has_many :check_ins, serializer: SpreeCmCommissioner::V2::Operator::CheckInSerializer
|
|
17
18
|
has_one :line_item, serializer: SpreeCmCommissioner::V2::Operator::LineItemSerializer
|
|
@@ -6,8 +6,8 @@ module SpreeCmCommissioner
|
|
|
6
6
|
|
|
7
7
|
attributes :number, :name, :quantity, :options_text, :qr_data, :kyc_fields, :available_social_contact_platforms
|
|
8
8
|
|
|
9
|
-
belongs_to :order, serializer: SpreeCmCommissioner::V2::Operator::
|
|
10
|
-
has_one
|
|
9
|
+
belongs_to :order, serializer: SpreeCmCommissioner::V2::Operator::OrderSerializer
|
|
10
|
+
has_one :variant, serializer: SpreeCmCommissioner::V2::Operator::VariantSerializer
|
|
11
11
|
has_many :guests, serializer: SpreeCmCommissioner::V2::Operator::GuestSerializer
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module V2
|
|
3
3
|
module Operator
|
|
4
|
-
class
|
|
4
|
+
class OrderSerializer < BaseSerializer
|
|
5
5
|
set_type :order
|
|
6
6
|
|
|
7
|
-
has_one :user, serializer: Spree::V2::Storefront::UserSerializer
|
|
8
7
|
belongs_to :billing_address,
|
|
9
8
|
id_method_name: :bill_address_id,
|
|
10
9
|
record_type: :address,
|
|
@@ -2,10 +2,8 @@ module SpreeCmCommissioner
|
|
|
2
2
|
module V2
|
|
3
3
|
module Storefront
|
|
4
4
|
class RouteSerializer < BaseSerializer
|
|
5
|
-
attributes :route_name
|
|
5
|
+
attributes :route_name
|
|
6
6
|
|
|
7
|
-
has_many :route_photos, serializer: ::SpreeCmCommissioner::V2::Storefront::AssetSerializer,
|
|
8
|
-
if: proc { |record, _params| record.respond_to?(:route_photos) }
|
|
9
7
|
has_one :origin_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
10
8
|
has_one :destination_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
11
9
|
end
|
|
@@ -2,7 +2,7 @@ module SpreeCmCommissioner
|
|
|
2
2
|
module V2
|
|
3
3
|
module Storefront
|
|
4
4
|
class TripStopSerializer < BaseSerializer
|
|
5
|
-
attributes :id, :
|
|
5
|
+
attributes :id, :stop_type, :stop_name, :arrival_time, :departure_time
|
|
6
6
|
|
|
7
7
|
has_one :stop_place, serializer: ::SpreeCmCommissioner::V2::Storefront::TripPlaceSerializer
|
|
8
8
|
end
|