spree_cm_commissioner 2.8.6.pre.pre1 → 2.8.7

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.
Files changed (213) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +8 -0
  3. data/.gitignore +0 -6
  4. data/Gemfile.lock +1 -1
  5. data/app/controllers/concerns/spree_cm_commissioner/content_cachable.rb +0 -2
  6. data/app/controllers/spree/admin/homepage_section_controller.rb +1 -4
  7. data/app/controllers/spree/admin/import_new_orders_controller.rb +25 -0
  8. data/app/controllers/spree/admin/taxons_controller_decorator.rb +0 -19
  9. data/app/controllers/spree/api/v2/storefront/account/orders_controller_decorator.rb +5 -0
  10. data/app/controllers/spree/api/v2/storefront/homepage_sections_controller.rb +0 -1
  11. data/app/controllers/spree/api/v2/storefront/order_histories_controller.rb +2 -2
  12. data/app/controllers/spree/api/v2/storefront/ticket_transfers_controller.rb +4 -3
  13. data/app/controllers/spree/api/v2/tenant/base_controller.rb +0 -4
  14. data/app/controllers/spree/api/v2/tenant/homepage_sections_controller.rb +0 -1
  15. data/app/controllers/spree/api/v2/tenant/order_histories_controller.rb +2 -2
  16. data/app/controllers/spree/api/v2/tenant/products_controller.rb +1 -1
  17. data/app/controllers/spree/api/v2/tenant/taxons_controller.rb +1 -1
  18. data/app/controllers/spree_cm_commissioner/admin/products_controller_decorator.rb +0 -19
  19. data/app/finders/spree_cm_commissioner/events/find_matches.rb +0 -1
  20. data/app/finders/spree_cm_commissioner/orders/find.rb +33 -3
  21. data/app/finders/spree_cm_commissioner/orders/find_by_all_state.rb +11 -0
  22. data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +0 -2
  23. data/app/interactors/spree_cm_commissioner/waiting_guests_caller.rb +16 -4
  24. data/app/jobs/concerns/spree_cm_commissioner/idempotent_job.rb +12 -0
  25. data/app/jobs/spree_cm_commissioner/cancel_import_order_job.rb +11 -0
  26. data/app/jobs/spree_cm_commissioner/idempotency_keys/prune_job.rb +20 -0
  27. data/app/jobs/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_job.rb +21 -3
  28. data/app/jobs/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_on_hold_job.rb +34 -3
  29. data/app/jobs/spree_cm_commissioner/telegram_alerts/order_integrity_check_job.rb +17 -0
  30. data/app/jobs/spree_cm_commissioner/waiting_guests_caller_job.rb +5 -0
  31. data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +1 -2
  32. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +1 -2
  33. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +10 -10
  34. data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
  35. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +1 -0
  36. data/app/models/concerns/spree_cm_commissioner/taxon_kind.rb +1 -3
  37. data/app/models/spree_cm_commissioner/idempotency_key.rb +21 -0
  38. data/app/models/spree_cm_commissioner/import.rb +3 -2
  39. data/app/models/spree_cm_commissioner/imported_order.rb +6 -0
  40. data/app/models/spree_cm_commissioner/imports/import_order.rb +21 -0
  41. data/app/models/spree_cm_commissioner/order_decorator.rb +14 -0
  42. data/app/models/spree_cm_commissioner/payment_decorator.rb +1 -0
  43. data/app/models/spree_cm_commissioner/product_decorator.rb +0 -52
  44. data/app/models/spree_cm_commissioner/role_decorator.rb +1 -4
  45. data/app/models/spree_cm_commissioner/taxon_decorator.rb +0 -23
  46. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +1 -19
  47. data/app/models/spree_cm_commissioner/tenant.rb +0 -9
  48. data/app/models/spree_cm_commissioner/user_decorator.rb +0 -17
  49. data/app/models/spree_cm_commissioner/variant_decorator.rb +0 -4
  50. data/app/models/spree_cm_commissioner/variant_options.rb +0 -4
  51. data/app/models/spree_cm_commissioner/vendor_decorator.rb +0 -5
  52. data/app/overrides/spree/admin/products/_form/allow_gift_transfer.html.erb.deface +1 -1
  53. data/app/overrides/spree/admin/products/_form/allow_transfer.html.erb.deface +1 -1
  54. data/app/overrides/spree/admin/stores/_form/store_preferences.html.erb.deface +9 -0
  55. data/app/serializers/spree/v2/storefront/homepage_section_serializer.rb +1 -1
  56. data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +1 -2
  57. data/app/serializers/spree/v2/storefront/role_serializer.rb +1 -1
  58. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +1 -2
  59. data/app/serializers/spree/v2/tenant/homepage_section_serializer.rb +1 -1
  60. data/app/serializers/spree/v2/tenant/role_serializer.rb +1 -1
  61. data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +0 -12
  62. data/app/services/spree_cm_commissioner/imports/orders/cancel.rb +63 -0
  63. data/app/services/spree_cm_commissioner/imports/orders/create.rb +1 -0
  64. data/app/services/spree_cm_commissioner/telegram_alerts/checks/order_complete_payment_not_paid.rb +21 -0
  65. data/app/services/spree_cm_commissioner/telegram_alerts/checks/payment_paid_order_not_complete.rb +21 -0
  66. data/app/services/spree_cm_commissioner/telegram_alerts/order_integrity_alert.rb +112 -0
  67. data/app/services/spree_cm_commissioner/telegram_alerts/order_integrity_checks_runner.rb +32 -0
  68. data/app/services/spree_cm_commissioner/waiting_room/stamp_queue_positions.rb +66 -24
  69. data/app/views/spree/admin/homepage_section/_form.html.erb +0 -5
  70. data/app/views/spree/admin/import_new_orders/_cancel_modal.html.erb +35 -0
  71. data/app/views/spree/admin/import_new_orders/index.html.erb +4 -0
  72. data/app/views/spree/admin/import_new_orders/show.html.erb +30 -0
  73. data/app/views/spree/admin/imports/index.html.erb +2 -0
  74. data/app/views/spree/admin/inventory_holds/index.html.erb +8 -3
  75. data/config/initializers/spree_permitted_attributes.rb +1 -8
  76. data/config/locales/en.yml +7 -49
  77. data/config/locales/km.yml +5 -36
  78. data/config/routes.rb +1 -29
  79. data/db/migrate/20260616000001_add_canceled_by_id_to_cm_imports.rb +5 -0
  80. data/db/migrate/20260617000002_create_cm_imported_orders.rb +12 -0
  81. data/db/migrate/20260619000001_create_cm_idempotency_keys.rb +11 -0
  82. data/lib/spree_cm_commissioner/version.rb +1 -1
  83. data/lib/spree_cm_commissioner.rb +1 -7
  84. data/spree_cm_commissioner.gemspec +1 -1
  85. metadata +20 -133
  86. data/app/controllers/spree/api/v2/storefront/preview_products_controller.rb +0 -48
  87. data/app/controllers/spree/api/v2/storefront/preview_sections_controller.rb +0 -27
  88. data/app/controllers/spree/api/v2/storefront/preview_taxons_controller.rb +0 -18
  89. data/app/controllers/spree/api/v2/storefront/products_controller_decorator.rb +0 -15
  90. data/app/controllers/spree/api/v2/storefront/taxons_controller_decorator.rb +0 -15
  91. data/app/controllers/spree/api/v2/tenant/ad_campaigns_controller.rb +0 -25
  92. data/app/controllers/spree/api/v2/tenant/episodes_controller.rb +0 -38
  93. data/app/controllers/spree/api/v2/tenant/free_vote_claim_controller.rb +0 -58
  94. data/app/controllers/spree/api/v2/tenant/preview_products_controller.rb +0 -47
  95. data/app/controllers/spree/api/v2/tenant/preview_sections_controller.rb +0 -26
  96. data/app/controllers/spree/api/v2/tenant/preview_shows_controller.rb +0 -19
  97. data/app/controllers/spree/api/v2/tenant/preview_taxons_controller.rb +0 -19
  98. data/app/controllers/spree/api/v2/tenant/show_contestants_controller.rb +0 -52
  99. data/app/controllers/spree/api/v2/tenant/show_elimination_sessions_controller.rb +0 -57
  100. data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +0 -49
  101. data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +0 -36
  102. data/app/controllers/spree/api/v2/tenant/shows_controller.rb +0 -36
  103. data/app/controllers/spree/api/v2/tenant/vote_packages_controller.rb +0 -33
  104. data/app/controllers/spree/api/v2/tenant/votes_controller.rb +0 -94
  105. data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +0 -43
  106. data/app/controllers/spree/api/v2/tenant/voting_credit_transactions_controller.rb +0 -41
  107. data/app/controllers/spree/api/v2/tenant/voting_credits_controller.rb +0 -31
  108. data/app/jobs/spree_cm_commissioner/show_contestants/import_job.rb +0 -9
  109. data/app/jobs/spree_cm_commissioner/vote_fraud_event_job.rb +0 -9
  110. data/app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb +0 -10
  111. data/app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb +0 -10
  112. data/app/models/concerns/spree_cm_commissioner/voting_session_live_stream_concern.rb +0 -41
  113. data/app/models/spree_cm_commissioner/imports/import_contestant.rb +0 -7
  114. data/app/models/spree_cm_commissioner/maintenance_tasks/voting_session.rb +0 -36
  115. data/app/models/spree_cm_commissioner/preview_role.rb +0 -8
  116. data/app/models/spree_cm_commissioner/role_user_decorator.rb +0 -8
  117. data/app/models/spree_cm_commissioner/show.rb +0 -169
  118. data/app/models/spree_cm_commissioner/show_contestant.rb +0 -39
  119. data/app/models/spree_cm_commissioner/show_contestant_image.rb +0 -11
  120. data/app/models/spree_cm_commissioner/show_contestant_video.rb +0 -15
  121. data/app/models/spree_cm_commissioner/show_episode.rb +0 -108
  122. data/app/models/spree_cm_commissioner/show_person.rb +0 -15
  123. data/app/models/spree_cm_commissioner/show_person_assignment.rb +0 -21
  124. data/app/models/spree_cm_commissioner/show_person_image.rb +0 -11
  125. data/app/models/spree_cm_commissioner/vote.rb +0 -16
  126. data/app/models/spree_cm_commissioner/vote_fraud_event.rb +0 -20
  127. data/app/models/spree_cm_commissioner/voting_contestant.rb +0 -100
  128. data/app/models/spree_cm_commissioner/voting_credit.rb +0 -72
  129. data/app/models/spree_cm_commissioner/voting_credit_transaction.rb +0 -55
  130. data/app/models/spree_cm_commissioner/voting_session.rb +0 -216
  131. data/app/models/spree_cm_commissioner/voting_session_stat.rb +0 -8
  132. data/app/overrides/spree/admin/products/_form/preview_checkbox.html.erb.deface +0 -9
  133. data/app/overrides/spree/admin/taxons/_form/preview_checkbox.html.erb.deface +0 -7
  134. data/app/serializers/spree/v2/tenant/ad_campaign_serializer.rb +0 -13
  135. data/app/serializers/spree/v2/tenant/advertisement_serializer.rb +0 -11
  136. data/app/serializers/spree/v2/tenant/free_vote_claim_serializer.rb +0 -9
  137. data/app/serializers/spree/v2/tenant/show_contestant_serializer.rb +0 -21
  138. data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +0 -20
  139. data/app/serializers/spree/v2/tenant/show_parent_serializer.rb +0 -13
  140. data/app/serializers/spree/v2/tenant/show_person_assignment_serializer.rb +0 -16
  141. data/app/serializers/spree/v2/tenant/show_person_serializer.rb +0 -13
  142. data/app/serializers/spree/v2/tenant/show_serializer.rb +0 -23
  143. data/app/serializers/spree/v2/tenant/video_serializer.rb +0 -9
  144. data/app/serializers/spree/v2/tenant/vote_package_serializer.rb +0 -12
  145. data/app/serializers/spree/v2/tenant/vote_serializer.rb +0 -14
  146. data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +0 -23
  147. data/app/serializers/spree/v2/tenant/voting_credit_serializer.rb +0 -10
  148. data/app/serializers/spree/v2/tenant/voting_credit_transaction_serializer.rb +0 -14
  149. data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +0 -21
  150. data/app/services/spree_cm_commissioner/fraud_check.rb +0 -299
  151. data/app/services/spree_cm_commissioner/imports/contestants/create.rb +0 -153
  152. data/app/services/spree_cm_commissioner/show_contestants/normalize_video_highlights.rb +0 -57
  153. data/app/services/spree_cm_commissioner/show_persons/contestant_assigner.rb +0 -51
  154. data/app/services/spree_cm_commissioner/url_embed/youtube_embed.rb +0 -44
  155. data/app/services/spree_cm_commissioner/vote_counters/audit_counters.rb +0 -43
  156. data/app/services/spree_cm_commissioner/vote_counters/base.rb +0 -31
  157. data/app/services/spree_cm_commissioner/vote_counters/increment.rb +0 -44
  158. data/app/services/spree_cm_commissioner/vote_counters/per_contestant_counter.rb +0 -68
  159. data/app/services/spree_cm_commissioner/vote_counters/rebuild_from_db.rb +0 -70
  160. data/app/services/spree_cm_commissioner/vote_counters/snapshot_to_db.rb +0 -113
  161. data/app/services/spree_cm_commissioner/vote_credit_deductor.rb +0 -68
  162. data/app/services/spree_cm_commissioner/vote_package/create.rb +0 -145
  163. data/app/services/spree_cm_commissioner/vote_package/update.rb +0 -91
  164. data/app/services/spree_cm_commissioner/vote_processor.rb +0 -144
  165. data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +0 -335
  166. data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +0 -40
  167. data/app/services/spree_cm_commissioner/voting_contestants/bulk_updater.rb +0 -106
  168. data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +0 -77
  169. data/app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb +0 -116
  170. data/app/services/spree_cm_commissioner/voting_credits/credit_calculator.rb +0 -35
  171. data/app/services/spree_cm_commissioner/voting_credits/de_allocate.rb +0 -87
  172. data/app/services/spree_cm_commissioner/voting_leaderboards/calculate_score.rb +0 -67
  173. data/app/services/spree_cm_commissioner/voting_leaderboards/combined_result.rb +0 -190
  174. data/app/services/spree_cm_commissioner/voting_sessions/finalize.rb +0 -105
  175. data/db/migrate/20260309230148_create_cm_show_people.rb +0 -14
  176. data/db/migrate/20260309230149_create_cm_show_people_assignments.rb +0 -16
  177. data/db/migrate/20260310082711_create_cm_show_contestants.rb +0 -28
  178. data/db/migrate/20260310082720_create_cm_voting_sessions.rb +0 -21
  179. data/db/migrate/20260310082721_create_cm_voting_contestants.rb +0 -23
  180. data/db/migrate/20260310082734_add_voting_fields_to_spree_taxons.rb +0 -9
  181. data/db/migrate/20260310082735_add_type_to_spree_products.rb +0 -6
  182. data/db/migrate/20260310082749_create_cm_voting_credits.rb +0 -27
  183. data/db/migrate/20260326080200_create_cm_voting_credit_transactions.rb +0 -27
  184. data/db/migrate/20260330160000_create_cm_votes.rb +0 -25
  185. data/db/migrate/20260401072500_add_advanced_from_to_cm_voting_contestants.rb +0 -7
  186. data/db/migrate/20260402000001_add_voting_credit_scope_to_spree_taxons.rb +0 -6
  187. data/db/migrate/20260402000002_rename_scopeable_to_votable_in_cm_voting_credits.rb +0 -12
  188. data/db/migrate/20260403070000_add_name_to_cm_voting_sessions.rb +0 -5
  189. data/db/migrate/20260406000001_add_vendor_id_to_voting_tables.rb +0 -6
  190. data/db/migrate/20260406000001_rename_votes_remaining_to_amount_in_cm_voting_credits.rb +0 -11
  191. data/db/migrate/20260408085255_add_show_id_and_vendor_id_to_cm_voting_sessions.rb +0 -9
  192. data/db/migrate/20260420000001_rename_type_to_credit_type_in_cm_voting_credits.rb +0 -25
  193. data/db/migrate/20260422000001_create_cm_vote_fraud_events.rb +0 -23
  194. data/db/migrate/20260423000001_add_preview_to_taxons_products_and_sections.rb +0 -11
  195. data/db/migrate/20260423000002_create_preview_roles.rb +0 -24
  196. data/db/migrate/20260515120000_add_public_metadata_to_cm_voting_sessions.rb +0 -5
  197. data/db/migrate/20260518090920_add_unique_voter_count_to_voting_contestants.rb +0 -5
  198. data/db/migrate/20260518094322_create_cm_voting_session_stats.rb +0 -17
  199. data/db/migrate/20260520000001_add_scoring_model_to_cm_voting_sessions.rb +0 -5
  200. data/db/migrate/20260520000001_optimize_cm_votes_indexes.rb +0 -22
  201. data/db/migrate/20260525042257_add_vote_number_to_cm_voting_contestants.rb +0 -18
  202. data/db/migrate/20260527035430_add_confirmed_rank_to_cm_voting_contestants.rb +0 -5
  203. data/db/migrate/20260527062005_add_eliminated_at_to_cm_show_contestants.rb +0 -5
  204. data/db/migrate/20260603063652_add_parent_to_voting_sessions.rb +0 -9
  205. data/db/migrate/20260603090000_add_session_type_to_cm_voting_sessions.rb +0 -6
  206. data/db/migrate/20260608000000_add_display_to_cm_voting_sessions.rb +0 -5
  207. data/docs/sql/jsonb_query_guide.md +0 -57
  208. data/lib/spree_cm_commissioner/test_helper/factories/show_episode_factory.rb +0 -12
  209. data/lib/spree_cm_commissioner/test_helper/factories/show_factory.rb +0 -120
  210. data/lib/spree_cm_commissioner/test_helper/factories/vote_credit_factory.rb +0 -37
  211. data/lib/spree_cm_commissioner/test_helper/factories/vote_factory.rb +0 -28
  212. data/lib/spree_cm_commissioner/test_helper/factories/voting_credit_transaction_factory.rb +0 -11
  213. data/lib/spree_cm_commissioner/test_helper/factories/voting_session_factory.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a85bc5a3c971cde57bbe6e2549e83b5fed6e70b06cf07e46dc31eb04de53b6ca
