spree_cm_commissioner 2.12.8 → 2.13.0
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/.env.example +10 -1
- data/Gemfile.lock +1 -1
- data/app/controllers/concerns/spree_cm_commissioner/anonymous_booking_guard.rb +22 -0
- data/app/controllers/concerns/spree_cm_commissioner/waiting_room_authorization.rb +3 -5
- data/app/controllers/spree/admin/orders/customer_details_controller_decorator.rb +53 -3
- data/app/controllers/spree/admin/stock_managements_controller.rb +20 -12
- data/app/controllers/spree/admin/system/waiting_room_controller.rb +1 -1
- data/app/controllers/spree/admin/system/waiting_room_sessions_controller.rb +1 -1
- data/app/controllers/spree/api/v2/platform/resource_controller_decorator.rb +48 -0
- data/app/controllers/spree/api/v2/storefront/draft_orders_controller.rb +8 -0
- data/app/controllers/spree/api/v2/storefront/intercity_taxi/draft_orders_controller.rb +7 -0
- data/app/controllers/spree/api/v2/storefront/payment_methods_controller.rb +9 -3
- data/app/controllers/spree/api/v2/storefront/queue_draft_orders_controller.rb +8 -0
- data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +10 -0
- data/app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb +5 -5
- data/app/controllers/spree/api/v2/tenant/draft_orders_controller.rb +9 -0
- data/app/controllers/spree/api/v2/tenant/intercity_taxi/draft_orders_controller.rb +7 -0
- data/app/controllers/spree/api/v2/tenant/payment_methods_controller.rb +12 -5
- data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +1 -3
- data/app/controllers/spree/api/v2/tenant/transit/draft_orders_controller.rb +10 -0
- data/app/controllers/spree/api/v2/tenant/waiting_room_sessions_controller.rb +5 -5
- data/app/controllers/spree_cm_commissioner/waiting_room_simulation_operations_controller.rb +39 -0
- data/app/finders/spree_cm_commissioner/payment_methods/find.rb +12 -9
- data/app/finders/spree_cm_commissioner/variants/find_by_option_value_ids.rb +26 -0
- data/app/interactors/spree_cm_commissioner/user_telegram_oauth_authenticator.rb +39 -8
- data/app/jobs/spree_cm_commissioner/stock_status_consolidation_job.rb +2 -4
- data/app/jobs/spree_cm_commissioner/waiting_room/admission/call_guests_chunk_job.rb +20 -0
- data/app/jobs/spree_cm_commissioner/waiting_room/advance_queue_job.rb +18 -0
- data/app/models/concerns/spree_cm_commissioner/firestore_connection.rb +4 -5
- data/app/models/concerns/spree_cm_commissioner/order_holdable.rb +22 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +10 -0
- data/app/models/concerns/spree_cm_commissioner/voting_session_timing_concern.rb +22 -4
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +1 -1
- data/app/models/spree_cm_commissioner/show.rb +1 -1
- data/app/models/spree_cm_commissioner/voting_session.rb +2 -0
- data/app/models/spree_cm_commissioner/waiting_room_session.rb +1 -1
- data/app/overrides/spree/admin/orders/_order_actions/actions.html.erb.deface +4 -4
- data/app/overrides/spree/admin/orders/_search/taxon_search_field.html.erb.deface +8 -0
- data/app/overrides/spree/admin/orders/customer_details/_form/view_profile_link.html.erb.deface +16 -0
- data/app/overrides/spree/admin/orders/customer_details/edit/reassign_customer_search.html.erb.deface +48 -0
- data/app/overrides/spree/admin/shared/sub_menu/_product/events_tab.html.erb.deface +4 -1
- data/app/overrides/spree/admin/taxons/_form/preview_checkbox.html.erb.deface +4 -0
- data/app/overrides/spree/admin/taxons/_form/vendor.html.erb.deface +10 -2
- data/app/overrides/spree/admin/users/_form/fields.html.erb.deface +5 -0
- data/app/overrides/spree/admin/users/_tabs/page_title.html.erb.deface +6 -0
- data/app/overrides/spree/admin/users/_tabs/tabs.html.erb.deface +1 -1
- data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +2 -1
- data/app/serializers/spree/v2/tenant/waiting_room_session_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/cart_serializer_decorator.rb +1 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/waiting_room_session_serializer.rb +1 -1
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/inventory/unstock_inventory.rb +3 -1
- data/app/services/spree_cm_commissioner/inventory_holds/convert.rb +25 -6
- data/app/services/spree_cm_commissioner/voting_sessions/publish_live_state.rb +1 -1
- data/app/services/spree_cm_commissioner/voting_sessions/publish_to_firestore.rb +15 -3
- data/app/services/spree_cm_commissioner/waiting_room/admission/call_guests.rb +279 -0
- data/app/services/spree_cm_commissioner/waiting_room/admission/call_guests_chunk.rb +109 -0
- data/app/services/spree_cm_commissioner/waiting_room/advance_queue.rb +120 -0
- data/app/services/spree_cm_commissioner/waiting_room/bypass_eligibility.rb +1 -1
- data/app/services/spree_cm_commissioner/waiting_room/heartbeat/sweep.rb +90 -0
- data/app/services/spree_cm_commissioner/waiting_room/positions/stamp.rb +186 -0
- data/app/services/spree_cm_commissioner/waiting_room/sessions/issue.rb +232 -0
- data/app/services/spree_cm_commissioner/waiting_room_lobby_metadata_fetcher.rb +2 -2
- data/app/views/spree/admin/events/_search_form.html.erb +4 -4
- data/app/views/spree/admin/shared/_taxon_tabs.html.erb +1 -1
- data/app/views/spree/admin/stock_managements/calendar.html.erb +72 -30
- data/app/views/spree/admin/stock_managements/index.html.erb +1 -1
- data/app/views/spree/admin/user_events/_new_user.html.erb +2 -1
- data/app/views/spree/admin/user_events/_users.html.erb +1 -1
- data/config/locales/en.yml +16 -0
- data/config/locales/km.yml +16 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20260909000000_rename_jwt_token_to_token_on_cm_waiting_room_sessions.rb +13 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +19 -7
- data/app/interactors/spree_cm_commissioner/waiting_guests_caller.rb +0 -259
- data/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb +0 -226
- data/app/jobs/spree_cm_commissioner/waiting_guests_caller_job.rb +0 -15
- data/app/jobs/spree_cm_commissioner/waiting_room/stamp_queue_positions_job.rb +0 -14
- data/app/services/spree_cm_commissioner/waiting_room/stamp_queue_positions.rb +0 -196
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b168ef103cb6aec0e7865fc4a752cc2367d526180fd6855bf21f6fba1cc72412
|
|
4
|
+
data.tar.gz: 05a6401d8ea20de2661c421471a16e0e8e1ace6882178a4f3f1cef0e9825d9b4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6cc975658385143deea2b8f1f8e7ac7b4d9ccfa2ce96054c1968a99fe4a6f3793a09585341bf389d2d9939f1097b4d9e867f62734a1d6aafaef0429628d2c875
|
|
7
|
+
data.tar.gz: 1baa1785ad8c59d5d3ae287518b19c4e680db6a796678096b31c9e18c7e5c1f856984e18ca19407e478e332a465ed4f0d86f0f3c269378e37e9567bb9e91e46b
|
data/.env.example
CHANGED
|
@@ -16,12 +16,21 @@ AWS_CF_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
|
|
|
16
16
|
CONTENT_HOST_URL=https://content.bookme.plus
|
|
17
17
|
CONTENT_CACHE_MAX_AGE=7200
|
|
18
18
|
|
|
19
|
-
WAITING_ROOM_SESSION_SIGNATURE="e6b2********************26e3"
|
|
20
19
|
WAITING_ROOM_SESSION_EXPIRE_DURATION_IN_SECOND=
|
|
20
|
+
WAITING_ROOM_SESSION_MAX_DURATION_IN_SECOND=600
|
|
21
|
+
WAITING_ROOM_BYPASS_HEADROOM_MULTIPLIER=1.1
|
|
21
22
|
WAITING_ROOM_MIN_SESSIONS_COUNT=5
|
|
22
23
|
WAITING_ROOM_DISABLED=no
|
|
23
24
|
WAITING_ROOM_POSITION_STAMP_LIMIT=1000
|
|
24
25
|
WAITING_ROOM_FIRESTORE_BATCH_SIZE=500
|
|
26
|
+
WAITING_ROOM_HEARTBEAT_TTL_SECONDS=180
|
|
27
|
+
WAITING_ROOM_SWEEP_LIMIT=1000
|
|
28
|
+
WAITING_ROOM_SIMULATION_TICK_KEY=
|
|
29
|
+
|
|
30
|
+
# See: app/services/spree_cm_commissioner/waiting_room/admission/call_guests.rb
|
|
31
|
+
WAITING_ROOM_MAX_CALLS_PER_RUN=200
|
|
32
|
+
WAITING_ROOM_CALL_CHUNK_SIZE=50
|
|
33
|
+
WAITING_ROOM_CALL_CLAIM_TTL_SECONDS=120
|
|
25
34
|
|
|
26
35
|
# Vattanac Bank
|
|
27
36
|
VATTANAC_AES_SECRET_KEY= ""
|
data/Gemfile.lock
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# Draft-order endpoints let a request through with no `spree_current_user` so guests can book —
|
|
3
|
+
# but only for products that opt into that via `Product#allow_anonymous_booking`. Include in any
|
|
4
|
+
# draft-order controller and call `ensure_anonymous_booking_allowed!(products)` before doing any
|
|
5
|
+
# writes, so a request for a login-required product fails fast with 401 (the app can then
|
|
6
|
+
# re-authenticate/open login and retry) instead of creating an order no signed-in account owns.
|
|
7
|
+
module AnonymousBookingGuard
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# products: [Array<Spree::Product>] every distinct product referenced by this request.
|
|
13
|
+
# Vacuously passes on an empty/unresolved list — an invalid variant/trip id is the underlying
|
|
14
|
+
# create service's error to raise, not this guard's.
|
|
15
|
+
def ensure_anonymous_booking_allowed!(products)
|
|
16
|
+
return if spree_current_user.present?
|
|
17
|
+
return if products.all?(&:allow_anonymous_booking)
|
|
18
|
+
|
|
19
|
+
raise Doorkeeper::Errors::DoorkeeperError
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -3,10 +3,6 @@ module SpreeCmCommissioner
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
included do
|
|
6
|
-
rescue_from JWT::ExpiredSignature, with: :handle_waiting_room_session_token_error
|
|
7
|
-
rescue_from JWT::VerificationError, with: :handle_waiting_room_session_token_error
|
|
8
|
-
rescue_from JWT::DecodeError, with: :handle_waiting_room_session_token_error
|
|
9
|
-
|
|
10
6
|
before_action :ensure_waiting_room_session_token
|
|
11
7
|
end
|
|
12
8
|
|
|
@@ -15,7 +11,9 @@ module SpreeCmCommissioner
|
|
|
15
11
|
return if request_from_client?
|
|
16
12
|
return unless required_waiting_room_session?
|
|
17
13
|
|
|
18
|
-
|
|
14
|
+
return if SpreeCmCommissioner::WaitingRoomSession.active.exists?(token: params[:waiting_room_session_token])
|
|
15
|
+
|
|
16
|
+
handle_waiting_room_session_token_error
|
|
19
17
|
end
|
|
20
18
|
|
|
21
19
|
def required_waiting_room_session?
|
|
@@ -2,11 +2,37 @@ module Spree
|
|
|
2
2
|
module Admin
|
|
3
3
|
module Orders
|
|
4
4
|
module CustomerDetailsControllerDecorator
|
|
5
|
+
# Override
|
|
6
|
+
def edit
|
|
7
|
+
super
|
|
8
|
+
search_for_customer
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Persists the customer found by `search_for_customer` right away — no separate
|
|
12
|
+
# "Update" click, no manual Guest Checkout toggle. `associate_user!` only backfills
|
|
13
|
+
# bill/ship address when the order doesn't already have one (`||=`), so an order's
|
|
14
|
+
# existing addresses are left as-is; this is a user swap only.
|
|
15
|
+
def assign_customer
|
|
16
|
+
user = tenant_user_scope.find_by(id: params[:user_id])
|
|
17
|
+
|
|
18
|
+
if user.nil?
|
|
19
|
+
flash[:error] = Spree.t(:user_not_found)
|
|
20
|
+
elsif user.id == @order.user_id
|
|
21
|
+
flash[:notice] = Spree.t('admin.orders.customer_details.user_already_assigned', email: user.email)
|
|
22
|
+
else
|
|
23
|
+
@order.associate_user!(user)
|
|
24
|
+
flash[:success] = Spree.t('admin.orders.customer_details.user_assigned', email: user.email)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
redirect_to spree.edit_admin_order_customer_path(@order)
|
|
28
|
+
end
|
|
29
|
+
|
|
5
30
|
# Override
|
|
6
31
|
def load_user
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
(
|
|
32
|
+
scope = tenant_user_scope
|
|
33
|
+
@user = scope.find_by(id: order_params[:user_id]) ||
|
|
34
|
+
(scope.find_by(email: order_params[:email]) if order_params[:email].present?) ||
|
|
35
|
+
(scope.find_by(phone_number: order_params[:phone_number]) if order_params[:phone_number].present?)
|
|
10
36
|
|
|
11
37
|
return if @user
|
|
12
38
|
|
|
@@ -21,6 +47,30 @@ module Spree
|
|
|
21
47
|
ship_address_attributes: permitted_address_attributes
|
|
22
48
|
)
|
|
23
49
|
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# Looks up a candidate to reassign this order to by whatever the admin typed —
|
|
54
|
+
# email, phone number, or login — the same lookup used for real user sign-in
|
|
55
|
+
# ([UserIdentity#find_user_by_login]), scoped to the order's own tenant so an
|
|
56
|
+
# admin can never attach a user from a different tenant. Only sets `@order.email`
|
|
57
|
+
# in memory (never saved here) — `assign_customer` is what actually persists it,
|
|
58
|
+
# once the admin confirms the match by clicking Assign.
|
|
59
|
+
def search_for_customer
|
|
60
|
+
@customer_search_query = params[:customer_search_query].presence
|
|
61
|
+
return if @customer_search_query.nil?
|
|
62
|
+
|
|
63
|
+
@customer_search_result = Spree.user_class.find_user_by_login(@customer_search_query, @order.tenant_id)
|
|
64
|
+
@order.email = @customer_search_result.email if @customer_search_result
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Same tenant boundary `find_user_by_login` uses — the actual save path (`update`)
|
|
68
|
+
# must enforce this itself rather than trust the search box above, since a
|
|
69
|
+
# crafted `order[user_id]`/`order[email]` could otherwise pull in a user from
|
|
70
|
+
# another tenant.
|
|
71
|
+
def tenant_user_scope
|
|
72
|
+
@order.tenant_id.present? ? Spree.user_class.by_tenant(@order.tenant_id) : Spree.user_class.by_non_tenant
|
|
73
|
+
end
|
|
24
74
|
end
|
|
25
75
|
end
|
|
26
76
|
end
|
|
@@ -40,24 +40,32 @@ module Spree
|
|
|
40
40
|
redirect_back fallback_location: admin_product_stock_managements_path(@product)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
# GET /products/:slug/stock_managements/calendar?year=&
|
|
43
|
+
# GET /products/:slug/stock_managements/calendar?year=&selected_option_value_ids[]=
|
|
44
44
|
def calendar
|
|
45
45
|
@year = params[:year].present? ? params[:year].to_i : Time.zone.today.year
|
|
46
46
|
|
|
47
47
|
from_date = Date.new(@year, 1, 1).beginning_of_year
|
|
48
48
|
to_date = Date.new(@year, 1, 1).end_of_year
|
|
49
49
|
|
|
50
|
-
@
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
@variant_kind_option_types = Spree::Variants::OptionTypesFinder.new(variant_ids: @product.variants.ids)
|
|
51
|
+
.execute.select(&:variant?)
|
|
52
|
+
@selected_option_value_ids = Array(params[:selected_option_value_ids]).map(&:to_i)
|
|
53
|
+
|
|
54
|
+
inventory_items = @product.inventory_items.includes(:variant, :prices).where(
|
|
55
|
+
inventory_date: from_date..to_date
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if @selected_option_value_ids.present?
|
|
59
|
+
variant_ids = SpreeCmCommissioner::Variants::FindByOptionValueIds.new(
|
|
60
|
+
product: @product, option_value_ids: @selected_option_value_ids
|
|
61
|
+
).execute.ids
|
|
62
|
+
inventory_items = inventory_items.where(variant_id: variant_ids)
|
|
63
|
+
@matched_variants_count = variant_ids.size
|
|
64
|
+
else
|
|
65
|
+
@matched_variants_count = @product.variants.count
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
@inventory_items = inventory_items.to_a
|
|
61
69
|
|
|
62
70
|
@cached_inventory_items = ::SpreeCmCommissioner::RedisStock::CachedInventoryItemsBuilder.new(@inventory_items)
|
|
63
71
|
.call
|
|
@@ -35,7 +35,7 @@ module Spree
|
|
|
35
35
|
|
|
36
36
|
def force_pull
|
|
37
37
|
SpreeCmCommissioner::WaitingRoomLatestSystemMetadataPullerJob.perform_now
|
|
38
|
-
SpreeCmCommissioner::
|
|
38
|
+
SpreeCmCommissioner::WaitingRoom::AdvanceQueueJob.perform_now
|
|
39
39
|
|
|
40
40
|
redirect_back fallback_location: admin_system_waiting_room_path
|
|
41
41
|
end
|
|
@@ -13,7 +13,7 @@ module Spree
|
|
|
13
13
|
|
|
14
14
|
# Kills only the sessions the admin checked (Gmail-style select + delete), instead of the
|
|
15
15
|
# room's whole active set. Firebase's active_sessions/full reflect this only on the next
|
|
16
|
-
#
|
|
16
|
+
# Admission::CallGuests run (they're a once-a-minute mirror, not live).
|
|
17
17
|
def kill
|
|
18
18
|
ids = Array(params[:ids])
|
|
19
19
|
count = SpreeCmCommissioner::WaitingRoomSession.kill_by_ids!(ids)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Platform
|
|
5
|
+
# Single reusable entry point for tenant-scoping any Platform API resource: pass
|
|
6
|
+
# `?tenant_id=X` on the request and the collection is narrowed to that tenant, without each
|
|
7
|
+
# controller having to wire up its own filter/param handling.
|
|
8
|
+
#
|
|
9
|
+
# Tri-state on the param itself:
|
|
10
|
+
# - absent: no scoping applied, collection flows through unrestricted (existing callers,
|
|
11
|
+
# eg. the products/users pickers in checkouts and private sales, are unaffected).
|
|
12
|
+
# - "0": scope to records with no tenant (tenant_id IS NULL) - most vendors today aren't
|
|
13
|
+
# attached to a tenant, so this is the common case for tenant-scoped search.
|
|
14
|
+
# - any other value: scope to that exact tenant_id.
|
|
15
|
+
#
|
|
16
|
+
# Default assumes the model owns a `tenant_id` column directly (true for Spree::User,
|
|
17
|
+
# Spree::Vendor, SpreeCmCommissioner::HomepageSection - see their `multi_tenant :tenant`
|
|
18
|
+
# declarations). A model related to tenant only indirectly should override
|
|
19
|
+
# `scope_by_tenant` in its own controller decorator.
|
|
20
|
+
module ResourceControllerDecorator
|
|
21
|
+
def scope
|
|
22
|
+
base_scope = super
|
|
23
|
+
|
|
24
|
+
# Keep the existing behavior when no tenant_id param is passed,
|
|
25
|
+
# so that existing controllers are unaffected.
|
|
26
|
+
return base_scope if params[:tenant_id].nil?
|
|
27
|
+
|
|
28
|
+
scope_by_tenant(base_scope, params[:tenant_id])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def scope_by_tenant(scope, tenant_id)
|
|
34
|
+
return scope unless scope.klass.column_names.include?('tenant_id')
|
|
35
|
+
|
|
36
|
+
if tenant_id.to_s == '0'
|
|
37
|
+
scope.where(tenant_id: nil)
|
|
38
|
+
else
|
|
39
|
+
scope.where(tenant_id: tenant_id)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Spree::Api::V2::Platform::ResourceController.prepend(Spree::Api::V2::Platform::ResourceControllerDecorator)
|
|
@@ -5,6 +5,9 @@ module Spree
|
|
|
5
5
|
class DraftOrdersController < ::Spree::Api::V2::ResourceController
|
|
6
6
|
# Creating the order acquires the inventory hold, so honor the load-test bypass here too.
|
|
7
7
|
include SpreeCmCommissioner::LoadTestBypassable
|
|
8
|
+
include SpreeCmCommissioner::AnonymousBookingGuard
|
|
9
|
+
|
|
10
|
+
before_action -> { ensure_anonymous_booking_allowed!(draft_order_products) }, only: :create
|
|
8
11
|
|
|
9
12
|
# Creates a generic draft order from a locally-assembled cart in a single request.
|
|
10
13
|
# Endpoint: POST /api/v2/storefront/draft_orders
|
|
@@ -46,6 +49,11 @@ module Spree
|
|
|
46
49
|
.fetch(:line_items, [])
|
|
47
50
|
end
|
|
48
51
|
|
|
52
|
+
def draft_order_products
|
|
53
|
+
variant_ids = draft_order_line_items.pluck(:variant_id).compact
|
|
54
|
+
Spree::Variant.where(id: variant_ids).includes(:product).map(&:product)
|
|
55
|
+
end
|
|
56
|
+
|
|
49
57
|
# override
|
|
50
58
|
def resource_serializer
|
|
51
59
|
Spree::Api::Dependencies.storefront_cart_serializer.constantize
|
|
@@ -6,8 +6,10 @@ module Spree
|
|
|
6
6
|
class DraftOrdersController < ::Spree::Api::V2::ResourceController
|
|
7
7
|
include Spree::Api::V2::Storefront::OrderConcern
|
|
8
8
|
include SpreeCmCommissioner::OrderConcern
|
|
9
|
+
include SpreeCmCommissioner::AnonymousBookingGuard
|
|
9
10
|
|
|
10
11
|
before_action :ensure_order, only: :update
|
|
12
|
+
before_action -> { ensure_anonymous_booking_allowed!(intercity_taxi_products) }, only: :create
|
|
11
13
|
|
|
12
14
|
def create
|
|
13
15
|
result = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
|
|
@@ -64,6 +66,11 @@ module Spree
|
|
|
64
66
|
|
|
65
67
|
private
|
|
66
68
|
|
|
69
|
+
def intercity_taxi_products
|
|
70
|
+
trip = SpreeCmCommissioner::Trip.find_by(id: params[:trip_id])
|
|
71
|
+
trip ? [trip.product] : []
|
|
72
|
+
end
|
|
73
|
+
|
|
67
74
|
def required_schema
|
|
68
75
|
if action_name == 'create'
|
|
69
76
|
SpreeCmCommissioner::IntercityTaxiDraftOrderSchema
|
|
@@ -3,9 +3,10 @@ module Spree
|
|
|
3
3
|
module V2
|
|
4
4
|
module Storefront
|
|
5
5
|
# Order & user independent catalog of the payment methods a vendor accepts.
|
|
6
|
-
# Takes vendor_id as a required query param
|
|
6
|
+
# Takes vendor_id as a required query param, and an optional payout param
|
|
7
|
+
# (caller passes Order#required_payway_payout? — see PaymentMethods::Find). See
|
|
7
8
|
# Spree::Api::V2::Tenant::PaymentMethodsController for the caching caveat
|
|
8
|
-
# that comes with
|
|
9
|
+
# that comes with vendor_id, and for what else this deliberately leaves out.
|
|
9
10
|
class PaymentMethodsController < Spree::Api::V2::ResourceController
|
|
10
11
|
# override
|
|
11
12
|
def index
|
|
@@ -19,7 +20,8 @@ module Spree
|
|
|
19
20
|
@collection ||= SpreeCmCommissioner::PaymentMethods::Find.new(
|
|
20
21
|
vendor: vendor,
|
|
21
22
|
store: current_store,
|
|
22
|
-
type: params[:type]
|
|
23
|
+
type: params[:type],
|
|
24
|
+
payout: payout?
|
|
23
25
|
).execute
|
|
24
26
|
end
|
|
25
27
|
|
|
@@ -33,6 +35,10 @@ module Spree
|
|
|
33
35
|
def vendor
|
|
34
36
|
@vendor ||= ::Spree::Vendor.active.find(params[:vendor_id])
|
|
35
37
|
end
|
|
38
|
+
|
|
39
|
+
def payout?
|
|
40
|
+
ActiveModel::Type::Boolean.new.cast(params[:payout])
|
|
41
|
+
end
|
|
36
42
|
end
|
|
37
43
|
end
|
|
38
44
|
end
|
|
@@ -6,6 +6,9 @@ module Spree
|
|
|
6
6
|
# Sets Current.load_test_bypass from the X-Request-Verify secret. The hold is acquired later
|
|
7
7
|
# in ProcessJob, so `create` passes this flag through options for the job to restore there.
|
|
8
8
|
include SpreeCmCommissioner::LoadTestBypassable
|
|
9
|
+
include SpreeCmCommissioner::AnonymousBookingGuard
|
|
10
|
+
|
|
11
|
+
before_action -> { ensure_anonymous_booking_allowed!(draft_order_products) }, only: :create
|
|
9
12
|
|
|
10
13
|
# Accepts a draft order request without building it: writes a queued Firestore status
|
|
11
14
|
# document and schedules the real build (SpreeCmCommissioner::DraftOrders::Create) on a
|
|
@@ -55,6 +58,11 @@ module Spree
|
|
|
55
58
|
.deep_symbolize_keys
|
|
56
59
|
.fetch(:line_items, [])
|
|
57
60
|
end
|
|
61
|
+
|
|
62
|
+
def draft_order_products
|
|
63
|
+
variant_ids = draft_order_line_items.pluck(:variant_id).compact
|
|
64
|
+
Spree::Variant.where(id: variant_ids).includes(:product).map(&:product)
|
|
65
|
+
end
|
|
58
66
|
end
|
|
59
67
|
end
|
|
60
68
|
end
|
|
@@ -4,6 +4,8 @@ module Spree
|
|
|
4
4
|
module Storefront
|
|
5
5
|
module Transit
|
|
6
6
|
class DraftOrdersController < ::Spree::Api::V2::ResourceController
|
|
7
|
+
include SpreeCmCommissioner::AnonymousBookingGuard
|
|
8
|
+
|
|
7
9
|
# Creates a draft transit order.
|
|
8
10
|
# Endpoint: POST /api/v2/storefront/transit/draft_orders
|
|
9
11
|
# Params:
|
|
@@ -15,6 +17,7 @@ module Spree
|
|
|
15
17
|
def create
|
|
16
18
|
@outbound_legs = params[:outbound_legs].is_a?(Array) && params[:outbound_legs].any? ? build_legs(:outbound, params[:outbound_legs]) : []
|
|
17
19
|
@inbound_legs = params[:inbound_legs].is_a?(Array) && params[:inbound_legs].any? ? build_legs(:inbound, params[:inbound_legs]) : []
|
|
20
|
+
ensure_anonymous_booking_allowed!(transit_leg_products)
|
|
18
21
|
|
|
19
22
|
result = SpreeCmCommissioner::TransitOrder::Create.call(
|
|
20
23
|
outbound_date: params[:outbound_date]&.to_date,
|
|
@@ -36,6 +39,13 @@ module Spree
|
|
|
36
39
|
SpreeCmCommissioner::Transit::LegsBuilderService.new(direction: direction, legs_params: legs_params).call
|
|
37
40
|
end
|
|
38
41
|
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def transit_leg_products
|
|
45
|
+
variant_ids = (@outbound_legs + @inbound_legs).flat_map(&:seat_selections).map(&:variant_id)
|
|
46
|
+
Spree::Variant.where(id: variant_ids).includes(:product).map(&:product)
|
|
47
|
+
end
|
|
48
|
+
|
|
39
49
|
# override
|
|
40
50
|
def resource_serializer
|
|
41
51
|
Spree::Api::Dependencies.storefront_cart_serializer.constantize
|
|
@@ -10,7 +10,7 @@ module Spree
|
|
|
10
10
|
skip_before_action :ensure_waiting_room_session_token
|
|
11
11
|
|
|
12
12
|
def create
|
|
13
|
-
|
|
13
|
+
result = SpreeCmCommissioner::WaitingRoom::Sessions::Issue.call(
|
|
14
14
|
tenant_id: nil,
|
|
15
15
|
remote_ip: request.remote_ip,
|
|
16
16
|
waiting_guest_firebase_doc_id: params[:waiting_guest_firebase_doc_id],
|
|
@@ -21,10 +21,10 @@ module Spree
|
|
|
21
21
|
user: spree_current_user
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
if
|
|
25
|
-
render_serialized_payload { serialize_resource(
|
|
24
|
+
if result.success?
|
|
25
|
+
render_serialized_payload { serialize_resource(result.value[:room_session]) }
|
|
26
26
|
else
|
|
27
|
-
render_error_payload(
|
|
27
|
+
render_error_payload(result.error)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -35,7 +35,7 @@ module Spree
|
|
|
35
35
|
private
|
|
36
36
|
|
|
37
37
|
def permitted_device_attributes
|
|
38
|
-
params.permit(*SpreeCmCommissioner::
|
|
38
|
+
params.permit(*SpreeCmCommissioner::WaitingRoom::Sessions::Issue::PERMITTED_DEVICE_ATTRIBUTES)
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
end
|
|
@@ -3,6 +3,10 @@ module Spree
|
|
|
3
3
|
module V2
|
|
4
4
|
module Tenant
|
|
5
5
|
class DraftOrdersController < BaseController
|
|
6
|
+
include SpreeCmCommissioner::AnonymousBookingGuard
|
|
7
|
+
|
|
8
|
+
before_action -> { ensure_anonymous_booking_allowed!(draft_order_products) }, only: :create
|
|
9
|
+
|
|
6
10
|
# Creates a generic draft order from a locally-assembled cart in a single request.
|
|
7
11
|
# Endpoint: POST /api/v2/tenant/draft_orders
|
|
8
12
|
# Params:
|
|
@@ -44,6 +48,11 @@ module Spree
|
|
|
44
48
|
.fetch(:line_items, [])
|
|
45
49
|
end
|
|
46
50
|
|
|
51
|
+
def draft_order_products
|
|
52
|
+
variant_ids = draft_order_line_items.pluck(:variant_id).compact
|
|
53
|
+
Spree::Variant.where(id: variant_ids).includes(:product).map(&:product)
|
|
54
|
+
end
|
|
55
|
+
|
|
47
56
|
# override
|
|
48
57
|
def resource_serializer
|
|
49
58
|
Spree::V2::Tenant::CartSerializer
|
|
@@ -6,8 +6,10 @@ module Spree
|
|
|
6
6
|
class DraftOrdersController < BaseController
|
|
7
7
|
include Spree::Api::V2::Storefront::OrderConcern
|
|
8
8
|
include SpreeCmCommissioner::OrderConcern
|
|
9
|
+
include SpreeCmCommissioner::AnonymousBookingGuard
|
|
9
10
|
|
|
10
11
|
before_action :ensure_order, only: :update
|
|
12
|
+
before_action -> { ensure_anonymous_booking_allowed!(intercity_taxi_products) }, only: :create
|
|
11
13
|
|
|
12
14
|
def create
|
|
13
15
|
result = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
|
|
@@ -65,6 +67,11 @@ module Spree
|
|
|
65
67
|
|
|
66
68
|
private
|
|
67
69
|
|
|
70
|
+
def intercity_taxi_products
|
|
71
|
+
trip = SpreeCmCommissioner::Trip.find_by(id: params[:trip_id])
|
|
72
|
+
trip ? [trip.product] : []
|
|
73
|
+
end
|
|
74
|
+
|
|
68
75
|
def required_schema
|
|
69
76
|
if action_name == 'create'
|
|
70
77
|
SpreeCmCommissioner::IntercityTaxiDraftOrderSchema
|
|
@@ -4,9 +4,11 @@ module Spree
|
|
|
4
4
|
module Tenant
|
|
5
5
|
# Order & user independent catalog of the payment methods a vendor accepts.
|
|
6
6
|
#
|
|
7
|
-
# vendor_id is a required query param
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# vendor_id is a required query param, and payout is an optional one (caller
|
|
8
|
+
# passes Order#required_payway_payout? — see PaymentMethods::Find). A vendor id
|
|
9
|
+
# is globally unique and belongs to exactly one tenant, so it is what identifies
|
|
10
|
+
# tenant, vendor and store here — the tenant itself comes from the Doorkeeper
|
|
11
|
+
# token, not the URL.
|
|
10
12
|
#
|
|
11
13
|
# IMPORTANT for caching (see SpreeCmCommissioner::ContentCachable): the CDN
|
|
12
14
|
# must include vendor_id in its cache key. If query strings are stripped,
|
|
@@ -14,7 +16,7 @@ module Spree
|
|
|
14
16
|
# are served. Moving to /vendors/:vendor_id/payment_methods would remove that
|
|
15
17
|
# dependency — tracked as a follow-up.
|
|
16
18
|
#
|
|
17
|
-
# For the per-order truth (store credit, early adopter
|
|
19
|
+
# For the full per-order truth (store credit, early adopter filtering)
|
|
18
20
|
# use CartPaymentMethodGroupsController instead.
|
|
19
21
|
class PaymentMethodsController < BaseController
|
|
20
22
|
# override
|
|
@@ -29,7 +31,8 @@ module Spree
|
|
|
29
31
|
@collection ||= SpreeCmCommissioner::PaymentMethods::Find.new(
|
|
30
32
|
vendor: vendor,
|
|
31
33
|
store: current_store,
|
|
32
|
-
type: params[:type]
|
|
34
|
+
type: params[:type],
|
|
35
|
+
payout: payout?
|
|
33
36
|
).execute
|
|
34
37
|
end
|
|
35
38
|
|
|
@@ -47,6 +50,10 @@ module Spree
|
|
|
47
50
|
::Spree::Vendor.active.find(params[:vendor_id])
|
|
48
51
|
end
|
|
49
52
|
end
|
|
53
|
+
|
|
54
|
+
def payout?
|
|
55
|
+
ActiveModel::Type::Boolean.new.cast(params[:payout])
|
|
56
|
+
end
|
|
50
57
|
end
|
|
51
58
|
end
|
|
52
59
|
end
|
|
@@ -13,9 +13,7 @@ module Spree
|
|
|
13
13
|
# override
|
|
14
14
|
def scope
|
|
15
15
|
scope = current_show.show_people
|
|
16
|
-
.
|
|
17
|
-
.distinct
|
|
18
|
-
.includes(:assignments, :show_person_images)
|
|
16
|
+
.includes(:show_person_images)
|
|
19
17
|
|
|
20
18
|
role = params[:role].presence
|
|
21
19
|
return scope if role.blank?
|
|
@@ -4,6 +4,8 @@ module Spree
|
|
|
4
4
|
module Tenant
|
|
5
5
|
module Transit
|
|
6
6
|
class DraftOrdersController < BaseController
|
|
7
|
+
include SpreeCmCommissioner::AnonymousBookingGuard
|
|
8
|
+
|
|
7
9
|
# Creates a draft transit order for tenant.
|
|
8
10
|
# Endpoint: POST /api/v2/tenant/transit/draft_orders
|
|
9
11
|
# Params:
|
|
@@ -15,6 +17,7 @@ module Spree
|
|
|
15
17
|
def create
|
|
16
18
|
@outbound_legs = params[:outbound_legs].is_a?(Array) && params[:outbound_legs].any? ? build_legs(:outbound, params[:outbound_legs]) : []
|
|
17
19
|
@inbound_legs = params[:inbound_legs].is_a?(Array) && params[:inbound_legs].any? ? build_legs(:inbound, params[:inbound_legs]) : []
|
|
20
|
+
ensure_anonymous_booking_allowed!(transit_leg_products)
|
|
18
21
|
|
|
19
22
|
result = SpreeCmCommissioner::TransitOrder::Create.call(
|
|
20
23
|
outbound_date: params[:outbound_date]&.to_date,
|
|
@@ -36,6 +39,13 @@ module Spree
|
|
|
36
39
|
SpreeCmCommissioner::Transit::LegsBuilderService.new(direction: direction, legs_params: legs_params).call
|
|
37
40
|
end
|
|
38
41
|
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def transit_leg_products
|
|
45
|
+
variant_ids = (@outbound_legs + @inbound_legs).flat_map(&:seat_selections).map(&:variant_id)
|
|
46
|
+
Spree::Variant.where(id: variant_ids).includes(:product).map(&:product)
|
|
47
|
+
end
|
|
48
|
+
|
|
39
49
|
# override
|
|
40
50
|
def resource_serializer
|
|
41
51
|
Spree::V2::Tenant::TransitCartSerializer
|
|
@@ -6,7 +6,7 @@ module Spree
|
|
|
6
6
|
skip_before_action :ensure_waiting_room_session_token
|
|
7
7
|
|
|
8
8
|
def create
|
|
9
|
-
|
|
9
|
+
result = SpreeCmCommissioner::WaitingRoom::Sessions::Issue.call(
|
|
10
10
|
tenant_id: MultiTenant.current_tenant_id,
|
|
11
11
|
remote_ip: request.remote_ip,
|
|
12
12
|
waiting_guest_firebase_doc_id: params[:waiting_guest_firebase_doc_id],
|
|
@@ -14,10 +14,10 @@ module Spree
|
|
|
14
14
|
device_attributes: permitted_device_attributes
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
if
|
|
18
|
-
render_serialized_payload { serialize_resource(
|
|
17
|
+
if result.success?
|
|
18
|
+
render_serialized_payload { serialize_resource(result.value[:room_session]) }
|
|
19
19
|
else
|
|
20
|
-
render_error_payload(
|
|
20
|
+
render_error_payload(result.error)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ module Spree
|
|
|
28
28
|
private
|
|
29
29
|
|
|
30
30
|
def permitted_device_attributes
|
|
31
|
-
params.permit(*SpreeCmCommissioner::
|
|
31
|
+
params.permit(*SpreeCmCommissioner::WaitingRoom::Sessions::Issue::PERMITTED_DEVICE_ATTRIBUTES)
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# The one server-side endpoint the standalone waiting_room_simulation/ CLI needs — everything
|
|
3
|
+
# else it does directly against Firestore with its own service account (seeding, heartbeats,
|
|
4
|
+
# reading frontier/lobby state), and session mint/renew already has a real public endpoint
|
|
5
|
+
# (WaitingRoomSessionsController). This exists only because WaitingRoom::AdvanceQueue is a Ruby
|
|
6
|
+
# class that only exists inside this Rails app, with no equivalent reachable over Firestore.
|
|
7
|
+
#
|
|
8
|
+
# A machine client with no session, so CSRF protection doesn't apply here the way it does for a
|
|
9
|
+
# browser-originated request — authenticated instead by a shared secret
|
|
10
|
+
# (WAITING_ROOM_SIMULATION_TICK_KEY), same shape as LoadTestBypassable's X-Request-Verify check.
|
|
11
|
+
# Unset key => the endpoint 404s entirely, never a default-open state.
|
|
12
|
+
class WaitingRoomSimulationOperationsController < ApplicationController
|
|
13
|
+
skip_before_action :verify_authenticity_token
|
|
14
|
+
before_action :verify_key!
|
|
15
|
+
|
|
16
|
+
# AdvanceQueue always runs its full sweep+stamp+call sequence — there's only ever one thing
|
|
17
|
+
# this endpoint can ask for, so the response always echoes both results. The response returns
|
|
18
|
+
# before CallGuestsChunkJob finishes writing Firestore — a caller polling for
|
|
19
|
+
# allow_to_enter_room_at needs to wait a beat.
|
|
20
|
+
def create
|
|
21
|
+
advance_result = SpreeCmCommissioner::WaitingRoom::AdvanceQueue.call!.value
|
|
22
|
+
|
|
23
|
+
render json: {
|
|
24
|
+
stamp: { stamped: advance_result[:stamped] },
|
|
25
|
+
call: { called: true, called_count: advance_result[:called_count] }
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def verify_key!
|
|
32
|
+
key = ENV.fetch('WAITING_ROOM_SIMULATION_TICK_KEY', nil)
|
|
33
|
+
return head(:not_found) if key.blank?
|
|
34
|
+
|
|
35
|
+
provided = request.headers.fetch('X-Waiting-Room-Simulation-Key', nil).to_s
|
|
36
|
+
head(:unauthorized) unless ActiveSupport::SecurityUtils.secure_compare(provided, key)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|