spree_cm_commissioner 2.9.3.pre.pre1 → 2.9.4
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/concerns/spree_cm_commissioner/load_test_account_scope.rb +14 -21
- data/app/controllers/concerns/spree_cm_commissioner/load_test_bypassable.rb +29 -0
- data/app/controllers/spree/api/chatrace/guests_controller.rb +1 -1
- data/app/controllers/spree/api/v2/storefront/account/guest_dynamic_fields_controller.rb +19 -48
- data/app/controllers/spree/api/v2/storefront/account/mark_guest_info_complete_controller.rb +2 -2
- data/app/controllers/spree/api/v2/storefront/account/orders_controller_decorator.rb +5 -15
- data/app/controllers/spree/api/v2/storefront/account/saved_guests_controller.rb +65 -0
- data/app/controllers/spree/api/v2/storefront/draft_orders_controller.rb +3 -0
- data/app/controllers/spree/api/v2/storefront/guests_controller.rb +10 -9
- data/app/controllers/spree/api/v2/storefront/order_histories_controller.rb +0 -1
- data/app/controllers/spree/api/v2/tenant/account/saved_guests_controller.rb +70 -0
- data/app/controllers/spree/api/v2/tenant/guests_controller.rb +11 -21
- data/app/controllers/spree/api/v2/tenant/order_histories_controller.rb +0 -1
- data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb +3 -14
- data/app/jobs/spree_cm_commissioner/maintenance_tasks/guest_data_fill_stage_batch_job.rb +28 -0
- data/app/models/concerns/spree_cm_commissioner/guest_data_fill_stage_concern.rb +82 -0
- data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +4 -0
- data/app/models/concerns/spree_cm_commissioner/line_item_guests_concern.rb +1 -1
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +13 -31
- data/app/models/spree_cm_commissioner/current.rb +6 -3
- data/app/models/spree_cm_commissioner/dynamic_field.rb +12 -0
- data/app/models/spree_cm_commissioner/guest.rb +1 -144
- data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +43 -71
- data/app/models/spree_cm_commissioner/maintenance_tasks/guest_data_fill_stage.rb +21 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +26 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +0 -22
- data/app/models/spree_cm_commissioner/product_dynamic_field.rb +17 -0
- data/app/models/spree_cm_commissioner/request_guard.rb +19 -0
- data/app/models/spree_cm_commissioner/user_decorator.rb +26 -1
- data/app/queries/spree_cm_commissioner/event_chart_queries.rb +2 -2
- data/app/queries/spree_cm_commissioner/pie_chart_event_aggregator_queries.rb +2 -2
- data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +2 -2
- data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +3 -1
- data/app/serializers/spree/v2/tenant/saved_guest_serializer.rb +23 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/cart_serializer_decorator.rb +1 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +3 -6
- data/app/serializers/spree_cm_commissioner/v2/storefront/ticket_transfer_serializer.rb +2 -2
- data/app/services/spree_cm_commissioner/{cart/create_guest.rb → guests/create.rb} +12 -10
- data/app/services/spree_cm_commissioner/guests/refresh_data_fill_stage.rb +20 -0
- data/app/services/spree_cm_commissioner/guests/send_dynamic_field_notification.rb +15 -0
- data/app/services/spree_cm_commissioner/guests/update.rb +68 -0
- data/app/services/spree_cm_commissioner/inventory_holds/validate_limits.rb +8 -3
- data/app/services/spree_cm_commissioner/load_test/destroy_accounts.rb +7 -10
- data/app/services/spree_cm_commissioner/order/guests/update.rb +5 -8
- data/config/initializers/spree_permitted_attributes.rb +19 -0
- data/config/locales/en.yml +0 -5
- data/config/routes.rb +3 -1
- data/db/migrate/20260714002626_rename_data_fill_stage_phase_on_cm_guests.rb +5 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/tasks/recompute_guest_data_fill_stage.rake +19 -0
- metadata +18 -14
- data/app/controllers/spree/api/v2/storefront/saved_guests_controller.rb +0 -77
- data/app/interactors/spree_cm_commissioner/guest_dynamic_field_notification_sender.rb +0 -11
- data/app/interactors/spree_cm_commissioner/guest_dynamic_fields_manager.rb +0 -33
- data/app/serializers/spree/v2/storefront/order_serializer_decorator.rb +0 -63
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_minimal_serializer.rb +0 -30
- data/app/serializers/spree_cm_commissioner/v2/storefront/line_item_minimal_serializer.rb +0 -57
- data/app/serializers/spree_cm_commissioner/v2/storefront/ticket_transfer_minimal_serializer.rb +0 -17
- data/app/services/spree_cm_commissioner/update_guest_service.rb +0 -129
- data/app/services/spree_cm_commissioner/users/incomplete_guest_checker_service.rb +0 -34
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 73795eb4d207d3bf219b7c5913512b6b82ff71b71f5f9c5ea41acd18a016ade6
|
|
4
|
+
data.tar.gz: 44ed0c0b84771995b3f858d6d3db3e5c8feb4cb872a5d23981022837c2c8f847
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56fa3d866b2de53c7f58978403eb1a04c26a0da14a95d4a8447502dfed4123db703e84ef733385aa0b271774940031a362096a10b3606f0b6dda63dbcb3136ba
|
|
7
|
+
data.tar.gz: 8343f2ec6ff570e80a833272763431b699d68b926648f5b0a98a8929fd321aa4df7636461cbd2b94c8685dfaef162131440cdd5a02a3610ab1a87ea090d7a02d
|
data/Gemfile.lock
CHANGED
|
@@ -10,37 +10,30 @@ module SpreeCmCommissioner
|
|
|
10
10
|
raise NotImplementedError, "#{self.class} must implement #load_test_tenant"
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
# covering realistic load-test pool sizes.
|
|
15
|
-
EXPORT_SCAN_LIMIT = 2000
|
|
16
|
-
|
|
17
|
-
DEFAULT_EXPORT_PER_PAGE = 20
|
|
13
|
+
DEFAULT_EXPORT_PER_PAGE = 10
|
|
18
14
|
|
|
19
15
|
# Queried fresh from the DB every time, so it's always in sync with the current pool rather than
|
|
20
16
|
# a stashed file or session state.
|
|
21
17
|
#
|
|
22
|
-
# `load_test_account`
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# correctly regardless of that storage quirk.
|
|
18
|
+
# Filters the `load_test_account` flag in SQL via the model's `load_test_accounts` scope (see
|
|
19
|
+
# UserDecorator), so only the tenant's load-test accounts are considered — never the whole users
|
|
20
|
+
# table. Unlike the old `order(:id).limit(2000).select(&:...)` approach, there's no scan cap:
|
|
21
|
+
# newly created accounts (highest ids, ordered first below) always show up even on tenants with
|
|
22
|
+
# far more than 2,000 users.
|
|
28
23
|
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# instead of hand-rolled pagination just for this one page.
|
|
24
|
+
# Kaminari pages on the relation itself, so the DB only ever loads the current page's rows
|
|
25
|
+
# (`LIMIT`/`OFFSET`) — never the full pool into memory — via the same `page`/`per_page` params
|
|
26
|
+
# and `paginate` view helper every other index page in this app uses.
|
|
33
27
|
#
|
|
34
28
|
# Shared between LoadTestAccountManagement (lists/mutates the pool) and
|
|
35
29
|
# LoadTestLoginTokenGeneration (mints tokens for exactly the page shown), so both controllers
|
|
36
30
|
# resolve the same page the same way.
|
|
37
31
|
def export_accounts
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Kaminari.paginate_array(matches).page(params[:page]).per(params[:per_page] || DEFAULT_EXPORT_PER_PAGE)
|
|
32
|
+
Spree::User.where(tenant_id: load_test_tenant&.id)
|
|
33
|
+
.load_test_accounts
|
|
34
|
+
.order(id: :desc)
|
|
35
|
+
.page(params[:page])
|
|
36
|
+
.per(params[:per_page] || DEFAULT_EXPORT_PER_PAGE)
|
|
44
37
|
end
|
|
45
38
|
end
|
|
46
39
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# Sets Current.load_test_bypass when the request carries the correct X-Request-Verify secret, so
|
|
3
|
+
# anti-abuse services (e.g. InventoryHolds::ValidateLimits) can skip their checks for trusted
|
|
4
|
+
# load tests. No-op unless LOAD_TEST_BYPASS_KEY is set. Include in any controller that can
|
|
5
|
+
# trigger such a check.
|
|
6
|
+
module LoadTestBypassable
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
before_action :set_load_test_bypass_current
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
# Always assigns the flag (false unless the request carries the correct
|
|
16
|
+
# secret), so its value never leaks from a previous request.
|
|
17
|
+
def set_load_test_bypass_current
|
|
18
|
+
SpreeCmCommissioner::Current.load_test_bypass = load_test_bypass_requested?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def load_test_bypass_requested?
|
|
22
|
+
key = ENV.fetch('LOAD_TEST_BYPASS_KEY', nil)
|
|
23
|
+
return false if key.blank?
|
|
24
|
+
|
|
25
|
+
provided = request.headers.fetch('X-Request-Verify', nil).to_s
|
|
26
|
+
ActiveSupport::SecurityUtils.secure_compare(provided, key)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -53,7 +53,7 @@ module Spree
|
|
|
53
53
|
preferences upload_later social_contact_platform user_id
|
|
54
54
|
bib_number bib_prefix bib_index public_metadata private_metadata
|
|
55
55
|
seat_number intel_phone_number block_id saved_guest_id
|
|
56
|
-
|
|
56
|
+
data_fill_stage
|
|
57
57
|
].freeze
|
|
58
58
|
|
|
59
59
|
def guest_base_columns
|
|
@@ -4,69 +4,40 @@ module Spree
|
|
|
4
4
|
module Storefront
|
|
5
5
|
module Account
|
|
6
6
|
class GuestDynamicFieldsController < ::Spree::Api::V2::ResourceController
|
|
7
|
+
ALLOWED_PHASES = %w[pre_registration post_registration].freeze
|
|
8
|
+
|
|
7
9
|
before_action :require_spree_current_user
|
|
8
10
|
before_action :load_guest
|
|
9
11
|
|
|
10
12
|
def update_dynamic_field
|
|
11
|
-
phase
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
process_dynamic_field(field)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
@guest.save_and_move_to_next_stage
|
|
13
|
+
return render_error_payload('Invalid phase') unless ALLOWED_PHASES.include?(params[:phase])
|
|
14
|
+
return render_error_payload('No dynamic fields provided') if dynamic_fields_params.blank?
|
|
15
|
+
|
|
16
|
+
result = SpreeCmCommissioner::Guests::Update.call(
|
|
17
|
+
guest: @guest,
|
|
18
|
+
guest_params: {
|
|
19
|
+
guest_dynamic_fields_attributes: dynamic_fields_params.map { |f| { dynamic_field_id: f[:id], value: f[:value] } }
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if result.success?
|
|
24
|
+
render_serialized_payload { serialize_guest(result.value[:guest]) }
|
|
25
|
+
else
|
|
26
|
+
render_error_payload(result.error)
|
|
29
27
|
end
|
|
30
|
-
|
|
31
|
-
render_serialized_payload { serialize_guest(@guest) }
|
|
32
|
-
rescue ActiveRecord::RecordInvalid => e
|
|
33
|
-
render_error_payload(e.record.errors, 422)
|
|
34
|
-
rescue ActiveRecord::RecordNotFound => e
|
|
35
|
-
render_error_payload(e.message, 404)
|
|
36
28
|
end
|
|
37
29
|
|
|
38
30
|
private
|
|
39
31
|
|
|
40
32
|
def dynamic_fields_params
|
|
41
|
-
params
|
|
42
|
-
field.permit(:id, :value)
|
|
43
|
-
end
|
|
33
|
+
Array(params[:dynamic_fields]).map { |field| field.permit(:id, :value) }
|
|
44
34
|
end
|
|
45
35
|
|
|
46
36
|
def load_guest
|
|
47
37
|
@guest = SpreeCmCommissioner::Guest.find(params[:guest_id])
|
|
48
38
|
|
|
49
|
-
unless spree_current_user.guests.exists?(id: @guest.id) ||
|
|
50
|
-
|
|
51
|
-
render_error_payload('Unauthorized', 401)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def process_dynamic_field(field)
|
|
56
|
-
dynamic_field = SpreeCmCommissioner::DynamicField.find(field[:id])
|
|
57
|
-
guest_field = @guest.guest_dynamic_fields.build(dynamic_field_id: dynamic_field.id)
|
|
58
|
-
|
|
59
|
-
if dynamic_field.requires_dynamic_field_options?
|
|
60
|
-
option = dynamic_field.dynamic_field_options.find_by(value: field[:value])
|
|
61
|
-
raise ActiveRecord::RecordNotFound, "Invalid value for dynamic field #{dynamic_field.label}" unless option
|
|
62
|
-
|
|
63
|
-
guest_field.dynamic_field_option = option
|
|
64
|
-
guest_field.value = option.value
|
|
65
|
-
else
|
|
66
|
-
guest_field.value = field[:value]
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
guest_field.save!
|
|
39
|
+
raise CanCan::AccessDenied unless spree_current_user.guests.exists?(id: @guest.id) ||
|
|
40
|
+
@guest.line_item.order.user == spree_current_user
|
|
70
41
|
end
|
|
71
42
|
|
|
72
43
|
def serialize_guest(guest)
|
|
@@ -7,7 +7,7 @@ module Spree
|
|
|
7
7
|
before_action :require_spree_current_user
|
|
8
8
|
|
|
9
9
|
def update
|
|
10
|
-
|
|
10
|
+
spree_current_user.clear_has_incomplete_guest_info_cache
|
|
11
11
|
render_success
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -18,7 +18,7 @@ module Spree
|
|
|
18
18
|
{
|
|
19
19
|
success: true,
|
|
20
20
|
message: 'Guest information marked as complete',
|
|
21
|
-
has_incomplete_guest_info:
|
|
21
|
+
has_incomplete_guest_info: spree_current_user.has_incomplete_guest_info?
|
|
22
22
|
}
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -24,21 +24,11 @@ module Spree
|
|
|
24
24
|
private
|
|
25
25
|
|
|
26
26
|
def find_orders_with_incomplete_guest_info
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
order.line_items.any? do |line_item|
|
|
33
|
-
line_item.guests.any? do |guest|
|
|
34
|
-
guest.pre_registration_completed? &&
|
|
35
|
-
guest.post_registration_fields? &&
|
|
36
|
-
!guest.post_registration_completed?
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end.map(&:id)
|
|
40
|
-
|
|
41
|
-
spree_current_user.orders.where(id: incomplete_order_ids)
|
|
27
|
+
spree_current_user.orders
|
|
28
|
+
.complete
|
|
29
|
+
.joins(:guests)
|
|
30
|
+
.merge(SpreeCmCommissioner::Guest.post_registration)
|
|
31
|
+
.distinct
|
|
42
32
|
end
|
|
43
33
|
|
|
44
34
|
def collection_finder
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Storefront
|
|
5
|
+
module Account
|
|
6
|
+
class SavedGuestsController < ::Spree::Api::V2::ResourceController
|
|
7
|
+
before_action :require_spree_current_user
|
|
8
|
+
before_action :load_saved_guest, only: %i[show update destroy]
|
|
9
|
+
|
|
10
|
+
def collection
|
|
11
|
+
spree_current_user.saved_guests.order(created_at: :desc)
|
|
12
|
+
.page(params[:page])
|
|
13
|
+
.per(params[:per_page])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create
|
|
17
|
+
saved_guest = spree_current_user.saved_guests.new(saved_guest_params)
|
|
18
|
+
|
|
19
|
+
if saved_guest.save
|
|
20
|
+
render_serialized_payload(201) { serialize_resource(saved_guest) }
|
|
21
|
+
else
|
|
22
|
+
render_error_payload(saved_guest.errors.full_messages.to_sentence, 422)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def update
|
|
27
|
+
if @saved_guest.update(saved_guest_params)
|
|
28
|
+
render_serialized_payload { serialize_resource(@saved_guest) }
|
|
29
|
+
else
|
|
30
|
+
render_error_payload(@saved_guest.errors.full_messages.to_sentence, 422)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def destroy
|
|
35
|
+
@saved_guest.destroy
|
|
36
|
+
head :no_content
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def model_class
|
|
42
|
+
SpreeCmCommissioner::SavedGuest
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def collection_serializer
|
|
46
|
+
resource_serializer
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def resource_serializer
|
|
50
|
+
SpreeCmCommissioner::V2::Storefront::SavedGuestSerializer
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def load_saved_guest
|
|
54
|
+
@saved_guest = spree_current_user.saved_guests.find(params[:id])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def saved_guest_params
|
|
58
|
+
params.require(:saved_guest).permit(*::Spree::PermittedAttributes.saved_guest_attributes)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -3,6 +3,9 @@ module Spree
|
|
|
3
3
|
module V2
|
|
4
4
|
module Storefront
|
|
5
5
|
class DraftOrdersController < ::Spree::Api::V2::ResourceController
|
|
6
|
+
# Creating the order acquires the inventory hold, so honor the load-test bypass here too.
|
|
7
|
+
include SpreeCmCommissioner::LoadTestBypassable
|
|
8
|
+
|
|
6
9
|
# Creates a generic draft order from a locally-assembled cart in a single request.
|
|
7
10
|
# Endpoint: POST /api/v2/storefront/draft_orders
|
|
8
11
|
# Params:
|
|
@@ -31,26 +31,27 @@ module Spree
|
|
|
31
31
|
def create
|
|
32
32
|
spree_authorize! :update, spree_current_order, order_token
|
|
33
33
|
|
|
34
|
-
result = SpreeCmCommissioner::
|
|
34
|
+
result = SpreeCmCommissioner::Guests::Create.call(
|
|
35
35
|
line_item: parent,
|
|
36
|
-
|
|
36
|
+
guest_params: guest_params
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
if result.success?
|
|
40
|
-
render_serialized_payload(201) { serialize_resource(result.value) }
|
|
40
|
+
render_serialized_payload(201) { serialize_resource(result.value[:guest]) }
|
|
41
41
|
else
|
|
42
|
-
render_error_payload(result.value.errors, 422)
|
|
42
|
+
render_error_payload(result.error || result.value.errors, 422)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def update
|
|
47
47
|
spree_authorize! :update, spree_current_order, order_token unless guest_for_update.pre_registration?
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
result = SpreeCmCommissioner::Guests::Update.call(guest: guest_for_update, guest_params: guest_params)
|
|
50
|
+
|
|
51
|
+
if result.success?
|
|
52
|
+
render_serialized_payload { serialize_resource(result.value[:guest]) }
|
|
53
|
+
else
|
|
54
|
+
render_error_payload(result.error || result.value&.errors, 422)
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
|
|
@@ -18,7 +18,6 @@ module Spree
|
|
|
18
18
|
order = Spree::Order.not_archived.find_by!(token: order_token)
|
|
19
19
|
|
|
20
20
|
spree_authorize! :update, order, order_token
|
|
21
|
-
raise CanCan::AccessDenied unless order.payment?
|
|
22
21
|
|
|
23
22
|
if order.mark_as_archive
|
|
24
23
|
render_serialized_payload { serialize_resource(order) }
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
module Account
|
|
6
|
+
class SavedGuestsController < BaseController
|
|
7
|
+
around_action :wrap_with_multitenant_without
|
|
8
|
+
before_action :require_spree_current_user
|
|
9
|
+
before_action :load_saved_guest, only: %i[show update destroy]
|
|
10
|
+
|
|
11
|
+
def collection
|
|
12
|
+
spree_current_user.saved_guests.order(created_at: :desc)
|
|
13
|
+
.page(params[:page])
|
|
14
|
+
.per(params[:per_page])
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create
|
|
18
|
+
saved_guest = spree_current_user.saved_guests.new(saved_guest_params)
|
|
19
|
+
|
|
20
|
+
if saved_guest.save
|
|
21
|
+
render_serialized_payload(201) { serialize_resource(saved_guest) }
|
|
22
|
+
else
|
|
23
|
+
render_error_payload(saved_guest.errors.full_messages.to_sentence, 422)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update
|
|
28
|
+
if @saved_guest.update(saved_guest_params)
|
|
29
|
+
render_serialized_payload { serialize_resource(@saved_guest) }
|
|
30
|
+
else
|
|
31
|
+
render_error_payload(@saved_guest.errors.full_messages.to_sentence, 422)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def destroy
|
|
36
|
+
@saved_guest.destroy
|
|
37
|
+
head :no_content
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def wrap_with_multitenant_without(&block)
|
|
43
|
+
MultiTenant.without(&block)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def model_class
|
|
47
|
+
SpreeCmCommissioner::SavedGuest
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def collection_serializer
|
|
51
|
+
resource_serializer
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def resource_serializer
|
|
55
|
+
Spree::V2::Tenant::SavedGuestSerializer
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def load_saved_guest
|
|
59
|
+
@saved_guest = spree_current_user.saved_guests.find(params[:id])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def saved_guest_params
|
|
63
|
+
params.require(:saved_guest).permit(*::Spree::PermittedAttributes.saved_guest_attributes)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -30,28 +30,25 @@ module Spree
|
|
|
30
30
|
def create
|
|
31
31
|
spree_authorize! :update, spree_current_order, order_token
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
result = SpreeCmCommissioner::Guests::Create.call(line_item: parent, guest_params: guest_params)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
if result.success?
|
|
36
|
+
render_serialized_payload(201) { serialize_resource(result.value[:guest]) }
|
|
37
|
+
else
|
|
38
|
+
render_error_payload(result.error || result.value&.errors, 422)
|
|
39
39
|
end
|
|
40
|
-
|
|
41
|
-
render_serialized_payload(201) { serialize_resource(resource) }
|
|
42
40
|
end
|
|
43
41
|
|
|
44
42
|
def update
|
|
45
43
|
spree_authorize! :update, spree_current_order, order_token
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
run_dynamic_fields(resource) if guest_params[:guest_dynamic_fields_attributes]
|
|
49
|
-
resource.assign_attributes(guest_params.except(:guest_dynamic_fields_attributes))
|
|
50
|
-
resource.save!
|
|
51
|
-
resource.save_and_move_to_next_stage
|
|
52
|
-
end
|
|
45
|
+
result = SpreeCmCommissioner::Guests::Update.call(guest: resource, guest_params: guest_params)
|
|
53
46
|
|
|
54
|
-
|
|
47
|
+
if result.success?
|
|
48
|
+
render_serialized_payload { serialize_resource(result.value[:guest]) }
|
|
49
|
+
else
|
|
50
|
+
render_error_payload(result.error || result.value&.errors, 422)
|
|
51
|
+
end
|
|
55
52
|
end
|
|
56
53
|
|
|
57
54
|
private
|
|
@@ -99,13 +96,6 @@ module Spree
|
|
|
99
96
|
)
|
|
100
97
|
end
|
|
101
98
|
|
|
102
|
-
def run_dynamic_fields(resource)
|
|
103
|
-
SpreeCmCommissioner::GuestDynamicFieldsManager.call(
|
|
104
|
-
resource: resource,
|
|
105
|
-
dynamic_fields_attributes: guest_params[:guest_dynamic_fields_attributes]
|
|
106
|
-
)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
99
|
def handle_record_invalid(exception)
|
|
110
100
|
render_error_payload(exception.record.errors, 422)
|
|
111
101
|
end
|
|
@@ -21,7 +21,6 @@ module Spree
|
|
|
21
21
|
order = Spree::Order.not_archived.find_by!(token: order_token)
|
|
22
22
|
|
|
23
23
|
spree_authorize! :update, order, order_token
|
|
24
|
-
raise CanCan::AccessDenied unless order.payment?
|
|
25
24
|
|
|
26
25
|
if order.mark_as_archive
|
|
27
26
|
render_serialized_payload { serialize_resource(order) }
|
data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb
CHANGED
|
@@ -5,22 +5,11 @@ module SpreeCmCommissioner
|
|
|
5
5
|
module CheckoutControllerDecorator
|
|
6
6
|
def self.prepended(base)
|
|
7
7
|
base.include SpreeCmCommissioner::OrderConcern
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
private
|
|
13
|
-
|
|
14
|
-
def set_load_test_bypass_current
|
|
15
|
-
key = ENV.fetch('LOAD_TEST_BYPASS_KEY', nil)
|
|
16
|
-
return if key.blank?
|
|
17
|
-
|
|
18
|
-
provided = request.headers.fetch('X-Request-Verify', nil).to_s
|
|
19
|
-
SpreeCmCommissioner::Current.load_test_bypass = ActiveSupport::SecurityUtils.secure_compare(provided, key)
|
|
8
|
+
# Sets Current.load_test_bypass from the X-Request-Verify secret so
|
|
9
|
+
# InventoryHolds::ValidateLimits can skip anti-abuse checks for trusted load tests.
|
|
10
|
+
base.include SpreeCmCommissioner::LoadTestBypassable
|
|
20
11
|
end
|
|
21
12
|
|
|
22
|
-
public
|
|
23
|
-
|
|
24
13
|
# Override Spree's default behavior:
|
|
25
14
|
# By default, Spree raises an error if `complete` is called on a completed or confirmed order.
|
|
26
15
|
# We want to allow this call to succeed, so the API can be used both to:
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module MaintenanceTasks
|
|
3
|
+
class GuestDataFillStageBatchJob < ApplicationJob
|
|
4
|
+
queue_as :maintenance
|
|
5
|
+
|
|
6
|
+
# Refreshes data_fill_stage (+ clears has_incomplete_guest_info caches, and optionally
|
|
7
|
+
# notifies newly-incomplete guests) for a bounded batch of guests. Enqueued in slices by
|
|
8
|
+
# MaintenanceTasks::GuestDataFillStage#maintain and by the recompute_guest_data_fill_stage
|
|
9
|
+
# rake task instead of looping over every guest inline, so neither a product with many
|
|
10
|
+
# completed-order guests nor a full-table backfill blocks a single job for a long time.
|
|
11
|
+
def perform(guest_ids:, notify: true)
|
|
12
|
+
SpreeCmCommissioner::Guest
|
|
13
|
+
.where(id: guest_ids)
|
|
14
|
+
.includes(line_item: :order)
|
|
15
|
+
.find_each do |guest|
|
|
16
|
+
result = SpreeCmCommissioner::Guests::RefreshDataFillStage.call(guest: guest, notify: notify)
|
|
17
|
+
|
|
18
|
+
next if result.success?
|
|
19
|
+
|
|
20
|
+
CmAppLogger.log(
|
|
21
|
+
label: "#{self.class.name} failed",
|
|
22
|
+
data: { guest_id: guest.id, error: result.error.to_s }
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module GuestDataFillStageConcern
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
STAGES = %w[pre_registration post_registration during_check_in].freeze
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
enum data_fill_stage: {
|
|
9
|
+
pre_registration: 'pre_registration',
|
|
10
|
+
post_registration: 'post_registration',
|
|
11
|
+
during_check_in: 'during_check_in',
|
|
12
|
+
completed: 'completed'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
has_one :product, through: :line_item, class_name: 'Spree::Product'
|
|
16
|
+
|
|
17
|
+
STAGES.each do |stage|
|
|
18
|
+
has_many :"#{stage}_fields", -> { where(data_fill_stage: stage) },
|
|
19
|
+
through: :product, source: :dynamic_fields, class_name: 'SpreeCmCommissioner::DynamicField'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
after_commit :refresh_data_fill_stage, on: %i[create update]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def refresh_data_fill_stage
|
|
26
|
+
new_stage = computed_data_fill_stage
|
|
27
|
+
return if data_fill_stage == new_stage
|
|
28
|
+
|
|
29
|
+
update_column(:data_fill_stage, new_stage) # rubocop:disable Rails/SkipsModelValidations
|
|
30
|
+
|
|
31
|
+
# Guests are also refreshed one-off outside the maintenance-task/batch-job path (e.g. a
|
|
32
|
+
# guest filling out a dynamic field via the API), so the cache clear has to live here
|
|
33
|
+
# rather than only in the caller — otherwise has_incomplete_guest_info? could keep
|
|
34
|
+
# returning a stale cached value after an individual guest update.
|
|
35
|
+
line_item&.order&.clear_has_incomplete_guest_info_cache
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Recomputes live rather than trusting the persisted data_fill_stage column, staying fully
|
|
39
|
+
# compatible with guests whose stage was set before this concern existed. Callers (Order,
|
|
40
|
+
# user-facing reads) sit behind caching, so the extra queries here aren't a hot-path concern.
|
|
41
|
+
def incomplete?
|
|
42
|
+
STAGES.any? { |stage| !stage_completed?(stage) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Already true when a stage has no required fields, so any_fields_exist_for? isn't needed
|
|
46
|
+
# first. Uses pluck/exists?, not any?/map, so it always queries fresh instead of reusing an
|
|
47
|
+
# association target cached from an earlier call.
|
|
48
|
+
def stage_completed?(stage)
|
|
49
|
+
# Pre-registration is considered complete if the order is completed
|
|
50
|
+
return true if stage.to_s == 'pre_registration' && line_item&.order&.completed? == true
|
|
51
|
+
|
|
52
|
+
# Once the line item's own duration has ended, pre/post-registration info is moot — don't
|
|
53
|
+
# keep asking for it. during_check_in is deliberately excluded: staff may still log
|
|
54
|
+
# check-in-stage data after the event occurs.
|
|
55
|
+
return true if %w[pre_registration post_registration].include?(stage.to_s) && line_item&.ended? == true
|
|
56
|
+
|
|
57
|
+
required_field_ids = fields_for_stage(stage).pluck(:id)
|
|
58
|
+
return true if required_field_ids.empty?
|
|
59
|
+
|
|
60
|
+
filled_field_ids = guest_dynamic_fields.pluck(:dynamic_field_id)
|
|
61
|
+
required_field_ids.to_set.subset?(filled_field_ids.to_set)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def any_fields_exist_for?(stage)
|
|
65
|
+
fields_for_stage(stage).exists?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def fields_for_stage(stage)
|
|
71
|
+
case stage.to_s
|
|
72
|
+
when 'pre_registration' then pre_registration_fields
|
|
73
|
+
when 'post_registration' then post_registration_fields
|
|
74
|
+
when 'during_check_in' then during_check_in_fields
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def computed_data_fill_stage
|
|
79
|
+
STAGES.find { |stage| !stage_completed?(stage) } || 'completed'
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|