spree_cm_commissioner 2.9.1.pre.pre1 → 2.9.1.pre.pre3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb +8 -1
- data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +1 -2
- data/app/controllers/spree/api/v2/tenant/waiting_room_sessions_controller.rb +8 -1
- data/app/controllers/spree_cm_commissioner/admin/orders_controller_decorator.rb +9 -0
- data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb +14 -1
- data/app/interactors/spree_cm_commissioner/waiting_guests_caller.rb +11 -2
- data/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb +31 -6
- data/app/models/spree_cm_commissioner/current.rb +8 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +6 -0
- data/app/models/spree_cm_commissioner/voting_contestant.rb +6 -5
- data/app/models/spree_cm_commissioner/waiting_room_session.rb +14 -0
- data/app/overrides/spree/admin/shared/_order_summary/internal_note.html.erb.deface +23 -0
- data/app/overrides/spree/admin/shared/_order_summary/internal_note_modal.html.erb.deface +40 -0
- data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +1 -9
- data/app/services/spree_cm_commissioner/inventory_holds/validate_limits.rb +1 -1
- data/config/locales/en.yml +5 -0
- data/config/locales/km.yml +5 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20260701000001_add_device_info_to_cm_waiting_room_sessions.rb +14 -0
- data/db/migrate/20260703065239_add_position_to_cm_voting_contestants.rb +6 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 84c27f4187a23acbe9422e5b33cba6cca077dee5fe7a5e879acd3a5d06ee20c9
|
|
4
|
+
data.tar.gz: e46311b9f88d5a32ddb2272506e99dc1aee786acf800163a833134d942705bc1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f679ac4607b2c7a6b01d6151605cd7339ff57ad721bb18708cff888362b4c19bb9b7f9847016b3e1f93fd0ce31a74635eb6635de83f6764b89873fc44aeb8d2e
|
|
7
|
+
data.tar.gz: 0705c2ddb0b22a1bd2581645cfe18f2488b7aaf827ba90873130c782ac253fd95a50aa9795f982b863fc401744eb8654bc4c225caebec1af08965bab8144c72f
|
data/Gemfile.lock
CHANGED
|
@@ -14,7 +14,8 @@ module Spree
|
|
|
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],
|
|
17
|
-
page_path: params[:page_path]
|
|
17
|
+
page_path: params[:page_path],
|
|
18
|
+
device_attributes: permitted_device_attributes
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
if context.success?
|
|
@@ -27,6 +28,12 @@ module Spree
|
|
|
27
28
|
def resource_serializer
|
|
28
29
|
SpreeCmCommissioner::V2::Storefront::WaitingRoomSessionSerializer
|
|
29
30
|
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def permitted_device_attributes
|
|
35
|
+
params.permit(*SpreeCmCommissioner::WaitingRoomSessionCreator::PERMITTED_DEVICE_ATTRIBUTES)
|
|
36
|
+
end
|
|
30
37
|
end
|
|
31
38
|
end
|
|
32
39
|
end
|
|
@@ -10,7 +10,8 @@ module Spree
|
|
|
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],
|
|
13
|
-
page_path: params[:page_path]
|
|
13
|
+
page_path: params[:page_path],
|
|
14
|
+
device_attributes: permitted_device_attributes
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
if context.success?
|
|
@@ -23,6 +24,12 @@ module Spree
|
|
|
23
24
|
def resource_serializer
|
|
24
25
|
Spree::V2::Tenant::WaitingRoomSessionSerializer
|
|
25
26
|
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def permitted_device_attributes
|
|
31
|
+
params.permit(*SpreeCmCommissioner::WaitingRoomSessionCreator::PERMITTED_DEVICE_ATTRIBUTES)
|
|
32
|
+
end
|
|
26
33
|
end
|
|
27
34
|
end
|
|
28
35
|
end
|
|
@@ -10,6 +10,7 @@ module SpreeCmCommissioner
|
|
|
10
10
|
close_adjustments cart channel set_channel
|
|
11
11
|
accept_all reject_all alert_request_to_vendor
|
|
12
12
|
notifications fire_notification queue_webhooks_requests update_order_status
|
|
13
|
+
update_internal_note
|
|
13
14
|
]
|
|
14
15
|
|
|
15
16
|
base.before_action :initialize_notification_methods, only: %i[
|
|
@@ -81,6 +82,14 @@ module SpreeCmCommissioner
|
|
|
81
82
|
|
|
82
83
|
def notifications; end
|
|
83
84
|
|
|
85
|
+
def update_internal_note
|
|
86
|
+
internal_note = params.dig(:order, :internal_note).to_s.strip.presence
|
|
87
|
+
@order.update_columns(internal_note: internal_note, updated_at: Time.current) # rubocop:disable Rails/SkipsModelValidations
|
|
88
|
+
|
|
89
|
+
flash[:success] = Spree.t(:internal_note_updated, default: 'Internal note updated')
|
|
90
|
+
redirect_back fallback_location: spree.edit_admin_order_url(@order)
|
|
91
|
+
end
|
|
92
|
+
|
|
84
93
|
def update_order_status
|
|
85
94
|
if @order.next
|
|
86
95
|
flash[:success] = I18n.t('orders.update_order_status.success')
|
data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb
CHANGED
|
@@ -7,9 +7,22 @@ module SpreeCmCommissioner
|
|
|
7
7
|
base.include SpreeCmCommissioner::OrderConcern
|
|
8
8
|
base.include SpreeCmCommissioner::TurnstileProtectable
|
|
9
9
|
|
|
10
|
-
base.before_action
|
|
10
|
+
base.before_action :set_load_test_bypass_current
|
|
11
|
+
base.before_action -> { verify_turnstile! }, only: %i[create_payment]
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def set_load_test_bypass_current
|
|
17
|
+
key = ENV.fetch('LOAD_TEST_BYPASS_KEY', nil)
|
|
18
|
+
return if key.blank?
|
|
19
|
+
|
|
20
|
+
provided = request.headers.fetch('X-Request-Verify', nil).to_s
|
|
21
|
+
SpreeCmCommissioner::Current.load_test_bypass = ActiveSupport::SecurityUtils.secure_compare(provided, key)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
public
|
|
25
|
+
|
|
13
26
|
# Override Spree's default behavior:
|
|
14
27
|
# By default, Spree raises an error if `complete` is called on a completed or confirmed order.
|
|
15
28
|
# We want to allow this call to succeed, so the API can be used both to:
|
|
@@ -12,14 +12,23 @@ module SpreeCmCommissioner
|
|
|
12
12
|
# Firestore bounds a batch update by payload size (10 MiB); 500 ops/commit leaves us far under that.
|
|
13
13
|
FIRESTORE_BATCH_SIZE = (ENV['WAITING_ROOM_FIRESTORE_BATCH_SIZE'] || 500).to_i
|
|
14
14
|
|
|
15
|
+
# Release at most this many guests per run, even when far more slots are free. A capacity spike
|
|
16
|
+
# (cold start / lull → active_sessions low → available_slots ≈ max_sessions, e.g. 900) would
|
|
17
|
+
# otherwise release every queued guest at once, dumping 900 simultaneous session-token requests
|
|
18
|
+
# on the server. Capping paces entry at ~MAX_CALLS_PER_RUN/min (the caller runs every minute);
|
|
19
|
+
# the rest stay queued and drain over the following runs. Matches the stamper's MAX_ETA_BATCH_SIZE
|
|
20
|
+
# so the per-batch ETA the app shows reflects the real release rate.
|
|
21
|
+
MAX_CALLS_PER_RUN = (ENV['WAITING_ROOM_MAX_CALLS_PER_RUN'] || 50).to_i
|
|
22
|
+
|
|
15
23
|
def call
|
|
16
24
|
started_at = Time.zone.now
|
|
17
25
|
available_slots = fetch_available_slots
|
|
26
|
+
call_limit = [available_slots, MAX_CALLS_PER_RUN].min
|
|
18
27
|
|
|
19
28
|
# Always run through to mark_as — even with no slots — so the lobby keeps a fresh heartbeat
|
|
20
29
|
# (logs.waiting_guests_caller_job) during peak/full periods instead of going stale
|
|
21
30
|
# and looking like a dead cron.
|
|
22
|
-
long_waiting_guests =
|
|
31
|
+
long_waiting_guests = call_limit.positive? ? fetch_long_waiting_guests(call_limit) : []
|
|
23
32
|
calling_all(long_waiting_guests) if long_waiting_guests.any?
|
|
24
33
|
|
|
25
34
|
mark_as(
|
|
@@ -28,7 +37,7 @@ module SpreeCmCommissioner
|
|
|
28
37
|
has_queue: waiting_exists?([previous_records_path, records_path]),
|
|
29
38
|
avg_wait_to_enter_seconds: compute_avg_wait_to_enter_seconds(long_waiting_guests),
|
|
30
39
|
avg_queue_to_enter_seconds: compute_avg_queue_to_enter_seconds(long_waiting_guests),
|
|
31
|
-
recent_slots_per_call:
|
|
40
|
+
recent_slots_per_call: call_limit.positive? ? call_limit : nil,
|
|
32
41
|
active_sessions: @active_sessions,
|
|
33
42
|
max_sessions: @max_sessions,
|
|
34
43
|
logs: call_logs(started_at: started_at, called_count: long_waiting_guests.size)
|
|
@@ -3,10 +3,21 @@ require 'zlib'
|
|
|
3
3
|
|
|
4
4
|
module SpreeCmCommissioner
|
|
5
5
|
class WaitingRoomSessionCreator < BaseInteractor
|
|
6
|
-
delegate :remote_ip, :waiting_guest_firebase_doc_id, :page_path, :tenant_id,
|
|
6
|
+
delegate :remote_ip, :waiting_guest_firebase_doc_id, :page_path, :tenant_id,
|
|
7
|
+
:device_attributes, to: :context
|
|
7
8
|
|
|
8
9
|
SESSION_CREATION_LOCK_KEY = 'waiting_room_session_creation'.freeze
|
|
9
10
|
|
|
11
|
+
# Device attributes the client may set on the session. device_id and
|
|
12
|
+
# device_fingerprint are real columns; the rest are virtual columns backed by
|
|
13
|
+
# public/private metadata (see WaitingRoomSession). Anything else the client
|
|
14
|
+
# sends is dropped so we never persist arbitrary payloads.
|
|
15
|
+
PERMITTED_DEVICE_ATTRIBUTES = %w[
|
|
16
|
+
device_id device_fingerprint
|
|
17
|
+
os_name os_version device_model device_manufacturer device_type
|
|
18
|
+
device_environment app_version app_build timezone network_type
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
10
21
|
def call
|
|
11
22
|
return context.fail!(message: 'must_provide_waiting_guest_firebase_doc_id') if waiting_guest_firebase_doc_id.blank?
|
|
12
23
|
return context.fail!(message: 'must_provide_remote_ip') if remote_ip.blank?
|
|
@@ -53,15 +64,29 @@ module SpreeCmCommissioner
|
|
|
53
64
|
# create or renew
|
|
54
65
|
context.room_session = SpreeCmCommissioner::WaitingRoomSession.where(guest_identifier: waiting_guest_firebase_doc_id).first_or_initialize
|
|
55
66
|
context.room_session.assign_attributes(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
{
|
|
68
|
+
jwt_token: context.jwt_token,
|
|
69
|
+
expired_at: expired_at,
|
|
70
|
+
remote_ip: remote_ip,
|
|
71
|
+
page_path: page_path,
|
|
72
|
+
tenant_id: tenant_id
|
|
73
|
+
}.merge(sanitized_device_attributes)
|
|
61
74
|
)
|
|
75
|
+
|
|
62
76
|
context.room_session.save!
|
|
63
77
|
end
|
|
64
78
|
|
|
79
|
+
# Whitelisted, flat device attributes. Real columns (device_id,
|
|
80
|
+
# device_fingerprint) and virtual columns (StoreMetadata) are assigned the
|
|
81
|
+
# same way; the model routes each to the right store.
|
|
82
|
+
def sanitized_device_attributes
|
|
83
|
+
attributes = device_attributes
|
|
84
|
+
attributes = attributes.to_unsafe_h if attributes.respond_to?(:to_unsafe_h)
|
|
85
|
+
return {} unless attributes.is_a?(Hash)
|
|
86
|
+
|
|
87
|
+
attributes.symbolize_keys.slice(*PERMITTED_DEVICE_ATTRIBUTES.map(&:to_sym))
|
|
88
|
+
end
|
|
89
|
+
|
|
65
90
|
def log_to_firebase
|
|
66
91
|
SpreeCmCommissioner::WaitingRoomSessionFirebaseLoggerJob.perform_later(
|
|
67
92
|
room_session_id: context.room_session.id,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class Current < ActiveSupport::CurrentAttributes
|
|
3
|
+
# Set to true for the duration of a load-test request so that rate-limiting
|
|
4
|
+
# services deeper in the call chain (e.g. InventoryHolds::ValidateLimits) can
|
|
5
|
+
# skip their checks without needing the request object threaded through them.
|
|
6
|
+
attribute :load_test_bypass
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -71,6 +71,12 @@ module SpreeCmCommissioner
|
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
# override
|
|
75
|
+
def after_resume
|
|
76
|
+
super
|
|
77
|
+
update_columns(canceled_at: nil, canceler_id: nil) # rubocop:disable Rails/SkipsModelValidations
|
|
78
|
+
end
|
|
79
|
+
|
|
74
80
|
# override to allow storing cancellation reason in one DB write
|
|
75
81
|
def canceled_by(user, cancellation_reason: nil)
|
|
76
82
|
transaction do
|
|
@@ -23,19 +23,20 @@ module SpreeCmCommissioner
|
|
|
23
23
|
before_destroy :prevent_if_has_advanced_to
|
|
24
24
|
before_destroy :prevent_if_pre_vote_session
|
|
25
25
|
|
|
26
|
+
acts_as_list scope: :voting_session_id
|
|
27
|
+
|
|
26
28
|
validates :eliminated_via, presence: true, if: :eliminated?
|
|
27
29
|
|
|
28
|
-
# Uniqueness validation: vote_number must be unique
|
|
29
|
-
# Scope:
|
|
30
|
+
# Uniqueness validation: vote_number must be unique among contestants within the same voting_session
|
|
31
|
+
# Scope: voting_session_id
|
|
30
32
|
#
|
|
31
33
|
# | contestant_id | session_id | vote_number | Status | Reason |
|
|
32
34
|
# |---------------|-----------|-------------|--------|---------------------------|
|
|
33
35
|
# | 1 | 1 | 1 | ✓ OK | First record |
|
|
34
|
-
# |
|
|
36
|
+
# | 2 | 1 | 1 | ✗ FAIL | Duplicate in same session |
|
|
35
37
|
# | 1 | 1 | 2 | ✓ OK | Can update/change number |
|
|
36
|
-
# | 2 | 1 | 1 | ✓ OK | Different contestant |
|
|
37
38
|
# | 1 | 2 | 1 | ✓ OK | Different session |
|
|
38
|
-
validates :vote_number, uniqueness: { scope:
|
|
39
|
+
validates :vote_number, uniqueness: { scope: :voting_session_id, allow_nil: true }
|
|
39
40
|
|
|
40
41
|
delegate :name, :contestant_number, :category, :gender, :eliminated_at, to: :show_contestant, allow_nil: true
|
|
41
42
|
delegate :can_vote?, to: :voting_session, allow_nil: true
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class WaitingRoomSession < Base
|
|
3
|
+
include SpreeCmCommissioner::StoreMetadata
|
|
4
|
+
|
|
5
|
+
store_public_metadata :os_name, :string # e.g. "ios", "android", "web"
|
|
6
|
+
store_public_metadata :os_version, :string # e.g. "17.4", "14"
|
|
7
|
+
store_public_metadata :device_model, :string # e.g. "iPhone15,2", "Pixel 8"
|
|
8
|
+
store_public_metadata :device_manufacturer, :string # e.g. "Apple", "Google", "Samsung"
|
|
9
|
+
store_public_metadata :device_type, :string # e.g. "phone", "tablet", "web", "desktop"
|
|
10
|
+
store_public_metadata :device_environment, :string # e.g. "physical", "emulator", "simulator"
|
|
11
|
+
store_public_metadata :app_version, :string # e.g. "2.3.1"
|
|
12
|
+
store_public_metadata :app_build, :string # e.g. "450"
|
|
13
|
+
|
|
14
|
+
store_private_metadata :timezone, :string # e.g. "ICT", "+07", "UTC"
|
|
15
|
+
store_private_metadata :network_type, :string # e.g. "wifi", "cellular", "ethernet", "none"
|
|
16
|
+
|
|
3
17
|
validates :jwt_token, presence: true
|
|
4
18
|
validates :expired_at, presence: true
|
|
5
19
|
validates :remote_ip, presence: true
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!-- insert_after "li:has(#order_channel)" -->
|
|
2
|
+
|
|
3
|
+
<li class="list-group-item d-flex justify-content-between align-items-center" data-hook="admin_order_tab_internal_note">
|
|
4
|
+
<small><%= Spree.t(:internal_note, default: 'Internal Note') %></small>
|
|
5
|
+
<span class="d-flex align-items-center" style="gap: .35rem; min-width: 0;">
|
|
6
|
+
<% if @order.internal_note.present? %>
|
|
7
|
+
<span class="small text-muted text-truncate text-right" style="max-width: 150px;" title="<%= @order.internal_note %>"><%= @order.internal_note %></span>
|
|
8
|
+
<% if can?(:update, @order) %>
|
|
9
|
+
<%= link_to '#', class: 'btn btn-light btn-sm with-tip icon-link p-1',
|
|
10
|
+
title: Spree.t(:edit),
|
|
11
|
+
data: { toggle: 'modal', target: "#internal-note-modal-#{@order.number}" } do %>
|
|
12
|
+
<%= svg_icon name: 'pencil-square.svg', width: '13', height: '13' %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% elsif can?(:update, @order) %>
|
|
16
|
+
<%= link_to Spree.t(:add_internal_note, default: 'Add note'), '#',
|
|
17
|
+
class: 'small',
|
|
18
|
+
data: { toggle: 'modal', target: "#internal-note-modal-#{@order.number}" } %>
|
|
19
|
+
<% else %>
|
|
20
|
+
<span class="small text-muted font-italic"><%= Spree.t(:no_internal_note, default: 'No note') %></span>
|
|
21
|
+
<% end %>
|
|
22
|
+
</span>
|
|
23
|
+
</li>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!-- insert_after "#order_tab_summary" -->
|
|
2
|
+
|
|
3
|
+
<% if can?(:update, @order) %>
|
|
4
|
+
<div id="internal-note-modal-<%= @order.number %>" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="internalNoteModalLabel-<%= @order.number %>" aria-hidden="true">
|
|
5
|
+
<div class="modal-dialog" role="document">
|
|
6
|
+
<div class="modal-content">
|
|
7
|
+
<div class="modal-header">
|
|
8
|
+
<h5 class="modal-title mr-auto" id="internalNoteModalLabel-<%= @order.number %>"><%= Spree.t(:internal_note, default: 'Internal Note') %></h5>
|
|
9
|
+
<button type="button" class="close" data-dismiss="modal" aria-label="<%= Spree.t(:close) %>">
|
|
10
|
+
<span aria-hidden="true">×</span>
|
|
11
|
+
</button>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<%= form_with model: @order, url: update_internal_note_admin_order_path(@order), method: :patch, local: true do |f| %>
|
|
15
|
+
<div class="modal-body">
|
|
16
|
+
<div class="form-group text-left text-start">
|
|
17
|
+
<%= f.text_area :internal_note, rows: 5, class: 'form-control' %>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="modal-footer d-flex justify-end">
|
|
21
|
+
<button type="button" class="btn btn-light" data-dismiss="modal"><%= Spree.t(:close) %></button>
|
|
22
|
+
<%= f.submit Spree.t(:save_internal_note, default: 'Save Note'), class: 'btn btn-primary' %>
|
|
23
|
+
</div>
|
|
24
|
+
<% end %>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<%# The summary renders inside #contextualSideMenu (position: sticky/fixed), whose %>
|
|
30
|
+
<%# stacking context sits below Bootstrap's body-level .modal-backdrop. Relocate the %>
|
|
31
|
+
<%# modal to <body> so it layers above the backdrop and stays interactive. %>
|
|
32
|
+
<script>
|
|
33
|
+
(function () {
|
|
34
|
+
var modal = document.getElementById('internal-note-modal-<%= @order.number %>');
|
|
35
|
+
if (modal && modal.parentNode !== document.body) {
|
|
36
|
+
document.body.appendChild(modal);
|
|
37
|
+
}
|
|
38
|
+
})();
|
|
39
|
+
</script>
|
|
40
|
+
<% end %>
|
|
@@ -2,15 +2,7 @@ module Spree
|
|
|
2
2
|
module V2
|
|
3
3
|
module Tenant
|
|
4
4
|
class ShowEpisodeSerializer < BaseSerializer
|
|
5
|
-
attributes :episode_number, :name, :scheduled_at, :ends_at
|
|
6
|
-
|
|
7
|
-
attribute :status do |episode|
|
|
8
|
-
if episode.current?
|
|
9
|
-
'current'
|
|
10
|
-
elsif episode.upcoming?
|
|
11
|
-
'upcoming'
|
|
12
|
-
end
|
|
13
|
-
end
|
|
5
|
+
attributes :episode_number, :name, :scheduled_at, :ends_at, :status, :preview
|
|
14
6
|
|
|
15
7
|
has_many :voting_sessions, serializer: Spree::V2::Tenant::VotingSessionSerializer
|
|
16
8
|
has_many :vote_packages, serializer: Spree::V2::Tenant::VotePackageSerializer
|
|
@@ -15,7 +15,7 @@ module SpreeCmCommissioner
|
|
|
15
15
|
# Validates all inventory hold limits for the given order
|
|
16
16
|
def call(order:)
|
|
17
17
|
validate_user_hold_limits!(order) if order.user.present?
|
|
18
|
-
validate_ip_rate_limit!(order)
|
|
18
|
+
validate_ip_rate_limit!(order) unless SpreeCmCommissioner::Current.load_test_bypass
|
|
19
19
|
validate_cooldown!(order)
|
|
20
20
|
|
|
21
21
|
success(nil)
|
data/config/locales/en.yml
CHANGED
|
@@ -216,6 +216,11 @@ en:
|
|
|
216
216
|
riel: "៛"
|
|
217
217
|
|
|
218
218
|
spree:
|
|
219
|
+
internal_note: "Internal Note"
|
|
220
|
+
add_internal_note: "Add note"
|
|
221
|
+
no_internal_note: "No note"
|
|
222
|
+
save_internal_note: "Save Note"
|
|
223
|
+
internal_note_updated: "Internal note updated"
|
|
219
224
|
voting_contestant_advancer:
|
|
220
225
|
reversion_error: "Cannot eliminate here yet. This contestant appears in a later session."
|
|
221
226
|
is_open_dated: "Is Open Dated Trip?"
|
data/config/locales/km.yml
CHANGED
|
@@ -200,6 +200,11 @@ km:
|
|
|
200
200
|
riel: "៛"
|
|
201
201
|
|
|
202
202
|
spree:
|
|
203
|
+
internal_note: "ចំណាំផ្ទៃក្នុង"
|
|
204
|
+
add_internal_note: "បន្ថែមចំណាំ"
|
|
205
|
+
no_internal_note: "គ្មានចំណាំ"
|
|
206
|
+
save_internal_note: "រក្សាទុកចំណាំ"
|
|
207
|
+
internal_note_updated: "បានរក្សាទុកចំណាំផ្ទៃក្នុង"
|
|
203
208
|
voting_contestant_advancer:
|
|
204
209
|
reversion_error: "មិនអាចដកចេញនៅវគ្គនេះបានទេ។ បេក្ខជននេះមាននៅវគ្គបោះឆ្នោតក្រោយ។"
|
|
205
210
|
registered_by:
|
data/config/routes.rb
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class AddDeviceInfoToCmWaitingRoomSessions < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
# Indexed identifiers used for dedup / abuse detection.
|
|
4
|
+
add_column :cm_waiting_room_sessions, :device_id, :string, if_not_exists: true
|
|
5
|
+
add_column :cm_waiting_room_sessions, :device_fingerprint, :string, if_not_exists: true
|
|
6
|
+
|
|
7
|
+
# Typed device attributes live here via SpreeCmCommissioner::StoreMetadata.
|
|
8
|
+
add_column :cm_waiting_room_sessions, :public_metadata, :jsonb, default: {}, if_not_exists: true
|
|
9
|
+
add_column :cm_waiting_room_sessions, :private_metadata, :jsonb, default: {}, if_not_exists: true
|
|
10
|
+
|
|
11
|
+
add_index :cm_waiting_room_sessions, :device_id, if_not_exists: true
|
|
12
|
+
add_index :cm_waiting_room_sessions, :device_fingerprint, if_not_exists: true
|
|
13
|
+
end
|
|
14
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_cm_commissioner
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.9.1.pre.
|
|
4
|
+
version: 2.9.1.pre.pre3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- You
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: spree
|
|
@@ -1512,6 +1512,7 @@ files:
|
|
|
1512
1512
|
- app/models/spree_cm_commissioner/configuration.rb
|
|
1513
1513
|
- app/models/spree_cm_commissioner/crew_invite.rb
|
|
1514
1514
|
- app/models/spree_cm_commissioner/crew_invite_link.rb
|
|
1515
|
+
- app/models/spree_cm_commissioner/current.rb
|
|
1515
1516
|
- app/models/spree_cm_commissioner/customer.rb
|
|
1516
1517
|
- app/models/spree_cm_commissioner/customer_notification.rb
|
|
1517
1518
|
- app/models/spree_cm_commissioner/customer_promotion_rule.rb
|
|
@@ -1828,6 +1829,8 @@ files:
|
|
|
1828
1829
|
- app/overrides/spree/admin/shared/_main_menu/sidebar_customer_notification.html.erb.deface
|
|
1829
1830
|
- app/overrides/spree/admin/shared/_main_menu/sidebar_homepage.html.erb.deface
|
|
1830
1831
|
- app/overrides/spree/admin/shared/_order_summary/adjustment_total.html.erb.deface
|
|
1832
|
+
- app/overrides/spree/admin/shared/_order_summary/internal_note.html.erb.deface
|
|
1833
|
+
- app/overrides/spree/admin/shared/_order_summary/internal_note_modal.html.erb.deface
|
|
1831
1834
|
- app/overrides/spree/admin/shared/_order_summary/order_status.html.erb.deface
|
|
1832
1835
|
- app/overrides/spree/admin/shared/_order_summary/request_state.html.erb.deface
|
|
1833
1836
|
- app/overrides/spree/admin/shared/_order_tabs/guests.html.erb.deface
|
|
@@ -3407,6 +3410,8 @@ files:
|
|
|
3407
3410
|
- db/migrate/20260623082233_create_cm_claims.rb
|
|
3408
3411
|
- db/migrate/20260624175658_add_unique_index_to_cm_claims.rb
|
|
3409
3412
|
- db/migrate/20260629000001_add_term_of_use_to_spree_vendors.rb
|
|
3413
|
+
- db/migrate/20260701000001_add_device_info_to_cm_waiting_room_sessions.rb
|
|
3414
|
+
- db/migrate/20260703065239_add_position_to_cm_voting_contestants.rb
|
|
3410
3415
|
- docker-compose.yml
|
|
3411
3416
|
- docs/api/scoped-access-token-endpoints.md
|
|
3412
3417
|
- docs/option_types/attr_types.md
|