4
- data.tar.gz: 694d562a264083048a0111ff61ddf828892ff39660c596ecc292d150df182a1e
3
+ metadata.gz: 4f8d11251736536036263c451dde5cb82e34a6d54b12fe116866a338346d8097
4
+ data.tar.gz: f1cec56cfe9db369b4bf70a40c8545e09fd2b89a9d0fd1e2d0547cf4818a04eb
5
5
  SHA512:
6
- metadata.gz: d126f5f0121b305ec904bea9370449323d8383f547373cf76440d42d18fba6c92eafa153023d61b76a679eb1239edbfe41c810c97d801b8e34db2c7f4fabf52e
7
- data.tar.gz: 67f9eecfb1909d3b7b53c91029cd242ba8f279d0fc77fb63ceac338e451b8bb71efcfbe92d6a409573a9b0955f0af9ab7edb4e509b5d880fee57631004d5c02e
6
+ metadata.gz: b0d1ef1dca4921e6493b8455b31c31b9289444aa64b3d5378dc6d87c0676c081ee2c0148dfd3a46fb8572d30fb8fb86d6c53a24249768b3b9001fbb3f2f6a4fa
7
+ data.tar.gz: a3dfc479c74f38e3889e84ab72246fc01e31134a630563f4f3bf2b3713e3e13d2df1389a0bdf1aae4337e56d891cb6a6dc29e21570d28b84d9d75a5b1d1cd152
data/.env.example CHANGED
@@ -22,6 +22,11 @@ WAITING_ROOM_MIN_SESSIONS_COUNT=5
22
22
  WAITING_ROOM_DISABLED=no
