spree_cm_commissioner 2.8.15.pre.pre8 → 2.8.15.pre.pre9
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 +16 -0
- data/.github/workflows/test_and_build_gem.yml +2 -1
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/admin/import_new_orders_controller.rb +25 -0
- data/app/controllers/spree/admin/inventory_holds_controller.rb +21 -1
- data/app/controllers/spree/admin/system/maintenance_tasks_controller.rb +64 -0
- data/app/controllers/spree/admin/system/waiting_room_controller.rb +22 -0
- data/app/controllers/spree/admin/taxons_controller_decorator.rb +5 -0
- data/app/controllers/spree/api/v2/storefront/account/orders_controller_decorator.rb +5 -0
- data/app/controllers/spree/api/v2/storefront/guests_controller.rb +7 -5
- data/app/controllers/spree/api/v2/storefront/order_histories_controller.rb +14 -2
- data/app/controllers/spree/api/v2/storefront/ticket_transfers_controller.rb +4 -3
- data/app/controllers/spree/api/v2/tenant/order_histories_controller.rb +14 -2
- data/app/finders/spree_cm_commissioner/orders/find.rb +33 -3
- data/app/finders/spree_cm_commissioner/orders/find_by_all_state.rb +11 -0
- data/app/interactors/spree_cm_commissioner/payment_incomplete_notification_sender.rb +25 -0
- data/app/interactors/spree_cm_commissioner/release_inventory_item_notification_sender.rb +24 -0
- data/app/interactors/spree_cm_commissioner/waiting_guests_caller.rb +163 -20
- data/app/interactors/spree_cm_commissioner/waiting_room_latest_system_metadata_puller.rb +5 -3
- data/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb +25 -6
- data/app/jobs/concerns/spree_cm_commissioner/idempotent_job.rb +12 -0
- data/app/jobs/spree_cm_commissioner/cancel_import_order_job.rb +11 -0
- data/app/jobs/spree_cm_commissioner/idempotency_keys/prune_job.rb +20 -0
- data/app/jobs/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_job.rb +21 -3
- data/app/jobs/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_on_hold_job.rb +34 -3
- data/app/jobs/spree_cm_commissioner/maintenance_tasks/refinalize_guests_batch_job.rb +36 -0
- data/app/jobs/spree_cm_commissioner/payment_incomplete_notification_job.rb +12 -0
- data/app/jobs/spree_cm_commissioner/telegram_alerts/order_integrity_check_job.rb +17 -0
- data/app/jobs/spree_cm_commissioner/waiting_guests_caller_job.rb +6 -1
- data/app/jobs/spree_cm_commissioner/waiting_room/publish_lobby_path_job.rb +17 -0
- data/app/jobs/spree_cm_commissioner/waiting_room/stamp_queue_positions_job.rb +14 -0
- data/app/jobs/spree_cm_commissioner/waiting_room_latest_system_metadata_puller_job.rb +1 -1
- data/app/mailers/spree/order_mailer_decorator.rb +2 -0
- data/app/models/concerns/spree_cm_commissioner/line_item_guests_concern.rb +16 -2
- data/app/models/concerns/spree_cm_commissioner/order_holdable.rb +6 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +33 -1
- data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +2 -0
- data/app/models/concerns/spree_cm_commissioner/store_preference.rb +1 -0
- data/app/models/spree_cm_commissioner/idempotency_key.rb +21 -0
- data/app/models/spree_cm_commissioner/import.rb +2 -1
- data/app/models/spree_cm_commissioner/imported_order.rb +6 -0
- data/app/models/spree_cm_commissioner/imports/import_order.rb +21 -0
- data/app/models/spree_cm_commissioner/inventory_hold.rb +1 -1
- data/app/models/spree_cm_commissioner/maintenance_task.rb +10 -0
- data/app/models/spree_cm_commissioner/maintenance_tasks/event.rb +6 -13
- data/app/models/spree_cm_commissioner/notification.rb +1 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +18 -0
- data/app/models/spree_cm_commissioner/payment_decorator.rb +11 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +2 -0
- data/app/models/spree_cm_commissioner/taxon_ad_banner.rb +12 -0
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +1 -0
- data/app/notifications/spree_cm_commissioner/order_general_notification.rb +15 -0
- data/app/overrides/spree/admin/orders/_order_actions/actions.html.erb.deface +7 -0
- data/app/overrides/spree/admin/orders/_search/payment_number_search_field.html.erb.deface +8 -0
- data/app/overrides/spree/admin/products/_form/allow_gift_transfer.html.erb.deface +1 -1
- data/app/overrides/spree/admin/products/_form/allow_transfer.html.erb.deface +1 -1
- data/app/overrides/spree/admin/products/_form/enable_send_receipt.html.erb.deface +13 -0
- data/app/overrides/spree/admin/products/_form/enable_telegram_alert.html.erb.deface +14 -0
- data/app/overrides/spree/admin/stores/_form/store_preferences.html.erb.deface +9 -0
- data/app/overrides/spree/admin/taxons/_form/assets_form.html.erb.deface +10 -1
- data/app/overrides/spree/admin/vendors/_form/term_of_use.html.erb.deface +6 -0
- data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +2 -0
- data/app/serializers/spree/v2/tenant/guest_serializer.rb +2 -1
- data/app/services/spree_cm_commissioner/cart/add_guest.rb +30 -3
- data/app/services/spree_cm_commissioner/cart/add_item_decorator.rb +22 -0
- data/app/services/spree_cm_commissioner/cart/create_guest.rb +80 -0
- data/app/services/spree_cm_commissioner/cart/empty_decorator.rb +23 -0
- data/app/services/spree_cm_commissioner/cart/remove_guest.rb +26 -1
- data/app/services/spree_cm_commissioner/cart/remove_item_decorator.rb +17 -0
- data/app/services/spree_cm_commissioner/cart/remove_line_item_decorator.rb +17 -0
- data/app/services/spree_cm_commissioner/cart/set_quantity_decorator.rb +17 -0
- data/app/services/spree_cm_commissioner/fraud_check.rb +9 -0
- data/app/services/spree_cm_commissioner/imports/orders/cancel.rb +63 -0
- data/app/services/spree_cm_commissioner/imports/orders/create.rb +1 -0
- data/app/services/spree_cm_commissioner/inventory_holds/release.rb +18 -0
- data/app/services/spree_cm_commissioner/telegram_alerts/checks/order_complete_payment_not_paid.rb +21 -0
- data/app/services/spree_cm_commissioner/telegram_alerts/checks/payment_paid_order_not_complete.rb +21 -0
- data/app/services/spree_cm_commissioner/telegram_alerts/order_integrity_alert.rb +112 -0
- data/app/services/spree_cm_commissioner/telegram_alerts/order_integrity_checks_runner.rb +32 -0
- data/app/services/spree_cm_commissioner/waiting_room/publish_lobby_path.rb +57 -0
- data/app/services/spree_cm_commissioner/waiting_room/stamp_queue_positions.rb +185 -0
- data/app/services/spree_cm_commissioner/waiting_room_lobby_metadata_fetcher.rb +47 -0
- data/app/services/spree_cm_commissioner/waiting_room_system_metadata_fetcher.rb +19 -5
- data/app/services/spree_cm_commissioner/waiting_room_system_metadata_setter.rb +28 -5
- data/app/views/spree/admin/import_new_orders/_cancel_modal.html.erb +35 -0
- data/app/views/spree/admin/import_new_orders/index.html.erb +4 -0
- data/app/views/spree/admin/import_new_orders/show.html.erb +30 -0
- data/app/views/spree/admin/imports/index.html.erb +2 -0
- data/app/views/spree/admin/inventory_holds/index.html.erb +8 -3
- data/app/views/spree/admin/shared/_system_tabs.html.erb +7 -0
- data/app/views/spree/admin/system/maintenance_tasks/_search.html.erb +28 -0
- data/app/views/spree/admin/system/maintenance_tasks/index.html.erb +71 -0
- data/app/views/spree/admin/system/waiting_room/show.html.erb +58 -0
- data/app/views/spree/order_mailer/confirm_email.html.erb +10 -0
- data/app/views/spree_cm_commissioner/order_mailer/_mailer_stylesheets.html.erb +12 -0
- data/app/views/spree_cm_commissioner/order_mailer/purchased_items/_items.html.erb +12 -9
- data/config/initializers/spree_permitted_attributes.rb +6 -0
- data/config/locales/en.yml +20 -4
- data/config/locales/km.yml +12 -4
- data/config/routes.rb +10 -0
- data/db/migrate/20260616000001_add_canceled_by_id_to_cm_imports.rb +5 -0
- data/db/migrate/20260617000002_create_cm_imported_orders.rb +12 -0
- data/db/migrate/20260619000001_create_cm_idempotency_keys.rb +11 -0
- data/db/migrate/20260629000001_add_term_of_use_to_spree_vendors.rb +5 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +43 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db6de320351a3caae8243230898ce35ae85406e84424101bc01b126ce7eefb4c
|
|
4
|
+
data.tar.gz: 97a586260f667681ee89ca8aa224fc3ba05137dfcfbf4453191137e15d724735
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8d9ab3710513dd89d55d6422d924ce10c0e83f661cdf2576650586b1a11a627db19a161a8aa657c04d305201a10d1f4823d1ea2fff44657638829d7ba7c2ef52
|
|
7
|
+
data.tar.gz: de4d493586903410f18877bbb2e959383b42765b241c4d90cb08e0aef5cda6618433a24c6c18714391d60805176971b0aa12bb7165a694818abea06915164697
|
data/.env.example
CHANGED
|
@@ -20,6 +20,13 @@ WAITING_ROOM_SESSION_SIGNATURE="e6b2********************26e3"
|
|
|
20
20
|
WAITING_ROOM_SESSION_EXPIRE_DURATION_IN_SECOND=
|
|
21
21
|
WAITING_ROOM_MIN_SESSIONS_COUNT=5
|
|
22
22
|
WAITING_ROOM_DISABLED=no
|
|
23
|
+
WAITING_ROOM_POSITION_STAMP_LIMIT=1000
|
|
24
|
+
WAITING_ROOM_FIRESTORE_BATCH_SIZE=500
|
|
25
|
+
WAITING_ROOM_MIN_WAIT_TO_ENTER_SECONDS=600 # Waiting Room step floor (10 min)
|
|
26
|
+
WAITING_ROOM_MIN_QUEUE_TO_ENTER_SECONDS=300 # Queue step floor (5 min)
|
|
27
|
+
WAITING_ROOM_MAX_WAIT_TO_DISPLAY_SECONDS=3600 # above this the ETA is hidden (1 h)
|
|
28
|
+
WAITING_ROOM_CALLER_INTERVAL_SECONDS=60 # how often the caller runs; match the waiting_guests_caller cron
|
|
29
|
+
WAITING_ROOM_MAX_ETA_BATCH_SIZE=50 # cap on batch size assumed when estimating the ETA
|
|
23
30
|
|
|
24
31
|
# Vattanac Bank
|
|
25
32
|
VATTANAC_AES_SECRET_KEY= ""
|
|
@@ -45,6 +52,9 @@ CACHE_DEFAULT_MAX_AGE=300 # Default for unlisted controllers - 5 minutes
|
|
|
45
52
|
# Batch size for processing maintenance tasks in app/jobs/spree_cm_commissioner/maintenance_tasks/orchestrate_job.rb
|
|
46
53
|
MAINTENANCE_TASKS_BATCH_SIZE=100
|
|
47
54
|
|
|
55
|
+
# Batch size for slicing guest_ids when enqueuing RefinalizeGuestsBatchJob in app/models/spree_cm_commissioner/maintenance_tasks/event.rb
|
|
56
|
+
MAINTENANCE_TASKS_REFINALIZE_GUESTS_BATCH_SIZE=300
|
|
57
|
+
|
|
48
58
|
# Export related:
|
|
49
59
|
OPERATOR_GUEST_JSON_GZIP_CACHE_HOURS=24
|
|
50
60
|
EXPORT_PRESIGNED_URL_EXPIRATION_MINUTES=15
|
|
@@ -63,3 +73,9 @@ PAYMENT_LOCK_MIN_DURATION_IN_MINUTES=5 # Minimum remaining hold time requir
|
|
|
63
73
|
MAX_ACTIVE_HOLDS_PER_USER=3 # Max concurrent active holds allowed per user across all orders
|
|
64
74
|
MAX_HOLDS_PER_IP_PER_HOUR=5 # Max hold attempts per IP address within a rolling 1-hour window (abuse prevention)
|
|
65
75
|
HOLD_COOLDOWN_AFTER_EXPIRY_IN_MINUTES=2 # Cooldown period before a user can re-acquire a hold after one expired on them
|
|
76
|
+
|
|
77
|
+
# See: app/models/spree_cm_commissioner/payment_decorator.rb
|
|
78
|
+
PAYMENT_INCOMPLETE_NOTIFICATION_DELAY_IN_MINUTES=5 # Delay before notifying a customer that their order is still unpaid after payment is created
|
|
79
|
+
|
|
80
|
+
# See: app/jobs/spree_cm_commissioner/telegram_alerts/order_integrity_check_job.rb
|
|
81
|
+
ORDER_INTEGRITY_TELEGRAM_ALERT_ENABLED="no" # Set to yes to enable the order integrity Telegram alert job (off by default to avoid flooding Telegram rate limits during traffic spikes)
|
|
@@ -163,8 +163,9 @@ jobs:
|
|
|
163
163
|
- name: Security
|
|
164
164
|
env:
|
|
165
165
|
DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
|
|
166
|
+
|
|
167
|
+
# Using --no-threads to bypass a known Ruby 3.2.0 GC segmentation fault in CI.
|
|
166
168
|
run: |
|
|
167
|
-
# Using --no-threads to bypass a known Ruby 3.2.0 GC segmentation fault in CI.
|
|
168
169
|
bundle exec brakeman --no-threads --no-exit-on-warn
|
|
169
170
|
|
|
170
171
|
test:
|
data/Gemfile.lock
CHANGED
|
@@ -23,6 +23,31 @@ module Spree
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
|
+
|
|
27
|
+
# POST: /admin/orders/import_new_orders/:id/cancel
|
|
28
|
+
def cancel
|
|
29
|
+
import = model_class.find(params[:id])
|
|
30
|
+
show_url = spree.admin_import_new_order_url(import.id)
|
|
31
|
+
|
|
32
|
+
unless import.cancellable?
|
|
33
|
+
flash[:error] = 'These orders cannot be canceled.' # rubocop:disable Rails/I18nLocaleTexts
|
|
34
|
+
redirect_to show_url and return
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if params[:confirm_name].to_s.strip != import.name.to_s.strip
|
|
38
|
+
flash[:error] = 'Import name did not match. Cancellation aborted.' # rubocop:disable Rails/I18nLocaleTexts
|
|
39
|
+
redirect_to show_url and return
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
SpreeCmCommissioner::CancelImportOrderJob.perform_later(
|
|
43
|
+
import_order_id: import.id,
|
|
44
|
+
canceled_by_user_id: spree_current_user.id,
|
|
45
|
+
cancellation_reason: params[:cancellation_reason].to_s.strip.presence
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
flash[:success] = "Canceling orders from \"#{import.name}\". This runs in the background."
|
|
49
|
+
redirect_to show_url
|
|
50
|
+
end
|
|
26
51
|
end
|
|
27
52
|
end
|
|
28
53
|
end
|
|
@@ -9,6 +9,7 @@ module Spree
|
|
|
9
9
|
q['s'] ||= 'created_at desc'
|
|
10
10
|
q['created_at_gt'] = parse_time_or_nil(q['created_at_gt'], :beginning_of_day) if q['created_at_gt'].present?
|
|
11
11
|
q['created_at_lt'] = parse_time_or_nil(q['created_at_lt'], :end_of_day) if q['created_at_lt'].present?
|
|
12
|
+
normalize_enum_filters!(q)
|
|
12
13
|
|
|
13
14
|
@search = scope.ransack(q)
|
|
14
15
|
result_scope = @search.result(distinct: true)
|
|
@@ -18,7 +19,11 @@ module Spree
|
|
|
18
19
|
.page(params[:page])
|
|
19
20
|
.per(params[:per_page] || Spree::Backend::Config[:admin_orders_per_page])
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
# Status tab counts must reflect every other filter but NOT the status filter, so
|
|
23
|
+
# each tab shows how many holds it would return (otherwise selecting one status
|
|
24
|
+
# zeroes out all the other tabs).
|
|
25
|
+
@status_counts = scope.ransack(q.except('status_eq')).result(distinct: true)
|
|
26
|
+
.reorder(nil).group(:status).count
|
|
22
27
|
end
|
|
23
28
|
|
|
24
29
|
def release
|
|
@@ -44,8 +49,23 @@ module Spree
|
|
|
44
49
|
|
|
45
50
|
private
|
|
46
51
|
|
|
52
|
+
# `status` and `release_reason` are integer-backed enums. Ransack casts a string
|
|
53
|
+
# value for an integer column with String#to_i, so "payment_locked" becomes 0
|
|
54
|
+
# (== :pending) and every label silently filters to the wrong status. Translate
|
|
55
|
+
# enum labels to their stored integer before searching.
|
|
56
|
+
def normalize_enum_filters!(query)
|
|
57
|
+
{
|
|
58
|
+
'status_eq' => SpreeCmCommissioner::InventoryHold.statuses,
|
|
59
|
+
'release_reason_eq' => SpreeCmCommissioner::InventoryHold.release_reasons
|
|
60
|
+
}.each do |key, mapping|
|
|
61
|
+
value = query[key]
|
|
62
|
+
query[key] = mapping[value.to_s] if value.present? && mapping.key?(value.to_s)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
47
66
|
def back_params
|
|
48
67
|
raw_params = params[:back_params].presence || params
|
|
68
|
+
raw_params = raw_params.to_unsafe_h if raw_params.respond_to?(:to_unsafe_h)
|
|
49
69
|
ActionController::Parameters.new(raw_params).permit(:page, :per_page, q: {})
|
|
50
70
|
end
|
|
51
71
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
module System
|
|
4
|
+
class MaintenanceTasksController < Spree::Admin::BaseController
|
|
5
|
+
def index
|
|
6
|
+
authorize! :manage, SpreeCmCommissioner::MaintenanceTask
|
|
7
|
+
|
|
8
|
+
@search = scope.ransack(params[:q])
|
|
9
|
+
@tasks = @search.result(distinct: true)
|
|
10
|
+
.order(created_at: :desc)
|
|
11
|
+
.page(params[:page])
|
|
12
|
+
.per(params[:per_page] || Spree::Backend::Config[:admin_orders_per_page])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run_now
|
|
16
|
+
authorize! :manage, SpreeCmCommissioner::MaintenanceTask
|
|
17
|
+
|
|
18
|
+
task = SpreeCmCommissioner::MaintenanceTask.find(params[:id])
|
|
19
|
+
|
|
20
|
+
if task.completed_at.present?
|
|
21
|
+
flash[:error] = "Task ##{task.id} is already completed."
|
|
22
|
+
else
|
|
23
|
+
task.update!(manually_triggered: true)
|
|
24
|
+
task.execute
|
|
25
|
+
|
|
26
|
+
if task.completed_at.present?
|
|
27
|
+
flash[:success] = "Task ##{task.id} (#{task.type.demodulize}) ran successfully."
|
|
28
|
+
else
|
|
29
|
+
flash[:error] = "Task ##{task.id} (#{task.type.demodulize}) failed: #{task.last_error}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
redirect_to admin_system_maintenance_tasks_path(back_params)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def destroy
|
|
37
|
+
authorize! :manage, SpreeCmCommissioner::MaintenanceTask
|
|
38
|
+
|
|
39
|
+
task = SpreeCmCommissioner::MaintenanceTask.find(params[:id])
|
|
40
|
+
|
|
41
|
+
if task.destroy
|
|
42
|
+
flash[:success] = "Task ##{task.id} has been deleted."
|
|
43
|
+
else
|
|
44
|
+
flash[:error] = task.errors.full_messages.to_sentence.presence || "Failed to delete task ##{task.id}."
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
redirect_to admin_system_maintenance_tasks_path(back_params)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def scope
|
|
53
|
+
SpreeCmCommissioner::MaintenanceTask.accessible_by(current_ability, :manage)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def back_params
|
|
57
|
+
raw_params = params[:back_params].presence || params
|
|
58
|
+
raw_params = raw_params.to_unsafe_h if raw_params.respond_to?(:to_unsafe_h)
|
|
59
|
+
ActionController::Parameters.new(raw_params).permit(:page, :per_page, q: {})
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -4,7 +4,10 @@ module Spree
|
|
|
4
4
|
class WaitingRoomController < Spree::Admin::BaseController
|
|
5
5
|
def show
|
|
6
6
|
@fetcher = SpreeCmCommissioner::WaitingRoomSystemMetadataFetcher.new
|
|
7
|
+
@lobby_fetcher = SpreeCmCommissioner::WaitingRoomLobbyMetadataFetcher.new
|
|
8
|
+
|
|
7
9
|
@fetcher.load_document_data
|
|
10
|
+
@lobby_fetcher.load_document_data
|
|
8
11
|
|
|
9
12
|
@active_sesions_count = SpreeCmCommissioner::WaitingRoomSession.active.count
|
|
10
13
|
end
|
|
@@ -23,12 +26,31 @@ module Spree
|
|
|
23
26
|
redirect_back fallback_location: admin_system_waiting_room_path
|
|
24
27
|
end
|
|
25
28
|
|
|
29
|
+
def modify_max_sessions_count_cap
|
|
30
|
+
modifier = params[:max_sessions_count_cap]&.to_i || 0
|
|
31
|
+
SpreeCmCommissioner::WaitingRoomSystemMetadataSetter.new.modify_max_sessions_count_cap(modifier)
|
|
32
|
+
|
|
33
|
+
redirect_back fallback_location: admin_system_waiting_room_path
|
|
34
|
+
end
|
|
35
|
+
|
|
26
36
|
def force_pull
|
|
27
37
|
SpreeCmCommissioner::WaitingRoomLatestSystemMetadataPullerJob.perform_now
|
|
28
38
|
SpreeCmCommissioner::WaitingGuestsCallerJob.perform_now
|
|
29
39
|
|
|
30
40
|
redirect_back fallback_location: admin_system_waiting_room_path
|
|
31
41
|
end
|
|
42
|
+
|
|
43
|
+
def publish_lobby_path
|
|
44
|
+
result = SpreeCmCommissioner::WaitingRoom::PublishLobbyPath.call
|
|
45
|
+
|
|
46
|
+
if result.success?
|
|
47
|
+
flash[:success] = "Published waiting guests records path: #{result.value[:records_path]}"
|
|
48
|
+
else
|
|
49
|
+
flash[:error] = result.error.to_s
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
redirect_back fallback_location: admin_system_waiting_room_path
|
|
53
|
+
end
|
|
32
54
|
end
|
|
33
55
|
end
|
|
34
56
|
end
|
|
@@ -28,6 +28,10 @@ module Spree
|
|
|
28
28
|
remove_asset(@taxon.home_banner)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def remove_ad_banner
|
|
32
|
+
remove_asset(@taxon.ad_banner)
|
|
33
|
+
end
|
|
34
|
+
|
|
31
35
|
def remove_video_banner
|
|
32
36
|
remove_asset(@taxon.video_banner)
|
|
33
37
|
end
|
|
@@ -49,6 +53,7 @@ module Spree
|
|
|
49
53
|
@taxon.build_app_banner(attachment: permitted_resource_params.delete(:app_banner)) if permitted_resource_params[:app_banner]
|
|
50
54
|
@taxon.build_web_banner(attachment: permitted_resource_params.delete(:web_banner)) if permitted_resource_params[:web_banner]
|
|
51
55
|
@taxon.build_home_banner(attachment: permitted_resource_params.delete(:home_banner)) if permitted_resource_params[:home_banner]
|
|
56
|
+
@taxon.build_ad_banner(attachment: permitted_resource_params.delete(:ad_banner)) if permitted_resource_params[:ad_banner]
|
|
52
57
|
@taxon.build_video_banner(attachment: permitted_resource_params.delete(:video_banner)) if permitted_resource_params[:video_banner]
|
|
53
58
|
end
|
|
54
59
|
|
|
@@ -44,6 +44,11 @@ module Spree
|
|
|
44
44
|
def collection_finder
|
|
45
45
|
SpreeCmCommissioner::Orders::FindByState
|
|
46
46
|
end
|
|
47
|
+
|
|
48
|
+
# this allows fetching a single order regardless of its state.
|
|
49
|
+
def resource_finder
|
|
50
|
+
SpreeCmCommissioner::Orders::FindByAllState
|
|
51
|
+
end
|
|
47
52
|
end
|
|
48
53
|
end
|
|
49
54
|
end
|
|
@@ -31,13 +31,15 @@ module Spree
|
|
|
31
31
|
def create
|
|
32
32
|
spree_authorize! :update, spree_current_order, order_token
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
result = SpreeCmCommissioner::Cart::CreateGuest.call(
|
|
35
|
+
line_item: parent,
|
|
36
|
+
options: guest_params
|
|
37
|
+
)
|
|
35
38
|
|
|
36
|
-
if
|
|
37
|
-
|
|
38
|
-
render_serialized_payload(201) { serialize_resource(resource) }
|
|
39
|
+
if result.success?
|
|
40
|
+
render_serialized_payload(201) { serialize_resource(result.value) }
|
|
39
41
|
else
|
|
40
|
-
render_error_payload(
|
|
42
|
+
render_error_payload(result.value.errors, 422)
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
|
|
@@ -30,13 +30,25 @@ module Spree
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
32
|
def collection
|
|
33
|
+
# WARNING: Do NOT remove .order(id: :desc).
|
|
34
|
+
# Mobile relies on this ordering for paginated order_histories.
|
|
35
|
+
# Removing it will cause mobile to paginate by oldest records first (last 10 oldest),
|
|
36
|
+
# breaking the "most recent orders" display on the order history screen.
|
|
37
|
+
# It related to user cancel or complete order flow, which relies on the most recent orders being returned first.
|
|
33
38
|
if spree_current_user.present?
|
|
34
|
-
spree_current_user.orders
|
|
39
|
+
spree_current_user.orders
|
|
40
|
+
.not_archived
|
|
41
|
+
.incomplete
|
|
42
|
+
.order(id: :desc)
|
|
35
43
|
else
|
|
36
44
|
order_tokens = Array(params[:order_tokens])
|
|
37
45
|
return Spree::Order.none if order_tokens.empty?
|
|
38
46
|
|
|
39
|
-
Spree::Order.
|
|
47
|
+
Spree::Order.not_archived
|
|
48
|
+
.incomplete
|
|
49
|
+
.without_user
|
|
50
|
+
.where(token: order_tokens)
|
|
51
|
+
.order(id: :desc)
|
|
40
52
|
end
|
|
41
53
|
end
|
|
42
54
|
|
|
@@ -86,9 +86,10 @@ module Spree
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def collection
|
|
89
|
-
SpreeCmCommissioner::TicketTransfer
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
SpreeCmCommissioner::TicketTransfer
|
|
90
|
+
.includes(:order, from_guest: [:event, { line_item: :product }])
|
|
91
|
+
.where(from_user_id: spree_current_user.id)
|
|
92
|
+
.order(id: :desc)
|
|
92
93
|
end
|
|
93
94
|
|
|
94
95
|
def resource_serializer
|
|
@@ -37,13 +37,25 @@ module Spree
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def collection
|
|
40
|
+
# WARNING: Do NOT remove .order(id: :desc).
|
|
41
|
+
# Mobile relies on this ordering for paginated order_histories.
|
|
42
|
+
# Removing it will cause mobile to paginate by oldest records first (last 10 oldest),
|
|
43
|
+
# breaking the "most recent orders" display on the order history screen.
|
|
44
|
+
# It related to user cancel or complete order flow, which relies on the most recent orders being returned first.
|
|
40
45
|
if spree_current_user.present?
|
|
41
|
-
spree_current_user.orders
|
|
46
|
+
spree_current_user.orders
|
|
47
|
+
.not_archived
|
|
48
|
+
.incomplete
|
|
49
|
+
.order(id: :desc)
|
|
42
50
|
else
|
|
43
51
|
order_tokens = Array(params[:order_tokens])
|
|
44
52
|
return Spree::Order.none if order_tokens.empty?
|
|
45
53
|
|
|
46
|
-
Spree::Order.
|
|
54
|
+
Spree::Order.not_archived
|
|
55
|
+
.incomplete
|
|
56
|
+
.without_user
|
|
57
|
+
.where(token: order_tokens)
|
|
58
|
+
.order(id: :desc)
|
|
47
59
|
end
|
|
48
60
|
end
|
|
49
61
|
|
|
@@ -4,14 +4,30 @@
|
|
|
4
4
|
module SpreeCmCommissioner
|
|
5
5
|
module Orders
|
|
6
6
|
class Find
|
|
7
|
+
CURRENT_CART_STATES = %w[cart address].freeze
|
|
8
|
+
CART_MAX_AGE_IN_MINUTES = ENV.fetch('CART_MAX_AGE_IN_MINUTES', '720').to_i # Default: 12 hours
|
|
9
|
+
|
|
7
10
|
def execute(store:, user:, currency:, token: nil, state: nil)
|
|
11
|
+
state = Array(state).map(&:to_s)
|
|
12
|
+
|
|
8
13
|
params = { store_id: store.id, currency: currency }
|
|
9
14
|
params[:state] = state if state.present?
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
order = if token.present?
|
|
17
|
+
find_by_token(params, token)
|
|
18
|
+
elsif user.present?
|
|
19
|
+
find_by_user(params, user)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Only enforce hold and age expiration when fetching current cart states ('cart' or 'address').
|
|
23
|
+
# For other states (like 'payment,complete'), we want to return the order even if it's old or expired
|
|
24
|
+
# because some pages on client still need some specific state of order to display.
|
|
25
|
+
if state.intersect?(CURRENT_CART_STATES)
|
|
26
|
+
return nil if hold_expired?(order)
|
|
27
|
+
return nil if cart_too_old?(order)
|
|
28
|
+
end
|
|
13
29
|
|
|
14
|
-
|
|
30
|
+
order
|
|
15
31
|
end
|
|
16
32
|
|
|
17
33
|
def find_by_token(params, token)
|
|
@@ -24,6 +40,20 @@ module SpreeCmCommissioner
|
|
|
24
40
|
scope.order(created_at: :desc).find_by(params)
|
|
25
41
|
end
|
|
26
42
|
|
|
43
|
+
def hold_expired?(order)
|
|
44
|
+
return false if order.nil?
|
|
45
|
+
|
|
46
|
+
# Check if the order has a hold_expires_at column and if it has already passed
|
|
47
|
+
order.hold_expires_at.present? && order.hold_expires_at < Time.current
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def cart_too_old?(order)
|
|
51
|
+
return false if order.nil?
|
|
52
|
+
|
|
53
|
+
# Treat the cart as active based on its last update time, not its creation time.
|
|
54
|
+
order.updated_at < CART_MAX_AGE_IN_MINUTES.minutes.ago
|
|
55
|
+
end
|
|
56
|
+
|
|
27
57
|
private
|
|
28
58
|
|
|
29
59
|
def scope
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class PaymentIncompleteNotificationSender < BaseInteractor
|
|
3
|
+
def call
|
|
4
|
+
order = context.notificable
|
|
5
|
+
return if order.user.blank?
|
|
6
|
+
return if order.reload.payment_fulfilled?
|
|
7
|
+
|
|
8
|
+
SpreeCmCommissioner::OrderGeneralNotification.with(
|
|
9
|
+
notificable: order,
|
|
10
|
+
title: title,
|
|
11
|
+
message: message(order)
|
|
12
|
+
).deliver_later(order.user)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def title
|
|
18
|
+
I18n.t('notifications.spree_cm_commissioner.payment_incomplete_notification.title')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def message(order)
|
|
22
|
+
I18n.t('notifications.spree_cm_commissioner.payment_incomplete_notification.message', order_number: order.number)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class ReleaseInventoryItemNotificationSender < BaseInteractor
|
|
3
|
+
def call
|
|
4
|
+
order = context.notificable
|
|
5
|
+
return if order.user.blank?
|
|
6
|
+
|
|
7
|
+
SpreeCmCommissioner::OrderGeneralNotification.with(
|
|
8
|
+
notificable: order,
|
|
9
|
+
title: title,
|
|
10
|
+
message: message(order)
|
|
11
|
+
).deliver_later(order.user)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def title
|
|
17
|
+
I18n.t('notifications.spree_cm_commissioner.release_inventory_item_notification.title')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def message(order)
|
|
21
|
+
I18n.t('notifications.spree_cm_commissioner.release_inventory_item_notification.message', order_number: order.number)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|