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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Finder for Route records with common filtering options.
|
|
2
|
+
# Usage:
|
|
3
|
+
# finder = SpreeCmCommissioner::Routes::Find.new(vendor: vendor, params: params)
|
|
4
|
+
# finder = SpreeCmCommissioner::Routes::Find.new(tenant: tenant, params: params)
|
|
5
|
+
# finder.execute
|
|
6
|
+
# returns ActiveRecord::Relation of SpreeCmCommissioner::Route
|
|
7
|
+
|
|
8
|
+
module SpreeCmCommissioner
|
|
9
|
+
module Routes
|
|
10
|
+
class Find
|
|
11
|
+
def initialize(vendor: nil, tenant: nil, params: {})
|
|
12
|
+
@vendor = vendor
|
|
13
|
+
@tenant = tenant
|
|
14
|
+
@params = params || {}
|
|
15
|
+
|
|
16
|
+
@ids = Array(@params[:ids]).map(&:to_s)
|
|
17
|
+
@name = @params.dig(:filter, :name) || @params[:name]
|
|
18
|
+
@route_type = @params.dig(:filter, :route_type) || @params[:route_type]
|
|
19
|
+
@origin_place_id = @params.dig(:filter, :origin_place_id)
|
|
20
|
+
@destination_place_id = @params.dig(:filter, :destination_place_id)
|
|
21
|
+
@query = @params.dig(:filter, :query) || @params[:query]
|
|
22
|
+
@limit = (@params[:limit] || @params.dig(:page, :limit) || 50).to_i
|
|
23
|
+
@order = @params[:order] || 'created_at DESC'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def execute
|
|
27
|
+
results = base_scope
|
|
28
|
+
results = by_ids(results) if ids?
|
|
29
|
+
results = by_name(results) if name?
|
|
30
|
+
results = by_route_type(results) if route_type?
|
|
31
|
+
results = by_origin_place(results) if origin_place_id.present?
|
|
32
|
+
results = by_destination_place(results) if destination_place_id.present?
|
|
33
|
+
results = by_query(results) if query?
|
|
34
|
+
|
|
35
|
+
results.order(order_clause).limit(limit)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
attr_reader :vendor, :tenant, :params, :ids, :name, :route_type, :origin_place_id, :destination_place_id, :query, :limit, :order
|
|
41
|
+
|
|
42
|
+
def base_scope
|
|
43
|
+
scope = SpreeCmCommissioner::Route.includes(:origin_place, :destination_place, :route_photos)
|
|
44
|
+
scope = scope.where(vendor_id: vendor.id) if vendor.present?
|
|
45
|
+
scope = scope.where(tenant_id: tenant.id) if tenant.present?
|
|
46
|
+
scope
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ids?
|
|
50
|
+
ids.present?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def name?
|
|
54
|
+
name.present?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def route_type?
|
|
58
|
+
route_type.present?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def query?
|
|
62
|
+
query.present?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def by_ids(scope)
|
|
66
|
+
scope.where(id: ids)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def by_name(scope)
|
|
70
|
+
scope.where('cm_routes.route_name ILIKE ?', "%#{name}%")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def by_route_type(scope)
|
|
74
|
+
scope.where(route_type: route_type.to_s)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def by_origin_place(scope)
|
|
78
|
+
scope.where(origin_place_id: origin_place_id)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def by_destination_place(scope)
|
|
82
|
+
scope.where(destination_place_id: destination_place_id)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def by_query(scope)
|
|
86
|
+
scope.where('cm_routes.route_name ILIKE :q OR cm_routes.short_name ILIKE :q', q: "%#{query}%")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def order_clause
|
|
90
|
+
order
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -1,46 +1,31 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# @param route_type [Symbol, String] Optional. Filter by route type from associated trips (e.g., :ferry, :bus)
|
|
8
|
-
#
|
|
9
|
-
# @return [ActiveRecord::Relation<SpreeCmCommissioner::Route>] Routes with origin and destination places loaded,
|
|
10
|
-
# ordered from most to least popular
|
|
11
|
-
#
|
|
12
|
-
# @example Get all popular routes
|
|
13
|
-
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
14
|
-
# finder.execute # => Returns all routes sorted by fulfillment and order counts
|
|
15
|
-
#
|
|
16
|
-
# @example Get popular ferry routes
|
|
17
|
-
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
18
|
-
# finder.execute(route_type: :ferry) # => Returns ferry routes sorted by popularity
|
|
19
|
-
#
|
|
20
|
-
# @example Get popular bus routes
|
|
21
|
-
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
22
|
-
# finder.execute(route_type: :bus) # => Returns bus routes sorted by popularity
|
|
1
|
+
# Finder to get popular route metrics scoped to a vendor or tenant.
|
|
2
|
+
# Orders by fulfilled_order_count DESC, then order_count DESC.
|
|
3
|
+
# Usage:
|
|
4
|
+
# finder = SpreeCmCommissioner::Routes::FindPopular.new(vendor: vendor, route_type: :bus, limit: 10)
|
|
5
|
+
# finder.execute
|
|
23
6
|
|
|
24
7
|
module SpreeCmCommissioner
|
|
25
8
|
module Routes
|
|
26
9
|
class FindPopular
|
|
27
|
-
def
|
|
28
|
-
|
|
10
|
+
def initialize(vendor: nil, tenant: nil, route_type: nil, limit: 20)
|
|
11
|
+
@vendor = vendor
|
|
12
|
+
@tenant = tenant
|
|
13
|
+
@route_type = route_type
|
|
14
|
+
@limit = limit.to_i
|
|
29
15
|
end
|
|
30
16
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
17
|
+
def execute
|
|
18
|
+
scope = SpreeCmCommissioner::Route.includes(:origin_place, :destination_place, :route_photos)
|
|
19
|
+
scope = scope.where(vendor_id: vendor.id) if vendor.present?
|
|
20
|
+
scope = scope.where(tenant_id: tenant.id) if tenant.present?
|
|
21
|
+
scope = scope.where(route_type: route_type.to_sym) if route_type.present?
|
|
22
|
+
scope.order(fulfilled_order_count: :desc, order_count: :desc)
|
|
23
|
+
.limit(limit)
|
|
24
|
+
end
|
|
39
25
|
|
|
40
|
-
|
|
26
|
+
private
|
|
41
27
|
|
|
42
|
-
|
|
43
|
-
end
|
|
28
|
+
attr_reader :vendor, :tenant, :route_type, :limit
|
|
44
29
|
end
|
|
45
30
|
end
|
|
46
31
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class EventQrGenerator < BaseInteractor
|
|
3
|
-
delegate :operator, :event, :expired_in_mn, to: :context
|
|
3
|
+
delegate :operator, :event, :check_in_session, :expired_in_mn, to: :context
|
|
4
4
|
|
|
5
5
|
EventQrStruct = Struct.new(
|
|
6
6
|
:id,
|
|
@@ -18,6 +18,7 @@ module SpreeCmCommissioner
|
|
|
18
18
|
|
|
19
19
|
def jwt_token
|
|
20
20
|
payload = { event_id: event.id, operator_id: operator.id, exp: exp }
|
|
21
|
+
payload[:check_in_session_id] = check_in_session.id if check_in_session.present?
|
|
21
22
|
|
|
22
23
|
context.jwt_token ||= JWT.encode(
|
|
23
24
|
payload,
|
|
@@ -33,11 +33,18 @@ module SpreeCmCommissioner
|
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Returns dates for which inventory should be generated, based on calendar or default period.
|
|
36
37
|
def inventory_dates_for(variant)
|
|
37
|
-
|
|
38
|
-
end_date = Time.zone.today + pre_inventory_days_for(variant)
|
|
38
|
+
calendar = variant.product&.service_calendar
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
if calendar.present?
|
|
41
|
+
calendar.start_date.upto(calendar.end_date).select { |date| calendar.service_available?(date) }
|
|
42
|
+
else
|
|
43
|
+
start_date = Time.zone.tomorrow
|
|
44
|
+
end_date = Time.zone.today + pre_inventory_days_for(variant)
|
|
45
|
+
|
|
46
|
+
start_date.upto(end_date).to_a
|
|
47
|
+
end
|
|
41
48
|
end
|
|
42
49
|
|
|
43
50
|
def inventory_exist?(variant, inventory_date)
|
|
@@ -68,7 +75,7 @@ module SpreeCmCommissioner
|
|
|
68
75
|
def variants
|
|
69
76
|
scope = Spree::Variant.active.with_permanent_stock.where(is_master: false)
|
|
70
77
|
scope = scope.where(id: variant_ids) if variant_ids.present?
|
|
71
|
-
scope
|
|
78
|
+
scope.includes(product: :service_calendar)
|
|
72
79
|
end
|
|
73
80
|
end
|
|
74
81
|
end
|
|
@@ -8,7 +8,8 @@ module SpreeCmCommissioner
|
|
|
8
8
|
|
|
9
9
|
return context.fail!(message: Spree.t(:doesnt_track_inventory)) unless variant.track_inventory?
|
|
10
10
|
|
|
11
|
-
stock_location =
|
|
11
|
+
stock_location = resolve_stock_location(variant, stock_location_id)
|
|
12
|
+
|
|
12
13
|
stock_movement = stock_location.stock_movements.build(stock_movement_params)
|
|
13
14
|
stock_movement.stock_item = stock_location.set_up_stock_item(variant)
|
|
14
15
|
|
|
@@ -22,6 +23,14 @@ module SpreeCmCommissioner
|
|
|
22
23
|
|
|
23
24
|
private
|
|
24
25
|
|
|
26
|
+
def resolve_stock_location(variant, stock_location_id = nil)
|
|
27
|
+
if stock_location_id.present?
|
|
28
|
+
Spree::StockLocation.find(stock_location_id)
|
|
29
|
+
else
|
|
30
|
+
variant.vendor.stock_locations.first || variant.vendor.send(:create_stock_location)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
25
34
|
def adjust_inventory_items_async(variant_id, quantity)
|
|
26
35
|
args = { variant_id: variant_id, quantity: quantity }
|
|
27
36
|
CmAppLogger.log(label: "#{self.class.name}#adjust_inventory_items_async", data: args) do
|
|
@@ -91,9 +91,10 @@ module SpreeCmCommissioner
|
|
|
91
91
|
stop_params = params.dig(:trip_stops_attributes, index.to_s)
|
|
92
92
|
return unless stop_params
|
|
93
93
|
|
|
94
|
-
cloned_stop.departure_time
|
|
95
|
-
cloned_stop.arrival_time
|
|
96
|
-
cloned_stop.
|
|
94
|
+
cloned_stop.departure_time = parse_time(stop_params[:departure_time])
|
|
95
|
+
cloned_stop.arrival_time = parse_time(stop_params[:arrival_time])
|
|
96
|
+
cloned_stop.allow_boarding = stop_params[:allow_boarding] if stop_params.key?(:allow_boarding)
|
|
97
|
+
cloned_stop.allow_drop_off = stop_params[:allow_drop_off] if stop_params.key?(:allow_drop_off)
|
|
97
98
|
cloned_stop.location_place_id = stop_params[:location_place_id].presence
|
|
98
99
|
end
|
|
99
100
|
|
|
@@ -26,7 +26,7 @@ module SpreeCmCommissioner
|
|
|
26
26
|
trip: trip,
|
|
27
27
|
stop_place: origin_place,
|
|
28
28
|
location_place: origin_place,
|
|
29
|
-
|
|
29
|
+
allow_boarding: true
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
attributes[:departure_time] = departure_time
|
|
@@ -42,7 +42,7 @@ module SpreeCmCommissioner
|
|
|
42
42
|
trip: trip,
|
|
43
43
|
stop_place: destination_place,
|
|
44
44
|
location_place: destination_place,
|
|
45
|
-
|
|
45
|
+
allow_drop_off: true
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
attributes[:arrival_time] = departure_time + duration_seconds.seconds
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Guests
|
|
3
|
+
class PreloadCheckInSessionIdsJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options)
|
|
5
|
+
event = Spree::Taxon.find(options[:event_id])
|
|
6
|
+
SpreeCmCommissioner::Guests::PreloadCheckInSessionIds.call(event: event)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class DecreaseTripCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
trip = SpreeCmCommissioner::Trip.find(options[:trip_id])
|
|
6
|
+
SpreeCmCommissioner::RouteMetrics::DecreaseTripCount.call(trip: trip)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class IncreaseFulfilledOrderCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
order = Spree::Order.find(options[:order_id])
|
|
6
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseFulfilledOrderCount.call(order: order)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class IncreaseOrderCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
order = Spree::Order.find(options[:order_id])
|
|
6
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseOrderCount.call(order: order)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class IncreaseTripCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
trip = SpreeCmCommissioner::Trip.find(options[:trip_id])
|
|
6
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseTripCount.call(trip: trip)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module Stock
|
|
3
3
|
class PermanentInventoryItemsGeneratorJob < ApplicationUniqueJob
|
|
4
|
-
def perform
|
|
5
|
-
SpreeCmCommissioner::Stock::PermanentInventoryItemsGenerator.call
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
SpreeCmCommissioner::Stock::PermanentInventoryItemsGenerator.call(options)
|
|
6
6
|
end
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -7,13 +7,13 @@ module SpreeCmCommissioner
|
|
|
7
7
|
def increment_route_fulfilled_order_count
|
|
8
8
|
return unless has_trip_ids?
|
|
9
9
|
|
|
10
|
-
SpreeCmCommissioner::
|
|
10
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseFulfilledOrderCountJob.perform_later(order_id: id)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def increment_route_order_count
|
|
14
14
|
return unless has_trip_ids?
|
|
15
15
|
|
|
16
|
-
SpreeCmCommissioner::
|
|
16
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseOrderCountJob.perform_later(order_id: id)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -7,13 +7,15 @@ module SpreeCmCommissioner
|
|
|
7
7
|
enum entry_type: { normal: 0, VIP: 1 }
|
|
8
8
|
enum check_in_method: { manual: 0, scan: 1, sensor: 2, nfc: 3 }
|
|
9
9
|
|
|
10
|
-
belongs_to :guest, class_name: 'SpreeCmCommissioner::Guest'
|
|
11
|
-
belongs_to :check_in_by, class_name: 'Spree::User'
|
|
10
|
+
belongs_to :guest, class_name: 'SpreeCmCommissioner::Guest', optional: false
|
|
11
|
+
belongs_to :check_in_by, class_name: 'Spree::User', optional: true
|
|
12
|
+
|
|
12
13
|
has_one :line_item, class_name: 'Spree::LineItem', through: :guest
|
|
13
14
|
|
|
14
15
|
belongs_to :checkinable, polymorphic: true
|
|
15
16
|
belongs_to :check_in_session, class_name: 'SpreeCmCommissioner::CheckInSession', optional: true
|
|
16
|
-
|
|
17
|
+
|
|
18
|
+
before_validation :set_entry_type, on: :create, if: -> { guest.present? }
|
|
17
19
|
|
|
18
20
|
accepts_nested_attributes_for :guest, allow_destroy: true
|
|
19
21
|
|
|
@@ -21,11 +23,36 @@ module SpreeCmCommissioner
|
|
|
21
23
|
self.entry_type = guest.entry_type
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
# Ensure check_in_session is present when required and is eligible for the guest/ticket
|
|
27
|
+
validate :validate_check_in_session, if: -> { guest.present? }
|
|
28
|
+
|
|
24
29
|
validates :guest_id,
|
|
25
|
-
uniqueness: {
|
|
26
|
-
scope: :check_in_session_id,
|
|
27
|
-
message: I18n.t('spree.check_in.already_checked_in')
|
|
28
|
-
},
|
|
30
|
+
uniqueness: { scope: :check_in_session_id, message: I18n.t('spree.check_in.already_checked_in') },
|
|
29
31
|
if: -> { check_in_session_id.present? }
|
|
32
|
+
|
|
33
|
+
validates :guest_id,
|
|
34
|
+
uniqueness: { message: I18n.t('spree.check_in.already_checked_in') },
|
|
35
|
+
if: -> { check_in_session_id.blank? }
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def validate_check_in_session
|
|
40
|
+
# Case 1: Most common scenario for events without sessions We allow this case to check-in by default.
|
|
41
|
+
# - No check-in session provided from operator app (`check_in_session_id.blank?`)
|
|
42
|
+
# - Guest has no eligible sessions, most likely because the event has no sessions
|
|
43
|
+
return true if check_in_session_id.blank? && guest.eligible_check_in_session_ids.empty?
|
|
44
|
+
|
|
45
|
+
# Case 2: Events with sessions, but operator did not provide session id
|
|
46
|
+
if check_in_session_id.blank? && guest.eligible_check_in_session_ids.any?
|
|
47
|
+
return errors.add(:check_in_session_id, I18n.t('spree.check_in.check_in_session_required'))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Case 3: Provided session is not eligible for the guest (eg. special session for VIP tickets/guest only)
|
|
51
|
+
if check_in_session_id.present? && guest.eligible_check_in_session_ids.exclude?(check_in_session_id)
|
|
52
|
+
return errors.add(:check_in_session_id, I18n.t('spree.check_in.check_in_session_is_not_allowed_or_invalid'))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
true
|
|
56
|
+
end
|
|
30
57
|
end
|
|
31
58
|
end
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class CheckInSession < SpreeCmCommissioner::Base
|
|
3
|
+
acts_as_list scope: :event_id
|
|
4
|
+
|
|
3
5
|
belongs_to :event, class_name: 'Spree::Taxon', optional: false
|
|
6
|
+
|
|
4
7
|
has_many :check_in_rules, class_name: 'SpreeCmCommissioner::CheckInRule', dependent: :destroy
|
|
5
8
|
|
|
9
|
+
scope :active, -> { where(is_archived: false) }
|
|
10
|
+
|
|
6
11
|
accepts_nested_attributes_for :check_in_rules, allow_destroy: true
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
after_commit :preload_check_in_session_ids_to_event_guests
|
|
9
14
|
|
|
10
15
|
private
|
|
11
16
|
|
|
12
|
-
def
|
|
13
|
-
|
|
17
|
+
def preload_check_in_session_ids_to_event_guests
|
|
18
|
+
SpreeCmCommissioner::Guests::PreloadCheckInSessionIdsJob.perform_later(event_id: event.id)
|
|
14
19
|
end
|
|
15
20
|
end
|
|
16
21
|
end
|
|
@@ -3,13 +3,15 @@ module SpreeCmCommissioner
|
|
|
3
3
|
class Guest < SpreeCmCommissioner::Base # rubocop:disable Metrics/ClassLength
|
|
4
4
|
include SpreeCmCommissioner::KycBitwise
|
|
5
5
|
include SpreeCmCommissioner::PhoneNumberSanitizer
|
|
6
|
-
include SpreeCmCommissioner::StoreMetadata
|
|
7
6
|
include SpreeCmCommissioner::Integrations::IntegrationMappable
|
|
8
7
|
|
|
9
8
|
delegate :kyc, to: :line_item, allow_nil: true
|
|
10
9
|
delegate :allowed_upload_later?, to: :line_item, allow_nil: true
|
|
11
10
|
|
|
11
|
+
enum entry_type: { normal: 0, VIP: 1 }
|
|
12
12
|
enum gender: { :other => 0, :male => 1, :female => 2 }
|
|
13
|
+
enum age_group: { :adult => 0, :child => 1 }
|
|
14
|
+
enum nationality_group: { :local => 0, :foreigner => 1 }
|
|
13
15
|
enum social_contact_platform: {
|
|
14
16
|
:other => 0,
|
|
15
17
|
:telegram => 1,
|
|
@@ -36,12 +38,15 @@ module SpreeCmCommissioner
|
|
|
36
38
|
belongs_to :saved_guest, class_name: 'SpreeCmCommissioner::SavedGuest', optional: true
|
|
37
39
|
|
|
38
40
|
has_one :reserved_block, class_name: 'SpreeCmCommissioner::ReservedBlock', dependent: :nullify
|
|
41
|
+
has_one :earliest_check_in, -> { order(:confirmed_at) }, class_name: 'SpreeCmCommissioner::CheckIn'
|
|
39
42
|
|
|
40
|
-
scope :checked_ins, -> { joins(:
|
|
41
|
-
scope :no_show, -> { left_outer_joins(:
|
|
43
|
+
scope :checked_ins, -> { joins(:earliest_check_in) }
|
|
44
|
+
scope :no_show, -> { left_outer_joins(:earliest_check_in).where(cm_check_ins: { id: nil }) }
|
|
42
45
|
|
|
43
46
|
has_many :state_changes, as: :stateful, class_name: 'Spree::StateChange'
|
|
44
47
|
has_many :guest_dynamic_fields, dependent: :destroy, class_name: 'SpreeCmCommissioner::GuestDynamicField'
|
|
48
|
+
has_many :check_ins, class_name: 'SpreeCmCommissioner::CheckIn'
|
|
49
|
+
|
|
45
50
|
accepts_nested_attributes_for :guest_dynamic_fields, allow_destroy: true, reject_if: :reject_guest_dynamic_field?
|
|
46
51
|
|
|
47
52
|
belongs_to :event, class_name: 'Spree::Taxon'
|
|
@@ -56,6 +61,7 @@ module SpreeCmCommissioner
|
|
|
56
61
|
before_validation :assign_seat_number_with_bib, if: -> { bib_number.present? }
|
|
57
62
|
before_validation :assign_seat_number_with_block, if: -> { will_save_change_to_block_id? }
|
|
58
63
|
|
|
64
|
+
before_save :preload_eligible_check_in_session_ids, if: -> { new_record? }
|
|
59
65
|
before_create :generate_bib, if: -> { line_item.reload && variant.bib_pre_generation_on_create? }
|
|
60
66
|
after_create :preload_order_block_ids, if: -> { block_id.present? }
|
|
61
67
|
after_update :preload_order_block_ids, if: :saved_change_to_block_id?
|
|
@@ -300,6 +306,57 @@ module SpreeCmCommissioner
|
|
|
300
306
|
line_item.order.update(preload_block_ids: block_ids)
|
|
301
307
|
end
|
|
302
308
|
|
|
309
|
+
def eligible_check_in_session_ids
|
|
310
|
+
# Ensure public_metadata is a hash
|
|
311
|
+
self.public_metadata = JSON.parse(public_metadata) if public_metadata.is_a?(String)
|
|
312
|
+
|
|
313
|
+
# Return as array, even if key missing
|
|
314
|
+
Array(public_metadata['eligible_check_in_session_ids'])
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def eligible_check_in_session_ids=(ids)
|
|
318
|
+
# Ensure public_metadata is a hash
|
|
319
|
+
self.public_metadata = {} if public_metadata.nil?
|
|
320
|
+
self.public_metadata = JSON.parse(public_metadata) if public_metadata.is_a?(String)
|
|
321
|
+
|
|
322
|
+
public_metadata['eligible_check_in_session_ids'] = ids
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def preload_eligible_check_in_session_ids
|
|
326
|
+
event = self.event || line_item&.event
|
|
327
|
+
return if event.blank?
|
|
328
|
+
|
|
329
|
+
# only preload eligible and active check-in sessions, otherwise should not checkinable.
|
|
330
|
+
self.eligible_check_in_session_ids = event.active_check_in_sessions
|
|
331
|
+
.select { |session| eligible_for_session?(session) }
|
|
332
|
+
.map(&:id)
|
|
333
|
+
.uniq
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def eligible_for_session?(check_in_session)
|
|
337
|
+
rules = check_in_session.check_in_rules
|
|
338
|
+
return true if rules.empty?
|
|
339
|
+
|
|
340
|
+
rules.any? do |rule|
|
|
341
|
+
case rule.ruleable_type
|
|
342
|
+
when 'Spree::Product'
|
|
343
|
+
line_item.product_id == rule.ruleable_id
|
|
344
|
+
when 'Spree::Variant'
|
|
345
|
+
line_item.variant_id == rule.ruleable_id
|
|
346
|
+
when 'Spree::OptionValue'
|
|
347
|
+
line_item.variant.option_value_ids.include?(rule.ruleable_id)
|
|
348
|
+
else
|
|
349
|
+
false
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def eligible_check_in_sessions
|
|
355
|
+
return SpreeCmCommissioner::CheckInSession.none if eligible_check_in_session_ids.blank?
|
|
356
|
+
|
|
357
|
+
SpreeCmCommissioner::CheckInSession.where(id: eligible_check_in_session_ids)
|
|
358
|
+
end
|
|
359
|
+
|
|
303
360
|
def cancel_reserved_block!
|
|
304
361
|
reserved_block.update!(
|
|
305
362
|
status: :canceled,
|
|
@@ -400,31 +457,6 @@ module SpreeCmCommissioner
|
|
|
400
457
|
line_item&.product&.dynamic_fields&.during_check_in&.any?
|
|
401
458
|
end
|
|
402
459
|
|
|
403
|
-
def cm_check_in_rules
|
|
404
|
-
SpreeCmCommissioner::CheckInRule.where(ruleable_id: line_item.product_id)
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
def eligible_check_in_sessions
|
|
408
|
-
check_in_session_ids = []
|
|
409
|
-
|
|
410
|
-
cm_check_in_rules.includes(:check_in_session).find_each do |rule|
|
|
411
|
-
case rule.ruleable_type
|
|
412
|
-
when 'Spree::Product'
|
|
413
|
-
check_in_session_ids << rule.check_in_session_id if line_item.product_id == rule.ruleable_id
|
|
414
|
-
when 'Spree::Variant'
|
|
415
|
-
check_in_session_ids << rule.check_in_session_id if line_item.variant_id == rule.ruleable_id
|
|
416
|
-
when 'Spree::OptionValue'
|
|
417
|
-
check_in_session_ids << rule.check_in_session_id if line_item.variant.option_value_ids.include?(rule.ruleable_id)
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
SpreeCmCommissioner::CheckInSession.where(id: check_in_session_ids.uniq)
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
def can_check_in?(session)
|
|
425
|
-
eligible_check_in_sessions.include?(session)
|
|
426
|
-
end
|
|
427
|
-
|
|
428
460
|
def product_dynamic_fields
|
|
429
461
|
@product_dynamic_fields ||= line_item.product.dynamic_fields
|
|
430
462
|
end
|
|
@@ -74,9 +74,9 @@ module SpreeCmCommissioner
|
|
|
74
74
|
|
|
75
75
|
case dynamic_field.data_fill_stage.to_sym
|
|
76
76
|
when :pre_registration, :post_registration
|
|
77
|
-
guest.line_item.order.completed? && guest.
|
|
77
|
+
guest.line_item.order.completed? && guest.check_ins.empty?
|
|
78
78
|
when :during_check_in
|
|
79
|
-
guest.
|
|
79
|
+
guest.check_ins.exists?
|
|
80
80
|
else
|
|
81
81
|
false
|
|
82
82
|
end
|
|
@@ -21,15 +21,12 @@ 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 :route_metrics_as_origin, class_name: 'SpreeCmCommissioner::RouteMetric', foreign_key: :origin_place_id
|
|
25
|
+
has_many :route_metrics_as_destination, class_name: 'SpreeCmCommissioner::RouteMetric', foreign_key: :destination_place_id
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
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 }
|
|
27
|
+
# Scopes for route metric filtering
|
|
28
|
+
scope :with_route_metrics_as_origin, -> { joins(:route_metrics_as_origin).distinct }
|
|
29
|
+
scope :with_route_metrics_as_destination, -> { joins(:route_metrics_as_destination).distinct }
|
|
33
30
|
|
|
34
31
|
def self.ransackable_attributes(auth_object = nil)
|
|
35
32
|
super & %w[name code]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module PricingActions
|
|
3
|
+
class CreateGuestAdjustments < SpreeCmCommissioner::PricingAction
|
|
4
|
+
def perform(line_item)
|
|
5
|
+
return if pricing_rule_group.pricing_rules.blank?
|
|
6
|
+
return if line_item.guests.blank?
|
|
7
|
+
|
|
8
|
+
eligible_guests = find_eligible_guests(line_item)
|
|
9
|
+
return if eligible_guests.empty?
|
|
10
|
+
|
|
11
|
+
total_amount = eligible_guests.sum do |guest|
|
|
12
|
+
compute_guest_amount(line_item, guest)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
return if total_amount.zero?
|
|
16
|
+
|
|
17
|
+
create_adjustment(line_item, total_amount)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def find_eligible_guests(line_item)
|
|
23
|
+
line_item.guests.select do |guest|
|
|
24
|
+
guest_eligible_for_all_rules?(guest)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def guest_eligible_for_all_rules?(guest)
|
|
29
|
+
pricing_rule_group.pricing_rules.all? do |rule|
|
|
30
|
+
rule.respond_to?(:guest_eligible?) && rule.guest_eligible?(guest)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def compute_guest_amount(line_item, _guest)
|
|
35
|
+
per_guest_item = Struct.new(:amount, :currency, :quantity)
|
|
36
|
+
.new(line_item.amount_per_guest, line_item.currency, 1)
|
|
37
|
+
calculator&.compute(per_guest_item) || 0
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def create_adjustment(line_item, amount)
|
|
41
|
+
line_item.adjustments.create!(
|
|
42
|
+
amount: amount,
|
|
43
|
+
source: self,
|
|
44
|
+
adjustable: line_item,
|
|
45
|
+
order: line_item.order,
|
|
46
|
+
label: pricing_rule_group.pricing_model.name,
|
|
47
|
+
mandatory: true
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|