23
23
  WAITING_ROOM_POSITION_STAMP_LIMIT=1000
24
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
25
30
 
26
31
  # Vattanac Bank
27
32
  VATTANAC_AES_SECRET_KEY= ""
@@ -68,3 +73,6 @@ HOLD_COOLDOWN_AFTER_EXPIRY_IN_MINUTES=2 # Cooldown period before a user can
68
73
 
69
74
  # See: app/models/spree_cm_commissioner/payment_decorator.rb
70
75
  PAYMENT_INCOMPLETE_NOTIFICATION_DELAY_IN_MINUTES=5 # Delay before notifying a customer that their order is still unpaid after payment is created
76
+
77
+ # See: app/jobs/spree_cm_commissioner/telegram_alerts/order_integrity_check_job.rb
78
+ 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)
data/.gitignore CHANGED
@@ -34,9 +34,3 @@ vendor/bundle/
34
34
  # Cursor
35
35
  .cursor
36
36
  dump.rdb
37
- # Added by code-review-graph
38
- .code-review-graph/
39
- .cursorrules
40
- .claude
41
- CLAUDE.md
42
- AGENTS.md
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.8.6.pre.pre1)
37
+ spree_cm_commissioner (2.8.7)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -16,7 +16,6 @@ module SpreeCmCommissioner
16
16
  GuestCardClassesController
