spree_cm_commissioner 2.5.1.pre.pre1 → 2.5.1.pre.pre2
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/admin/guests_controller.rb +5 -5
- data/app/controllers/spree/api/chatrace/check_ins_controller.rb +6 -4
- data/app/controllers/spree/api/chatrace/guests_controller.rb +5 -18
- data/app/controllers/spree/api/v2/operator/check_in_bulks_controller.rb +6 -5
- data/app/controllers/spree/api/v2/operator/check_in_sessions_controller.rb +32 -0
- data/app/controllers/spree/api/v2/operator/check_ins_controller.rb +5 -5
- data/app/controllers/spree/api/v2/operator/event_qrs_controller.rb +2 -0
- data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +1 -1
- data/app/controllers/spree/api/v2/storefront/self_check_in_controller.rb +7 -7
- data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +4 -4
- data/app/controllers/spree/api/v2/tenant/popular_route_places_controller.rb +60 -0
- data/app/controllers/spree/api/v2/tenant/routes_controller.rb +50 -0
- data/app/controllers/spree/api/v2/tenant/transit/draft_orders_controller.rb +46 -0
- data/app/controllers/spree/events/guests_controller.rb +9 -10
- 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/route_metrics/find_popular.rb +44 -0
- data/app/finders/spree_cm_commissioner/routes/find.rb +94 -0
- data/app/finders/spree_cm_commissioner/routes/find_popular.rb +20 -35
- data/app/interactors/spree_cm_commissioner/event_qr_generator.rb +2 -1
- data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +11 -4
- data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +10 -1
- data/app/interactors/spree_cm_commissioner/trip_clone_creator.rb +4 -3
- data/app/interactors/spree_cm_commissioner/trip_stops_creator.rb +2 -2
- data/app/jobs/spree_cm_commissioner/guests/preload_check_in_session_ids_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/route_metrics/decrease_trip_count_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_fulfilled_order_count_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_order_count_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_trip_count_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +2 -2
- data/app/models/concerns/spree_cm_commissioner/route_order_countable.rb +2 -2
- data/app/models/spree_cm_commissioner/check_in.rb +34 -7
- data/app/models/spree_cm_commissioner/check_in_session.rb +8 -3
- data/app/models/spree_cm_commissioner/guest.rb +60 -28
- data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +2 -2
- data/app/models/spree_cm_commissioner/place.rb +5 -8
- data/app/models/spree_cm_commissioner/pricing_actions/create_guest_adjustments.rb +52 -0
- data/app/models/spree_cm_commissioner/pricing_actions/create_line_item_adjustments.rb +6 -0
- data/app/models/spree_cm_commissioner/pricing_rule.rb +4 -0
- data/app/models/spree_cm_commissioner/pricing_rules/age_group.rb +45 -0
- data/app/models/spree_cm_commissioner/pricing_rules/nationality.rb +21 -2
- data/app/models/spree_cm_commissioner/pricing_rules/nationality_group.rb +41 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/route.rb +46 -5
- data/app/models/spree_cm_commissioner/route_metric.rb +21 -0
- data/app/models/spree_cm_commissioner/route_photo.rb +12 -0
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/trip.rb +8 -33
- data/app/models/spree_cm_commissioner/trip_stop.rb +16 -2
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +3 -1
- data/app/queries/spree_cm_commissioner/trip_query.rb +2 -2
- data/app/serializers/spree/v2/tenant/transit_cart_serializer.rb +11 -0
- data/app/serializers/spree_cm_commissioner/v2/operator/check_in_serializer.rb +1 -2
- data/app/serializers/spree_cm_commissioner/v2/operator/check_in_session_serializer.rb +9 -0
- data/app/serializers/spree_cm_commissioner/v2/operator/guest_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +2 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/route_serializer.rb +3 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/transit_line_item_serializer.rb +17 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +1 -1
- data/app/services/spree_cm_commissioner/check_ins/create_bulk.rb +65 -0
- data/app/services/spree_cm_commissioner/check_ins/destroy_bulk.rb +54 -0
- data/app/services/spree_cm_commissioner/guests/preload_check_in_session_ids.rb +22 -0
- data/app/services/spree_cm_commissioner/pricing_models/create_with_rule_groups.rb +56 -0
- data/app/services/spree_cm_commissioner/pricing_models/update_with_rule_groups.rb +69 -0
- data/app/services/spree_cm_commissioner/pricing_rules/build_params.rb +16 -7
- data/app/services/spree_cm_commissioner/route_metrics/decrease_trip_count.rb +31 -0
- data/app/services/spree_cm_commissioner/{routes/increment_fulfilled_order_count.rb → route_metrics/increase_fulfilled_order_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/{routes/increment_order_count.rb → route_metrics/increase_order_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/route_metrics/increase_trip_count.rb +31 -0
- data/app/services/spree_cm_commissioner/{routes/base_update_order_metrics.rb → route_metrics/update_route_metrics.rb} +11 -16
- data/app/services/spree_cm_commissioner/routes/create.rb +51 -0
- data/app/services/spree_cm_commissioner/routes/update.rb +25 -0
- data/app/{interactors/spree_cm_commissioner/transit/draft_order_creator.rb → services/spree_cm_commissioner/transit_order/create.rb} +13 -16
- data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +123 -0
- data/app/services/spree_cm_commissioner/trips/service_calendars/create_or_update.rb +54 -0
- data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +88 -0
- data/app/services/spree_cm_commissioner/trips/variants/create.rb +103 -0
- data/app/views/spree/transit/trip_stops/index.html.erb +4 -2
- data/app/views/spree_cm_commissioner/guest_mailer/send_ticket_to_guest.html.erb +0 -1
- data/config/initializers/spree_permitted_attributes.rb +11 -0
- data/config/locales/en.yml +2 -0
- data/config/locales/km.yml +2 -0
- data/config/routes.rb +8 -0
- data/db/migrate/20251224033103_migrate_cm_routes_to_cm_route_metrics.rb +17 -0
- data/db/migrate/20251224033910_migrate_cm_vendor_routes_to_cm_routes.rb +30 -0
- data/db/migrate/20251225100000_add_age_group_to_cm_guests.rb +6 -0
- data/db/migrate/20260105024742_add_type_to_cm_pricing_actions.rb +11 -0
- data/db/migrate/20260105072450_migrate_cm_trip_stops_to_support_trip_connection.rb +12 -0
- data/db/migrate/20260108101406_add_allow_booking_to_cm_trips.rb +5 -0
- data/db/migrate/20260121024645_add_nationality_group_to_cm_guests.rb +5 -0
- data/docs/pricing_model/age_group.md +40 -0
- data/docs/pricing_model/nationality_group.md +35 -0
- data/lib/spree_cm_commissioner/test_helper/factories/check_in_factory.rb +3 -1
- data/lib/spree_cm_commissioner/test_helper/factories/check_in_session_factory.rb +26 -0
- data/lib/spree_cm_commissioner/test_helper/factories/guest_factory.rb +4 -0
- data/lib/spree_cm_commissioner/test_helper/factories/pricing_action_factory.rb +4 -0
- data/lib/spree_cm_commissioner/test_helper/factories/pricing_rule_factory.rb +45 -1
- data/lib/spree_cm_commissioner/test_helper/factories/route_factory.rb +7 -6
- data/lib/spree_cm_commissioner/test_helper/factories/route_metric_factory.rb +12 -0
- data/lib/spree_cm_commissioner/test_helper/factories/route_photo_factory.rb +5 -0
- data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +4 -1
- data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +3 -1
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +2 -0
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +22 -0
- data/lib/spree_cm_commissioner/transit/route_stop.rb +61 -0
- data/lib/spree_cm_commissioner/transit/route_stop_collection.rb +175 -0
- data/lib/spree_cm_commissioner/transit/trip_form.rb +81 -0
- data/lib/spree_cm_commissioner/transit/trip_stop_form.rb +61 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +4 -0
- metadata +54 -20
- data/app/interactors/spree_cm_commissioner/check_in_bulk_creator.rb +0 -71
- data/app/interactors/spree_cm_commissioner/check_in_destroyer.rb +0 -43
- data/app/jobs/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/transit/route_order_count_incrementer_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_job.rb +0 -13
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_decrementer_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_incrementer_job.rb +0 -10
- data/app/models/concerns/spree_cm_commissioner/route_trip_count_callbacks.rb +0 -48
- data/app/models/spree_cm_commissioner/trip_connection.rb +0 -36
- data/app/models/spree_cm_commissioner/vendor_route.rb +0 -9
- data/app/services/spree_cm_commissioner/routes/decrement_previous_trip_count.rb +0 -30
- data/app/services/spree_cm_commissioner/routes/decrement_trip_count.rb +0 -33
- data/app/services/spree_cm_commissioner/routes/increment_trip_count.rb +0 -33
- data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +0 -6
|
@@ -8,6 +8,10 @@ module SpreeCmCommissioner
|
|
|
8
8
|
raise 'eligible? should be implemented in a sub-class of SpreeCmCommissioner::PricingRule'
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def guest_eligible?(_guest)
|
|
12
|
+
raise 'guest_eligible? should be implemented in a sub-class of SpreeCmCommissioner::PricingRule'
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def self.available_rule_types
|
|
12
16
|
SpreeCmCommissioner::PricingRules.constants.map(&:to_s)
|
|
13
17
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module PricingRules
|
|
3
|
+
class AgeGroup < SpreeCmCommissioner::PricingRule
|
|
4
|
+
AGE_GROUPS = {
|
|
5
|
+
child: [0, 12],
|
|
6
|
+
adult: [13, 99]
|
|
7
|
+
}.freeze
|
|
8
|
+
|
|
9
|
+
RULE_TYPES = %w[all any none].freeze
|
|
10
|
+
|
|
11
|
+
store_accessor :private_metadata, :rule_type, :age_groups
|
|
12
|
+
|
|
13
|
+
def eligible?(line_item)
|
|
14
|
+
return false if line_item.guests.blank?
|
|
15
|
+
|
|
16
|
+
case rule_type
|
|
17
|
+
when 'all'
|
|
18
|
+
line_item.guests.all? { |guest| guest_eligible?(guest) }
|
|
19
|
+
when 'any'
|
|
20
|
+
line_item.guests.any? { |guest| guest_eligible?(guest) }
|
|
21
|
+
when 'none'
|
|
22
|
+
line_item.guests.none? { |guest| guest_eligible?(guest) }
|
|
23
|
+
else
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def guest_eligible?(guest)
|
|
29
|
+
guest_age_group = resolve_guest_age_group(guest)
|
|
30
|
+
return false if guest_age_group.nil?
|
|
31
|
+
|
|
32
|
+
Array.wrap(age_groups).map(&:to_sym).include?(guest_age_group.to_sym)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def resolve_guest_age_group(guest)
|
|
38
|
+
return guest.age_group if guest.age_group.present?
|
|
39
|
+
return guest.saved_guest.age_group if guest.saved_guest&.age_group.present?
|
|
40
|
+
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -14,12 +14,31 @@ module SpreeCmCommissioner
|
|
|
14
14
|
return false if guest_nationalities.empty?
|
|
15
15
|
|
|
16
16
|
case rule_type
|
|
17
|
+
|
|
17
18
|
when 'all'
|
|
18
19
|
guest_nationalities.all? { |n| targets.include?(n) }
|
|
19
20
|
when 'any'
|
|
20
21
|
guest_nationalities.any? { |n| targets.include?(n) }
|
|
21
22
|
when 'none'
|
|
22
|
-
guest_nationalities.
|
|
23
|
+
guest_nationalities.any? { |n| targets.exclude?(n) }
|
|
24
|
+
else
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def guest_eligible?(guest)
|
|
30
|
+
return false if nationalities.blank?
|
|
31
|
+
|
|
32
|
+
targets = Array.wrap(nationalities).map(&:downcase)
|
|
33
|
+
guest_nat = guest_nationality(guest)
|
|
34
|
+
|
|
35
|
+
return false if guest_nat.blank?
|
|
36
|
+
|
|
37
|
+
case rule_type
|
|
38
|
+
when 'all', 'any'
|
|
39
|
+
targets.include?(guest_nat)
|
|
40
|
+
when 'none'
|
|
41
|
+
targets.exclude?(guest_nat)
|
|
23
42
|
else
|
|
24
43
|
false
|
|
25
44
|
end
|
|
@@ -28,7 +47,7 @@ module SpreeCmCommissioner
|
|
|
28
47
|
private
|
|
29
48
|
|
|
30
49
|
def guest_nationality(guest)
|
|
31
|
-
guest.nationality&.name.to_s.downcase
|
|
50
|
+
(guest.nationality&.name || guest.saved_guest&.nationality&.name).to_s.downcase
|
|
32
51
|
end
|
|
33
52
|
end
|
|
34
53
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module PricingRules
|
|
3
|
+
class NationalityGroup < SpreeCmCommissioner::PricingRule
|
|
4
|
+
RULE_TYPES = %w[all any none].freeze
|
|
5
|
+
|
|
6
|
+
store_accessor :private_metadata, :rule_type, :nationality_groups
|
|
7
|
+
|
|
8
|
+
def eligible?(line_item)
|
|
9
|
+
return false if nationality_groups.blank? || line_item.guests.blank?
|
|
10
|
+
|
|
11
|
+
case rule_type
|
|
12
|
+
when 'all'
|
|
13
|
+
line_item.guests.all? { |guest| guest_eligible?(guest) }
|
|
14
|
+
when 'any'
|
|
15
|
+
line_item.guests.any? { |guest| guest_eligible?(guest) }
|
|
16
|
+
when 'none'
|
|
17
|
+
line_item.guests.none? { |guest| guest_eligible?(guest) }
|
|
18
|
+
else
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def guest_eligible?(guest)
|
|
24
|
+
guest_nationality_group = resolve_guest_nationality_group(guest)
|
|
25
|
+
return false if guest_nationality_group.nil?
|
|
26
|
+
return false if nationality_groups.blank?
|
|
27
|
+
|
|
28
|
+
Array.wrap(nationality_groups).map(&:to_sym).include?(guest_nationality_group.to_sym)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def resolve_guest_nationality_group(guest)
|
|
34
|
+
return guest.nationality_group if guest.nationality_group.present?
|
|
35
|
+
return guest.saved_guest.nationality_group if guest.saved_guest&.nationality_group.present?
|
|
36
|
+
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -48,6 +48,7 @@ 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
|
|
51
52
|
|
|
52
53
|
base.belongs_to :event, class_name: 'Spree::Taxon', optional: true
|
|
53
54
|
|
|
@@ -1,12 +1,53 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class Route < Base
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
include SpreeCmCommissioner::RouteType
|
|
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
|
+
|
|
5
17
|
has_many :trips, inverse_of: :route
|
|
6
18
|
|
|
7
|
-
|
|
8
|
-
|
|
19
|
+
validates :route_name, presence: true
|
|
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?
|
|
9
49
|
|
|
10
|
-
|
|
50
|
+
route_stops.errors.each { |error| errors.add(:route_stops, error) }
|
|
51
|
+
end
|
|
11
52
|
end
|
|
12
53
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class RouteMetric < Base
|
|
3
|
+
include SpreeCmCommissioner::RouteType
|
|
4
|
+
|
|
5
|
+
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
6
|
+
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
7
|
+
|
|
8
|
+
validates :route_type, presence: true
|
|
9
|
+
validates :route_type, uniqueness: { scope: %i[origin_place_id destination_place_id] }
|
|
10
|
+
|
|
11
|
+
validate :origin_and_destination_cannot_be_the_same
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def origin_and_destination_cannot_be_the_same
|
|
16
|
+
return unless origin_place_id == destination_place_id
|
|
17
|
+
|
|
18
|
+
errors.add(:base, 'Origin and destination cannot be the same')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -77,6 +77,7 @@ module SpreeCmCommissioner
|
|
|
77
77
|
base.has_many :taxon_places, class_name: 'SpreeCmCommissioner::TaxonPlace', dependent: :destroy
|
|
78
78
|
base.has_many :places, through: :taxon_places
|
|
79
79
|
base.has_many :check_in_sessions, class_name: 'SpreeCmCommissioner::CheckInSession', foreign_key: :event_id, dependent: :destroy
|
|
80
|
+
base.has_many :active_check_in_sessions, -> { active }, class_name: 'SpreeCmCommissioner::CheckInSession', foreign_key: :event_id
|
|
80
81
|
|
|
81
82
|
base.has_many :import_orders, as: :importable, class_name: 'SpreeCmCommissioner::Imports::ImportOrder', dependent: :destroy
|
|
82
83
|
|
|
@@ -2,7 +2,6 @@ module SpreeCmCommissioner
|
|
|
2
2
|
class Trip < Base
|
|
3
3
|
include SpreeCmCommissioner::StoreMetadata
|
|
4
4
|
include SpreeCmCommissioner::RouteType
|
|
5
|
-
include SpreeCmCommissioner::RouteTripCountCallbacks
|
|
6
5
|
|
|
7
6
|
attr_accessor :hours, :minutes, :seconds
|
|
8
7
|
|
|
@@ -15,7 +14,9 @@ module SpreeCmCommissioner
|
|
|
15
14
|
|
|
16
15
|
# before create, duplicate seat layout from vehicle_type if present
|
|
17
16
|
before_validation :duplicate_seat_layout_from_vehicle, if: -> { seat_layout.nil? && vehicle_type&.seat_layout.present? }
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
after_create :increment_route_metric_trip_count
|
|
19
|
+
before_destroy :decrement_route_metric_trip_count
|
|
19
20
|
|
|
20
21
|
belongs_to :vendor, class_name: 'Spree::Vendor', inverse_of: :trips, optional: false
|
|
21
22
|
belongs_to :product, class_name: 'Spree::Product', inverse_of: :trip, optional: false
|
|
@@ -26,7 +27,7 @@ module SpreeCmCommissioner
|
|
|
26
27
|
|
|
27
28
|
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
28
29
|
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
29
|
-
belongs_to :route, inverse_of: :trips
|
|
30
|
+
belongs_to :route, class_name: 'SpreeCmCommissioner::Route', inverse_of: :trips, optional: true
|
|
30
31
|
|
|
31
32
|
has_many :trip_blazer_queries, as: :queryable, class_name: 'SpreeCmCommissioner::BlazerQueryable'
|
|
32
33
|
has_many :blazer_queries, through: :trip_blazer_queries, source: :blazer_query, class_name: 'Blazer::Query'
|
|
@@ -48,10 +49,6 @@ module SpreeCmCommissioner
|
|
|
48
49
|
self.whitelisted_ransackable_associations = %w[product vehicle vehicle_type]
|
|
49
50
|
self.whitelisted_ransackable_attributes = %w[origin_place_id destination_place_id]
|
|
50
51
|
|
|
51
|
-
def changed_route_attributes?
|
|
52
|
-
origin_place_id_changed? || destination_place_id_changed? || vendor_id_changed?
|
|
53
|
-
end
|
|
54
|
-
|
|
55
52
|
def convert_duration_to_seconds
|
|
56
53
|
return if hours.blank? && minutes.blank? && seconds.blank?
|
|
57
54
|
|
|
@@ -104,34 +101,12 @@ module SpreeCmCommissioner
|
|
|
104
101
|
)
|
|
105
102
|
end
|
|
106
103
|
|
|
107
|
-
|
|
108
|
-
|
|
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?
|
|
104
|
+
def increment_route_metric_trip_count
|
|
105
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseTripCountJob.perform_later(trip_id: id)
|
|
121
106
|
end
|
|
122
107
|
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
108
|
+
def decrement_route_metric_trip_count
|
|
109
|
+
SpreeCmCommissioner::RouteMetrics::DecreaseTripCountJob.perform_later(trip_id: id)
|
|
135
110
|
end
|
|
136
111
|
end
|
|
137
112
|
end
|
|
@@ -1,25 +1,39 @@
|
|
|
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 }
|
|
5
4
|
|
|
6
5
|
belongs_to :trip, class_name: 'SpreeCmCommissioner::Trip', optional: false
|
|
7
6
|
belongs_to :stop_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
8
7
|
belongs_to :location_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
9
8
|
|
|
9
|
+
belongs_to :board_to_trip, class_name: 'SpreeCmCommissioner::Trip', optional: true
|
|
10
|
+
|
|
10
11
|
before_validation :set_stop_name
|
|
12
|
+
|
|
11
13
|
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) }
|
|
12
18
|
|
|
13
19
|
def set_stop_name
|
|
14
20
|
self.stop_name = stop_place.name if stop_place.present?
|
|
15
21
|
end
|
|
16
22
|
|
|
17
23
|
def self.ransackable_attributes(_auth_object = nil)
|
|
18
|
-
%w[stop_name
|
|
24
|
+
%w[stop_name allow_boarding allow_drop_off]
|
|
19
25
|
end
|
|
20
26
|
|
|
21
27
|
def self.ransackable_associations(_auth_object = nil)
|
|
22
28
|
['stop_place']
|
|
23
29
|
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
|
|
24
38
|
end
|
|
25
39
|
end
|
|
@@ -82,7 +82,9 @@ 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 :
|
|
85
|
+
base.has_many :routes, class_name: 'SpreeCmCommissioner::Route', dependent: :destroy
|
|
86
|
+
base.has_many :popular_routes, -> { order(fulfilled_order_count: :desc, order_count: :desc) },
|
|
87
|
+
class_name: 'SpreeCmCommissioner::Route'
|
|
86
88
|
|
|
87
89
|
base.has_many :homepage_section_relatables,
|
|
88
90
|
as: :relatable,
|
|
@@ -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.allow_boarding = true
|
|
81
|
+
INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = cm_trips.id AND drop_off.allow_drop_off = true
|
|
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
|
|
@@ -2,8 +2,6 @@ module SpreeCmCommissioner
|
|
|
2
2
|
module V2
|
|
3
3
|
module Operator
|
|
4
4
|
class CheckInSerializer < BaseSerializer
|
|
5
|
-
set_type :check_in
|
|
6
|
-
|
|
7
5
|
attributes :guest_id,
|
|
8
6
|
:verification_state,
|
|
9
7
|
:check_in_type,
|
|
@@ -13,6 +11,7 @@ module SpreeCmCommissioner
|
|
|
13
11
|
:token
|
|
14
12
|
|
|
15
13
|
belongs_to :check_in_by, serializer: ::Spree::V2::Storefront::UserSerializer
|
|
14
|
+
belongs_to :check_in_session, serializer: ::SpreeCmCommissioner::V2::Operator::CheckInSessionSerializer
|
|
16
15
|
belongs_to :guest, serializer: ::SpreeCmCommissioner::V2::Operator::GuestSerializer
|
|
17
16
|
belongs_to :line_item, serializer: ::SpreeCmCommissioner::V2::Operator::LineItemSerializer
|
|
18
17
|
end
|
|
@@ -13,7 +13,7 @@ module SpreeCmCommissioner
|
|
|
13
13
|
|
|
14
14
|
belongs_to :occupation, serializer: Spree::V2::Storefront::TaxonSerializer
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
has_many :check_ins, serializer: SpreeCmCommissioner::V2::Operator::CheckInSerializer
|
|
17
17
|
has_one :line_item, serializer: SpreeCmCommissioner::V2::Operator::LineItemSerializer
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -27,7 +27,8 @@ module SpreeCmCommissioner
|
|
|
27
27
|
has_many :guest_dynamic_fields, serializer: SpreeCmCommissioner::V2::Storefront::GuestDynamicFieldSerializer
|
|
28
28
|
|
|
29
29
|
has_one :id_card, serializer: Spree::V2::Storefront::IdCardSerializer
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
has_many :check_ins, serializer: SpreeCmCommissioner::V2::Storefront::CheckInSerializer
|
|
31
32
|
|
|
32
33
|
# allowed_checkout updates frequently
|
|
33
34
|
cache_options store: nil
|
|
@@ -2,8 +2,10 @@ module SpreeCmCommissioner
|
|
|
2
2
|
module V2
|
|
3
3
|
module Storefront
|
|
4
4
|
class RouteSerializer < BaseSerializer
|
|
5
|
-
attributes :route_name
|
|
5
|
+
attributes :route_name, :route_type, :short_name
|
|
6
6
|
|
|
7
|
+
has_many :route_photos, serializer: ::SpreeCmCommissioner::V2::Storefront::AssetSerializer,
|
|
8
|
+
if: proc { |record, _params| record.respond_to?(:route_photos) }
|
|
7
9
|
has_one :origin_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
8
10
|
has_one :destination_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
9
11
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module V2
|
|
3
|
+
module Storefront
|
|
4
|
+
class TransitLineItemSerializer < BaseSerializer
|
|
5
|
+
# Nested-only API shape
|
|
6
|
+
attributes :from_date,
|
|
7
|
+
:to_date,
|
|
8
|
+
:trip_id,
|
|
9
|
+
:direction,
|
|
10
|
+
:boarding_trip_stop_id,
|
|
11
|
+
:drop_off_trip_stop_id
|
|
12
|
+
|
|
13
|
+
cache_options store: nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
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, :allow_boarding, :allow_drop_off, :stop_name, :arrival_time, :departure_time
|
|
6
6
|
|
|
7
7
|
has_one :stop_place, serializer: ::SpreeCmCommissioner::V2::Storefront::TripPlaceSerializer
|
|
8
8
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module CheckIns
|
|
3
|
+
class CreateBulk
|
|
4
|
+
prepend Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(check_ins_attributes:, check_in_by: nil, confirmed_at: nil)
|
|
7
|
+
return failure(:guest_ids_must_not_blank) if check_ins_attributes.blank?
|
|
8
|
+
|
|
9
|
+
confirmed_at ||= Time.current
|
|
10
|
+
guest_ids = check_ins_attributes.pluck(:guest_id)
|
|
11
|
+
indexed_guests = SpreeCmCommissioner::Guest.where(id: guest_ids).index_by(&:id)
|
|
12
|
+
|
|
13
|
+
check_ins = ActiveRecord::Base.transaction do
|
|
14
|
+
check_ins_attributes.map do |attrs|
|
|
15
|
+
guest = indexed_guests[attrs[:guest_id].to_i]
|
|
16
|
+
raise ActiveRecord::RecordNotFound, "Couldn't find Guest with 'id'=#{attrs[:guest_id]}" if guest.blank?
|
|
17
|
+
|
|
18
|
+
check_in = check_in!(guest, attrs, check_in_by, confirmed_at)
|
|
19
|
+
update_guest!(guest, attrs[:guest_attributes]) if attrs[:guest_attributes].present?
|
|
20
|
+
|
|
21
|
+
check_in
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
success(check_ins: check_ins)
|
|
26
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
27
|
+
failure(:invalid_record, e.record.errors.full_messages.join(', '))
|
|
28
|
+
rescue ActiveRecord::RecordNotFound => e
|
|
29
|
+
failure(:record_not_found, e.message)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def check_in!(guest, check_in_attributes, check_in_by, confirmed_at)
|
|
35
|
+
check_in = SpreeCmCommissioner::CheckIn.new(
|
|
36
|
+
guest: guest,
|
|
37
|
+
check_in_session_id: check_in_attributes[:check_in_session_id],
|
|
38
|
+
check_in_by: check_in_by,
|
|
39
|
+
checkinable: guest.event,
|
|
40
|
+
confirmed_at: check_in_attributes[:confirmed_at] || confirmed_at
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# save will validate attributes, uniqueness of check-in etc.
|
|
44
|
+
check_in.save!
|
|
45
|
+
|
|
46
|
+
# Track state change with session ID in the name for audit trail
|
|
47
|
+
name = check_in_attributes[:check_in_session_id].present? ? "guest_#{check_in_attributes[:check_in_session_id]}" : 'guest'
|
|
48
|
+
guest.state_changes.create!(
|
|
49
|
+
user: check_in_by,
|
|
50
|
+
previous_state: 'unchecked_in',
|
|
51
|
+
next_state: 'checked_in',
|
|
52
|
+
name: name
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
check_in
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def update_guest!(guest, guest_attributes)
|
|
59
|
+
return if guest_attributes.blank?
|
|
60
|
+
|
|
61
|
+
guest.update!(guest_attributes)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module CheckIns
|
|
3
|
+
class DestroyBulk
|
|
4
|
+
prepend Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(check_ins_attributes:, destroyed_by: nil)
|
|
7
|
+
return failure(:guest_ids_must_not_blank) if check_ins_attributes.blank?
|
|
8
|
+
|
|
9
|
+
check_ins = []
|
|
10
|
+
|
|
11
|
+
ActiveRecord::Base.transaction do
|
|
12
|
+
check_ins = check_ins_attributes.map do |attrs|
|
|
13
|
+
guest = SpreeCmCommissioner::Guest.find(attrs[:guest_id])
|
|
14
|
+
destroy_check_in!(guest, attrs, destroyed_by)
|
|
15
|
+
end.compact
|
|
16
|
+
|
|
17
|
+
success(check_ins: check_ins)
|
|
18
|
+
end
|
|
19
|
+
rescue ActiveRecord::RecordNotFound => e
|
|
20
|
+
failure(:record_not_found, e.message)
|
|
21
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
22
|
+
failure(:invalid_record, e.record.errors.full_messages.join(', '))
|
|
23
|
+
rescue StandardError => e
|
|
24
|
+
failure(:failed_to_destroy_check_ins, e.message)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def destroy_check_in!(guest, check_in_attributes, destroyed_by)
|
|
30
|
+
# Find the check-in to destroy
|
|
31
|
+
check_in = if check_in_attributes[:check_in_session_id].present?
|
|
32
|
+
guest.check_ins.find_by(check_in_session_id: check_in_attributes[:check_in_session_id])
|
|
33
|
+
else
|
|
34
|
+
guest.earliest_check_in
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
return nil if check_in.blank?
|
|
38
|
+
|
|
39
|
+
check_in.destroy!
|
|
40
|
+
|
|
41
|
+
# Track state change with session ID in the name for audit trail
|
|
42
|
+
name = check_in_attributes[:check_in_session_id].present? ? "guest_#{check_in_attributes[:check_in_session_id]}" : 'guest'
|
|
43
|
+
guest.state_changes.create!(
|
|
44
|
+
user: destroyed_by,
|
|
45
|
+
previous_state: 'checked_in',
|
|
46
|
+
next_state: 'unchecked_in',
|
|
47
|
+
name: name
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
check_in
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Guests
|
|
3
|
+
class PreloadCheckInSessionIds
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(event:)
|
|
7
|
+
return failure(nil, 'Event cannot be blank') if event.blank?
|
|
8
|
+
|
|
9
|
+
event.guests
|
|
10
|
+
.includes(event: { check_in_sessions: :check_in_rules })
|
|
11
|
+
.find_each(batch_size: 100) do |guest|
|
|
12
|
+
guest.preload_eligible_check_in_session_ids
|
|
13
|
+
guest.save(validate: false) # Skip validations for performance
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
success(true)
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
failure(nil, e.message)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|