17
17
  SeatLayoutController
18
18
  CmsPagesController
19
- AdvertisementsController
20
19
  ].freeze
21
20
 
22
21
  # Priority 2: Semi-Static (1 hour)
@@ -26,7 +25,6 @@ module SpreeCmCommissioner
26
25
  HomepageDataController
27
26
  PopularRoutesController
28
27
  RoutePlacesController
29
- ShowsController
30
28
  ].freeze
31
29
 
32
30
  # Priority 3: Moderate Freshness (30 minutes)
@@ -51,10 +51,7 @@ module Spree
51
51
  def permitted_resource_params
52
52
  segment_value = helpers.calculate_segment_value(params[:spree_cm_commissioner_homepage_section])
53
53
 
54
- params
55
- .require(:spree_cm_commissioner_homepage_section)
56
- .permit(:title, :description, :active, :tenant_id, :preview)
57
- .merge(segment: segment_value)
54
+ params.require(:spree_cm_commissioner_homepage_section).permit(:title, :description, :active, :tenant_id).merge(segment: segment_value)
58
55
  end
59
56
 
60
57
  def load_tenants
@@ -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
@@ -3,7 +3,6 @@ module Spree
3
3
  module TaxonsControllerDecorator
4
4
  def self.prepended(base)
5
5
  base.before_action :build_assets, only: %i[create update]
6
- base.before_action :normalize_sti_taxon_params, only: %i[create update]
7
6
  end
8
7
 
9
8
  # override
@@ -56,24 +55,6 @@ module Spree
56
55
  @taxon.build_ad_banner(attachment: permitted_resource_params.delete(:ad_banner)) if permitted_resource_params[:ad_banner]
57
56
  @taxon.build_video_banner(attachment: permitted_resource_params.delete(:video_banner)) if permitted_resource_params[:video_banner]
58
57
  end
59
-
60
- # Some admin forms submit STI records under their class key
61
- # (e.g. :spree_cm_commissioner_show) while Spree update reads :taxon.
62
- # Merge both into :taxon so all edited fields are persisted.
63
- def normalize_sti_taxon_params
64
- return unless @taxon
65
-
66
- sti_key = @taxon.class.model_name.param_key.to_sym
67
- sti_params = params[sti_key]
68
- return unless sti_params.is_a?(::ActionController::Parameters)
69
-
70
- base_params = params[:taxon]
71
- base_hash = base_params.is_a?(::ActionController::Parameters) ? base_params.to_unsafe_h : {}
72
- sti_hash = sti_params.to_unsafe_h
73
- merged_params = ::ActionController::Parameters.new(base_hash.merge(sti_hash))
74
-
75
- params[:taxon] = merged_params
76
- end
77
58
  end
78
59
  end
79
60
  end
@@ -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
@@ -19,7 +19,6 @@ module Spree
19
19
  @collection ||= model_class.filter_by_segment(params[:homepage_id] || :general)
20
20
  .active
21
21
  .where(tenant_id: nil)
22
- .where(preview: false)
23
22
  .order(position: :asc)
24
23
  end
25
24
  end
@@ -31,12 +31,12 @@ module Spree
31
31
 
32
32
  def collection
33
33
  if spree_current_user.present?
34
- spree_current_user.orders.payment.not_archived
34
+ spree_current_user.orders.not_archived
35
35
  else
36
36
  order_tokens = Array(params[:order_tokens])
37
37
  return Spree::Order.none if order_tokens.empty?
38
38
 
39
- Spree::Order.payment.not_archived.without_user.where(token: order_tokens)
39
+ Spree::Order.not_archived.without_user.where(token: order_tokens)
40
40
  end
41
41
  end
42
42
 
@@ -86,9 +86,10 @@ module Spree
86
86
  end
87
87
 
88
88
  def collection
89
- SpreeCmCommissioner::TicketTransfer.includes(:order,
90
- from_guest: [:event, { line_item: :product }]
91
- ).where(from_user_id: spree_current_user.id)
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
@@ -16,10 +16,6 @@ module Spree
16
16
  set_current_tenant(@tenant)
17
17
  end
18
18
 
19
- def current_vendor
20
- @current_vendor ||= @tenant.active_vendor
21
- end
22
-
23
19
  def render_serialized_payload(status = 200)
24
20
  render json: yield, status: status, content_type: content_type
25
21
  end
@@ -5,7 +5,6 @@ module Spree
5
5
  class HomepageSectionsController < BaseController
6
6
  def collection
7
7
  @collection ||= scope.filter_by_segment(params[:homepage_id] || :general)
8
- .where(preview: false)
9
8
  .active
10
9
  .order(position: :asc)
11
10
  end
@@ -38,12 +38,12 @@ module Spree
38
38
 
39
39
  def collection
40
40
  if spree_current_user.present?
41
- spree_current_user.orders.payment.not_archived
41
+ spree_current_user.orders.not_archived
42
42
  else
43
43
  order_tokens = Array(params[:order_tokens])
44
44
  return Spree::Order.none if order_tokens.empty?
45
45
 
46
- Spree::Order.payment.not_archived.without_user.where(token: order_tokens)
46
+ Spree::Order.not_archived.without_user.where(token: order_tokens)
47
47
  end
48
48
  end
49
49
 
@@ -21,7 +21,7 @@ module Spree
21
21
 
22
22
  def scope
23
23
  MultiTenant.with(@tenant) do
24
- model_class.where(tenant_id: MultiTenant.current_tenant_id, preview: false)
24
+ model_class.where(tenant_id: MultiTenant.current_tenant_id)
25
25
  end
26
26
  end
27
27
 
@@ -31,7 +31,7 @@ module Spree
31
31
 
32
32
  def scope
33
33
  MultiTenant.with(@tenant) do
34
- model_class.where(preview: false)
34
+ model_class
35
35
  end
36
36
  end
37
37
 
@@ -6,7 +6,6 @@ module SpreeCmCommissioner
6
6
  base.around_action :set_writing_role, only: %i[index]
7
7
  base.after_action :set_tenant_after_update, only: %i[update]
8
8
  base.before_action :merge_industry_taxons_into_taxons, only: [:update]
9
- base.before_action :normalize_sti_product_params, only: %i[create update]
10
9
  end
11
10
 
12
11
  # Override
@@ -52,24 +51,6 @@ module SpreeCmCommissioner
52
51
 
53
52
  protected
54
53
 
55
- # Some admin forms submit STI records under their class key while
56
- # Spree update reads :product. Merge both into :product so all edited
57
- # fields are persisted for STI product subclasses.
58
- def normalize_sti_product_params
59
- return unless @product
60
-
61
- sti_key = @product.class.model_name.param_key.to_sym
62
- sti_params = params[sti_key]
63
- return unless sti_params.is_a?(::ActionController::Parameters)
64
-
65
- base_params = params[:product]
66
- base_hash = base_params.is_a?(::ActionController::Parameters) ? base_params.to_unsafe_h : {}
67
- sti_hash = sti_params.to_unsafe_h
68
- merged_params = ::ActionController::Parameters.new(base_hash.merge(sti_hash))
69
-
70
- params[:product] = merged_params
71
- end
72
-
73
54
  def merge_industry_taxons_into_taxons
74
55
  return if params[:product].blank?
75
56
 
@@ -7,7 +7,6 @@ module SpreeCmCommissioner
7
7
  def execute
8
8
  scope.joins(:integration_mappings).where(
9
9
  kind: :event,
10
- preview: false,
11
10
  integration_mappings: { status: :active }
12
11
  ).distinct
13
12
  end
@@ -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
- return find_by_token(params, token) if token.present?
12
- return find_by_user(params, user) if user.present?
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
- nil
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,11 @@
1
+ module SpreeCmCommissioner
2
+ module Orders
3
+ class FindByAllState < Spree::Orders::FindComplete
4
+ private
5
+
6
+ def scope
7
+ user? ? user.orders.includes(scope_includes) : Spree::Order.includes(scope_includes)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -15,8 +15,6 @@ module SpreeCmCommissioner
15
15
  'badge badge-success text-uppercase'
16
16
  when :football
17
17
  'badge badge-dark text-uppercase'
18
- when :show
19
- 'badge badge-danger text-uppercase'
20
18
  else
21
19
  'badge'
22
20
  end
@@ -3,8 +3,11 @@ require 'google/cloud/firestore'
3
3
  # TODO: alert when available_slots is negative.
4
4
  module SpreeCmCommissioner
5
5
  class WaitingGuestsCaller < BaseInteractor
6
- MIN_WAIT_TO_ENTER_SECONDS = 120 # 2 min floor — Waiting Room step (full journey, more uncertainty)
7
- MIN_QUEUE_TO_ENTER_SECONDS = 60 # 1 min floor — Queue step (position assigned, one caller cycle minimum)
6
+ # 10 min floor — Waiting Room step (full journey, most uncertainty)
7
+ MIN_WAIT_TO_ENTER_SECONDS = (ENV['WAITING_ROOM_MIN_WAIT_TO_ENTER_SECONDS'] || 600).to_i
8
+
9
+ # 5 min floor — Queue step (position assigned, sub-segment of the journey)
10
+ MIN_QUEUE_TO_ENTER_SECONDS = (ENV['WAITING_ROOM_MIN_QUEUE_TO_ENTER_SECONDS'] || 300).to_i
8
11
 
9
12
  # Firestore bounds a batch update by payload size (10 MiB); 500 ops/commit leaves us far under that.
10
13
  FIRESTORE_BATCH_SIZE = (ENV['WAITING_ROOM_FIRESTORE_BATCH_SIZE'] || 500).to_i
@@ -20,7 +23,8 @@ module SpreeCmCommissioner
20
23
  full: long_waiting_guests.size >= available_slots,
21
24
  available_slots: available_slots - long_waiting_guests.size,
22
25
  avg_wait_to_enter_seconds: compute_avg_wait_to_enter_seconds(long_waiting_guests),
23
- avg_queue_to_enter_seconds: compute_avg_queue_to_enter_seconds(long_waiting_guests)
26
+ avg_queue_to_enter_seconds: compute_avg_queue_to_enter_seconds(long_waiting_guests),
27
+ slots_per_call: available_slots
24
28
  )
25
29
  end
26
30
 
@@ -96,10 +100,18 @@ module SpreeCmCommissioner
96
100
  end
97
101
 
98
102
  # merge: true so we preserve the published `waiting_guests_records_path` on the lobby doc.
99
- def mark_as(full:, available_slots:, avg_wait_to_enter_seconds: nil, avg_queue_to_enter_seconds: nil)
103
+ def mark_as(
104
+ full:,
105
+ available_slots:,
106
+ avg_wait_to_enter_seconds: nil,
107
+ avg_queue_to_enter_seconds: nil,
108
+ slots_per_call: nil
109
+ )
100
110
  data = { full: full, available_slots: available_slots }
101
111
  data[:avg_wait_to_enter_seconds] = avg_wait_to_enter_seconds if avg_wait_to_enter_seconds
102
112
  data[:avg_queue_to_enter_seconds] = avg_queue_to_enter_seconds if avg_queue_to_enter_seconds
113
+ data[:slots_per_call] = slots_per_call if slots_per_call
114
+
103
115
  lobby_document.set(data, merge: true)
104
116
  end
105
117
 
@@ -0,0 +1,12 @@
1
+ module SpreeCmCommissioner
2
+ module IdempotentJob
3
+ def with_idempotency(key = nil, &block)
4
+ SpreeCmCommissioner::IdempotencyKey.run_once(key.presence || default_idempotency_key, &block)
5
+ end
6
+
7
+ # Namespaced so the key is self-describing in the DB (no `source` column needed).
8
+ def default_idempotency_key
9
+ "#{self.class.name}:#{job_id}"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module SpreeCmCommissioner
2
+ class CancelImportOrderJob < ApplicationUniqueJob
3
+ def perform(options = {})
4
+ SpreeCmCommissioner::Imports::Orders::Cancel.new(
5
+ import_order_id: options[:import_order_id],
6
+ canceled_by_user_id: options[:canceled_by_user_id],
7
+ cancellation_reason: options[:cancellation_reason]
8
+ ).call
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # Prunes old idempotency keys. Keys only matter within the retry window
2
+ # (minutes–hours), so anything older than the cutoff is safe to delete.
3
+ # The `created_at` index supports the delete.
4
+ module SpreeCmCommissioner
5
+ module IdempotencyKeys
6
+ class PruneJob < SpreeCmCommissioner::ApplicationJob
7
+ DEFAULT_CUTOFF_DAYS = 3
8
+
9
+ queue_as :default
10
+
11
+ def perform(cutoff_days = DEFAULT_CUTOFF_DAYS)
12
+ days = [cutoff_days.to_i, 1].max
13
+
14
+ SpreeCmCommissioner::IdempotencyKey
15
+ .where(created_at: ...days.days.ago)
16
+ .delete_all
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,12 +1,30 @@
1
1
  module SpreeCmCommissioner
2
2
  module InventoryItems
3
3
  class BulkAdjustQuantitiesJob < ApplicationUniqueJob
4
+ include SpreeCmCommissioner::IdempotentJob
5
+
6
+ def perform(options = {})
7
+ with_idempotency(idempotency_key_for(options)) do
8
+ bulk_adjust_quantities!(options)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ # Dedup on the line items being synced, so a re-pushed payload for the same
15
+ # line items no-ops. An explicit key wins; with no line items we fall back to
16
+ # the per-enqueue job_id (returning nil lets with_idempotency apply that default).
17
+ def idempotency_key_for(options)
18
+ return options[:idempotency_key] if options[:idempotency_key].present?
19
+ return nil if options[:line_item_ids].blank?
20
+
21
+ "line_item:#{options[:line_item_ids].compact.uniq.sort.join(':')}"
22
+ end
23
+
4
24
  # :line_item_ids, :inventory_id_and_quantities, :caller_source
5
25
  #
6
- # :line_item_ids is included for unique job key generation to prevent duplicate jobs,
7
- # though it's not used in the perform method implementation.
8
26
  # :caller_source is a string like "ClassName#method" identifying who enqueued the job.
9
- def perform(options = {})
27
+ def bulk_adjust_quantities!(options = {})
10
28
  SpreeCmCommissioner::InventoryItems::BulkAdjustQuantities.call!(
11
29
  inventory_id_and_quantities: options[:inventory_id_and_quantities],
12
30
  caller_source: options[:caller_source]
@@ -1,14 +1,45 @@
1
1
  module SpreeCmCommissioner
2
2
  module InventoryItems
3
3
  class BulkAdjustQuantitiesOnHoldJob < ApplicationUniqueJob
4
+ include SpreeCmCommissioner::IdempotentJob
5
+
4
6
  queue_as :default
5
7
 
8
+ def perform(options = {})
9
+ with_idempotency(idempotency_key_for(options)) do
10
+ bulk_adjust_quantities_on_hold(options)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ # Dedup on the order + the exact adjustment set, so a re-pushed identical
17
+ # on-hold payload no-ops while a genuinely different one re-applies. An explicit
18
+ # key wins; with no order_id we fall back to the per-enqueue job_id (returning
19
+ # nil lets with_idempotency apply that default).
20
+ def idempotency_key_for(options)
21
+ return options[:idempotency_key] if options[:idempotency_key].present?
22
+ return nil if options[:order_id].blank?
23
+
24
+ "order:#{options[:order_id]}:hold:#{adjustments_fingerprint(options[:inventory_id_and_quantities])}"
25
+ end
26
+
27
+ # Readable, order-independent fingerprint of the adjustment payload, e.g. "1=2,2=-1".
28
+ # Delimiter hierarchy: ":" separates key fields, "," separates adjustments, "=" pairs id/qty.
29
+ def adjustments_fingerprint(adjustments)
30
+ Array(adjustments)
31
+ .map { |adj| adj.with_indifferent_access.values_at(:inventory_id, :quantity) }
32
+ .sort_by { |inventory_id, quantity| [inventory_id.to_s, quantity.to_s] }
33
+ .map { |inventory_id, quantity| "#{inventory_id}=#{quantity}" }
34
+ .join(',')
35
+ end
36
+
6
37
  # :order_id, :inventory_id_and_quantities, :caller_source
7
38
  #
8
- # :order_id is included for unique job key generation to prevent duplicate jobs,
9
- # though it's not used in the perform method implementation.
39
+ # :order_id is used for unique idempotency key generation to prevent duplicate
40
+ # processing, though it's not used in the adjustment itself.
10
41
  # :caller_source is a string like "ClassName#method" identifying who enqueued the job.
11
- def perform(options = {})
42
+ def bulk_adjust_quantities_on_hold(options = {})
12
43
  raise ArgumentError, 'order_id is required' if options[:order_id].blank?
13
44
  raise ArgumentError, 'inventory_id_and_quantities is required' if options[:inventory_id_and_quantities].blank?
14
45
 
@@ -0,0 +1,17 @@
1
+ module SpreeCmCommissioner
2
+ module TelegramAlerts
3
+ class OrderIntegrityCheckJob < ApplicationUniqueJob
4
+ queue_as :telegram_bot
5
+ # retry: 0 sends failed jobs straight to the dead queue, avoiding retry storms.
6
+ # Guarded since sidekiq_options requires the host app's Sidekiq integration.
7
+ sidekiq_options retry: 0 if respond_to?(:sidekiq_options)
8
+
9
+ def perform(order_id:)
10
+ order = Spree::Order.find_by(id: order_id)
11
+ return unless order
12
+
13
+ SpreeCmCommissioner::TelegramAlerts::OrderIntegrityChecksRunner.call(order: order)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,11 @@
1
+ # Scheduled as a cron job (see cm-market-server/config/schedule.yml):
1
2
  # waiting_guests_caller:
2
3
  # cron: "*/1 * * * *" # Every minute
3
4
  # class: "SpreeCmCommissioner::WaitingGuestsCallerJob"
5
+ #
6
+ # NOTE: the queue ETA assumes one batch is released per run, so this interval must match
7
+ # SpreeCmCommissioner::WaitingRoom::StampQueuePositions::CALLER_INTERVAL_SECONDS
8
+ # (env WAITING_ROOM_CALLER_INTERVAL_SECONDS). If you change the cron, update that too.
4
9
  module SpreeCmCommissioner
5
10
  class WaitingGuestsCallerJob < ApplicationUniqueJob
6
11
  queue_as :waiting_room
@@ -8,8 +8,7 @@ module SpreeCmCommissioner
8
8
  tour: 0b00100,
9
9
  accommodation: 0b01000,
10
10
  things_to_do: 0b10000,
11
- football: 0b100000,
12
- show: 0b1000000
11
+ football: 0b100000
13
12
  }.freeze
14
13
 
15
14
  BIT_SEGMENT.each do |segment, bit_value|
@@ -45,8 +45,7 @@ module SpreeCmCommissioner
45
45
  'color' => 'color',
46
46
  'ticket-type' => 'string',
47
47
  'seat-type' => 'string',
48
- 'intercity-taxi' => 'string',
49
- 'vote-package' => 'integer'
48
+ 'intercity-taxi' => 'string'
50
49
  }.freeze
51
50
 
52
51
  included do
@@ -26,10 +26,9 @@ module SpreeCmCommissioner
26
26
 
27
27
  state_machine.after_transition to: :canceled, do: :precalculate_conversion
28
28
  state_machine.after_transition to: :canceled, do: :restock_inventory!
29
- state_machine.after_transition to: :canceled, do: :de_allocate_resources
30
29
 
31
30
  state_machine.after_transition to: :complete, do: :increment_route_fulfilled_order_count
32
- state_machine.after_transition to: :complete, do: :allocate_resources
31
+ state_machine.after_transition to: :complete, do: :run_order_complete_alerts
33
32
 
34
33
  scope :accepted, -> { where(request_state: 'accepted') }
35
34
 
@@ -324,19 +323,20 @@ module SpreeCmCommissioner
324
323
  TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory.message, parse_mode: factory.parse_mode)
325
324
  end
326
325
 
326
+ def run_order_complete_alerts
327
+ return unless ENV['ORDER_INTEGRITY_TELEGRAM_ALERT_ENABLED'] == 'yes'
328
+
329
+ # Allow async payment processors time to settle before checking for alerts
330
+ SpreeCmCommissioner::TelegramAlerts::OrderIntegrityCheckJob
331
+ .set(wait: 2.minutes)
332
+ .perform_later(order_id: id)
333
+ end
334
+
327
335
  def send_order_complete_telegram_alert_to_store
328
336
  title = '🎫 --- [NEW ORDER] ---'
329
337
  chat_id = store.preferred_telegram_order_alert_chat_id
330
338
  factory = OrderTelegramMessageFactory.new(title: title, order: self)
331
339
  TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory.message, parse_mode: factory.parse_mode)
332
340
  end
333
-
334
- def allocate_resources
335
- SpreeCmCommissioner::VotingCreditAllocationJob.perform_later(order_id: id)
336
- end
337
-
338
- def de_allocate_resources
339
- SpreeCmCommissioner::VotingCreditDeAllocationJob.perform_later(order_id: id)
340
- end
341
341
  end
342
342
  end