spree_cm_commissioner 2.8.4.pre.pre3 → 2.8.4.pre.pre4

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +5 -0
  3. data/.github/workflows/test_and_build_gem.yml +1 -2
  4. data/.gitignore +0 -6
  5. data/Gemfile.lock +1 -1
  6. data/app/controllers/concerns/spree_cm_commissioner/content_cachable.rb +0 -2
  7. data/app/controllers/spree/admin/homepage_section_controller.rb +1 -4
  8. data/app/controllers/spree/admin/taxons_controller_decorator.rb +0 -19
  9. data/app/controllers/spree/api/v2/storefront/homepage_sections_controller.rb +0 -1
  10. data/app/controllers/spree/api/v2/tenant/base_controller.rb +0 -4
  11. data/app/controllers/spree/api/v2/tenant/homepage_sections_controller.rb +0 -1
  12. data/app/controllers/spree/api/v2/tenant/products_controller.rb +1 -1
  13. data/app/controllers/spree/api/v2/tenant/taxons_controller.rb +1 -1
  14. data/app/controllers/spree_cm_commissioner/admin/products_controller_decorator.rb +0 -19
  15. data/app/finders/spree_cm_commissioner/events/find_matches.rb +0 -1
  16. data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +0 -2
  17. data/app/interactors/spree_cm_commissioner/payment_incomplete_notification_sender.rb +25 -0
  18. data/app/interactors/spree_cm_commissioner/release_inventory_item_notification_sender.rb +24 -0
  19. data/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb +23 -6
  20. data/app/jobs/spree_cm_commissioner/payment_incomplete_notification_job.rb +12 -0
  21. data/app/jobs/spree_cm_commissioner/waiting_room/stamp_queue_positions_job.rb +21 -2
  22. data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +1 -2
  23. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +1 -2
  24. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +0 -10
  25. data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
  26. data/app/models/concerns/spree_cm_commissioner/taxon_kind.rb +1 -3
  27. data/app/models/spree_cm_commissioner/import.rb +1 -1
  28. data/app/models/spree_cm_commissioner/notification.rb +1 -0
  29. data/app/models/spree_cm_commissioner/payment_decorator.rb +10 -0
  30. data/app/models/spree_cm_commissioner/product_decorator.rb +0 -52
  31. data/app/models/spree_cm_commissioner/role_decorator.rb +1 -4
  32. data/app/models/spree_cm_commissioner/taxon_decorator.rb +0 -23
  33. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +1 -19
  34. data/app/models/spree_cm_commissioner/tenant.rb +0 -9
  35. data/app/models/spree_cm_commissioner/user_decorator.rb +0 -17
  36. data/app/models/spree_cm_commissioner/variant_decorator.rb +0 -4
  37. data/app/models/spree_cm_commissioner/variant_options.rb +0 -4
  38. data/app/models/spree_cm_commissioner/vendor_decorator.rb +0 -5
  39. data/app/notifications/spree_cm_commissioner/order_general_notification.rb +15 -0
  40. data/app/serializers/spree/v2/storefront/homepage_section_serializer.rb +1 -1
  41. data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +1 -2
  42. data/app/serializers/spree/v2/storefront/role_serializer.rb +1 -1
  43. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +1 -2
  44. data/app/serializers/spree/v2/tenant/homepage_section_serializer.rb +1 -1
  45. data/app/serializers/spree/v2/tenant/role_serializer.rb +1 -1
  46. data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +0 -12
  47. data/app/services/spree_cm_commissioner/inventory_holds/release.rb +16 -0
  48. data/app/services/spree_cm_commissioner/waiting_room_system_metadata_fetcher.rb +9 -7
  49. data/app/services/spree_cm_commissioner/waiting_room_system_metadata_setter.rb +16 -6
  50. data/app/views/spree/admin/homepage_section/_form.html.erb +0 -5
  51. data/config/initializers/spree_permitted_attributes.rb +0 -8
  52. data/config/locales/en.yml +9 -45
  53. data/config/locales/km.yml +7 -32
  54. data/config/routes.rb +0 -29
  55. data/lib/spree_cm_commissioner/version.rb +1 -1
  56. data/lib/spree_cm_commissioner.rb +1 -7
  57. data/spree_cm_commissioner.gemspec +1 -1
  58. metadata +5 -130
  59. data/app/controllers/spree/api/v2/storefront/preview_products_controller.rb +0 -48
  60. data/app/controllers/spree/api/v2/storefront/preview_sections_controller.rb +0 -27
  61. data/app/controllers/spree/api/v2/storefront/preview_taxons_controller.rb +0 -18
  62. data/app/controllers/spree/api/v2/storefront/products_controller_decorator.rb +0 -15
  63. data/app/controllers/spree/api/v2/storefront/taxons_controller_decorator.rb +0 -15
  64. data/app/controllers/spree/api/v2/tenant/ad_campaigns_controller.rb +0 -25
  65. data/app/controllers/spree/api/v2/tenant/episodes_controller.rb +0 -38
  66. data/app/controllers/spree/api/v2/tenant/free_vote_claim_controller.rb +0 -58
  67. data/app/controllers/spree/api/v2/tenant/preview_products_controller.rb +0 -47
  68. data/app/controllers/spree/api/v2/tenant/preview_sections_controller.rb +0 -26
  69. data/app/controllers/spree/api/v2/tenant/preview_shows_controller.rb +0 -19
  70. data/app/controllers/spree/api/v2/tenant/preview_taxons_controller.rb +0 -19
  71. data/app/controllers/spree/api/v2/tenant/show_contestants_controller.rb +0 -52
  72. data/app/controllers/spree/api/v2/tenant/show_elimination_sessions_controller.rb +0 -57
  73. data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +0 -49
  74. data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +0 -36
  75. data/app/controllers/spree/api/v2/tenant/shows_controller.rb +0 -36
  76. data/app/controllers/spree/api/v2/tenant/vote_packages_controller.rb +0 -33
  77. data/app/controllers/spree/api/v2/tenant/votes_controller.rb +0 -94
  78. data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +0 -43
  79. data/app/controllers/spree/api/v2/tenant/voting_credit_transactions_controller.rb +0 -41
  80. data/app/controllers/spree/api/v2/tenant/voting_credits_controller.rb +0 -31
  81. data/app/jobs/spree_cm_commissioner/show_contestants/import_job.rb +0 -9
  82. data/app/jobs/spree_cm_commissioner/vote_fraud_event_job.rb +0 -9
  83. data/app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb +0 -10
  84. data/app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb +0 -10
  85. data/app/models/concerns/spree_cm_commissioner/voting_session_live_stream_concern.rb +0 -41
  86. data/app/models/spree_cm_commissioner/imports/import_contestant.rb +0 -7
  87. data/app/models/spree_cm_commissioner/maintenance_tasks/voting_session.rb +0 -36
  88. data/app/models/spree_cm_commissioner/preview_role.rb +0 -8
  89. data/app/models/spree_cm_commissioner/role_user_decorator.rb +0 -8
  90. data/app/models/spree_cm_commissioner/show.rb +0 -169
  91. data/app/models/spree_cm_commissioner/show_contestant.rb +0 -39
  92. data/app/models/spree_cm_commissioner/show_contestant_image.rb +0 -11
  93. data/app/models/spree_cm_commissioner/show_contestant_video.rb +0 -15
  94. data/app/models/spree_cm_commissioner/show_episode.rb +0 -108
  95. data/app/models/spree_cm_commissioner/show_person.rb +0 -15
  96. data/app/models/spree_cm_commissioner/show_person_assignment.rb +0 -21
  97. data/app/models/spree_cm_commissioner/show_person_image.rb +0 -11
  98. data/app/models/spree_cm_commissioner/vote.rb +0 -16
  99. data/app/models/spree_cm_commissioner/vote_fraud_event.rb +0 -20
  100. data/app/models/spree_cm_commissioner/voting_contestant.rb +0 -100
  101. data/app/models/spree_cm_commissioner/voting_credit.rb +0 -72
  102. data/app/models/spree_cm_commissioner/voting_credit_transaction.rb +0 -55
  103. data/app/models/spree_cm_commissioner/voting_session.rb +0 -216
  104. data/app/models/spree_cm_commissioner/voting_session_stat.rb +0 -8
  105. data/app/overrides/spree/admin/products/_form/preview_checkbox.html.erb.deface +0 -9
  106. data/app/overrides/spree/admin/taxons/_form/preview_checkbox.html.erb.deface +0 -7
  107. data/app/serializers/spree/v2/tenant/ad_campaign_serializer.rb +0 -13
  108. data/app/serializers/spree/v2/tenant/advertisement_serializer.rb +0 -11
  109. data/app/serializers/spree/v2/tenant/free_vote_claim_serializer.rb +0 -9
  110. data/app/serializers/spree/v2/tenant/show_contestant_serializer.rb +0 -21
  111. data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +0 -20
  112. data/app/serializers/spree/v2/tenant/show_parent_serializer.rb +0 -13
  113. data/app/serializers/spree/v2/tenant/show_person_assignment_serializer.rb +0 -16
  114. data/app/serializers/spree/v2/tenant/show_person_serializer.rb +0 -13
  115. data/app/serializers/spree/v2/tenant/show_serializer.rb +0 -23
  116. data/app/serializers/spree/v2/tenant/video_serializer.rb +0 -9
  117. data/app/serializers/spree/v2/tenant/vote_package_serializer.rb +0 -12
  118. data/app/serializers/spree/v2/tenant/vote_serializer.rb +0 -14
  119. data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +0 -23
  120. data/app/serializers/spree/v2/tenant/voting_credit_serializer.rb +0 -10
  121. data/app/serializers/spree/v2/tenant/voting_credit_transaction_serializer.rb +0 -14
  122. data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +0 -21
  123. data/app/services/spree_cm_commissioner/fraud_check.rb +0 -299
  124. data/app/services/spree_cm_commissioner/imports/contestants/create.rb +0 -153
  125. data/app/services/spree_cm_commissioner/show_contestants/normalize_video_highlights.rb +0 -57
  126. data/app/services/spree_cm_commissioner/show_persons/contestant_assigner.rb +0 -51
  127. data/app/services/spree_cm_commissioner/url_embed/youtube_embed.rb +0 -44
  128. data/app/services/spree_cm_commissioner/vote_counters/audit_counters.rb +0 -43
  129. data/app/services/spree_cm_commissioner/vote_counters/base.rb +0 -31
  130. data/app/services/spree_cm_commissioner/vote_counters/increment.rb +0 -44
  131. data/app/services/spree_cm_commissioner/vote_counters/per_contestant_counter.rb +0 -68
  132. data/app/services/spree_cm_commissioner/vote_counters/rebuild_from_db.rb +0 -70
  133. data/app/services/spree_cm_commissioner/vote_counters/snapshot_to_db.rb +0 -113
  134. data/app/services/spree_cm_commissioner/vote_credit_deductor.rb +0 -68
  135. data/app/services/spree_cm_commissioner/vote_package/create.rb +0 -145
  136. data/app/services/spree_cm_commissioner/vote_package/update.rb +0 -91
  137. data/app/services/spree_cm_commissioner/vote_processor.rb +0 -144
  138. data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +0 -335
  139. data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +0 -40
  140. data/app/services/spree_cm_commissioner/voting_contestants/bulk_updater.rb +0 -106
  141. data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +0 -77
  142. data/app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb +0 -116
  143. data/app/services/spree_cm_commissioner/voting_credits/credit_calculator.rb +0 -35
  144. data/app/services/spree_cm_commissioner/voting_credits/de_allocate.rb +0 -87
  145. data/app/services/spree_cm_commissioner/voting_leaderboards/calculate_score.rb +0 -67
  146. data/app/services/spree_cm_commissioner/voting_leaderboards/combined_result.rb +0 -190
  147. data/app/services/spree_cm_commissioner/voting_sessions/finalize.rb +0 -105
  148. data/db/migrate/20260309230148_create_cm_show_people.rb +0 -14
  149. data/db/migrate/20260309230149_create_cm_show_people_assignments.rb +0 -16
  150. data/db/migrate/20260310082711_create_cm_show_contestants.rb +0 -28
  151. data/db/migrate/20260310082720_create_cm_voting_sessions.rb +0 -21
  152. data/db/migrate/20260310082721_create_cm_voting_contestants.rb +0 -23
  153. data/db/migrate/20260310082734_add_voting_fields_to_spree_taxons.rb +0 -9
  154. data/db/migrate/20260310082735_add_type_to_spree_products.rb +0 -6
  155. data/db/migrate/20260310082749_create_cm_voting_credits.rb +0 -27
  156. data/db/migrate/20260326080200_create_cm_voting_credit_transactions.rb +0 -27
  157. data/db/migrate/20260330160000_create_cm_votes.rb +0 -25
  158. data/db/migrate/20260401072500_add_advanced_from_to_cm_voting_contestants.rb +0 -7
  159. data/db/migrate/20260402000001_add_voting_credit_scope_to_spree_taxons.rb +0 -6
  160. data/db/migrate/20260402000002_rename_scopeable_to_votable_in_cm_voting_credits.rb +0 -12
  161. data/db/migrate/20260403070000_add_name_to_cm_voting_sessions.rb +0 -5
  162. data/db/migrate/20260406000001_add_vendor_id_to_voting_tables.rb +0 -6
  163. data/db/migrate/20260406000001_rename_votes_remaining_to_amount_in_cm_voting_credits.rb +0 -11
  164. data/db/migrate/20260408085255_add_show_id_and_vendor_id_to_cm_voting_sessions.rb +0 -9
  165. data/db/migrate/20260420000001_rename_type_to_credit_type_in_cm_voting_credits.rb +0 -25
  166. data/db/migrate/20260422000001_create_cm_vote_fraud_events.rb +0 -23
  167. data/db/migrate/20260423000001_add_preview_to_taxons_products_and_sections.rb +0 -11
  168. data/db/migrate/20260423000002_create_preview_roles.rb +0 -24
  169. data/db/migrate/20260515120000_add_public_metadata_to_cm_voting_sessions.rb +0 -5
  170. data/db/migrate/20260518090920_add_unique_voter_count_to_voting_contestants.rb +0 -5
  171. data/db/migrate/20260518094322_create_cm_voting_session_stats.rb +0 -17
  172. data/db/migrate/20260520000001_add_scoring_model_to_cm_voting_sessions.rb +0 -5
  173. data/db/migrate/20260520000001_optimize_cm_votes_indexes.rb +0 -22
  174. data/db/migrate/20260525042257_add_vote_number_to_cm_voting_contestants.rb +0 -18
  175. data/db/migrate/20260527035430_add_confirmed_rank_to_cm_voting_contestants.rb +0 -5
  176. data/db/migrate/20260527062005_add_eliminated_at_to_cm_show_contestants.rb +0 -5
  177. data/db/migrate/20260603063652_add_parent_to_voting_sessions.rb +0 -9
  178. data/db/migrate/20260603090000_add_session_type_to_cm_voting_sessions.rb +0 -6
  179. data/db/migrate/20260608000000_add_display_to_cm_voting_sessions.rb +0 -5
  180. data/docs/sql/jsonb_query_guide.md +0 -57
  181. data/lib/spree_cm_commissioner/test_helper/factories/show_episode_factory.rb +0 -12
  182. data/lib/spree_cm_commissioner/test_helper/factories/show_factory.rb +0 -120
  183. data/lib/spree_cm_commissioner/test_helper/factories/vote_credit_factory.rb +0 -37
  184. data/lib/spree_cm_commissioner/test_helper/factories/vote_factory.rb +0 -28
  185. data/lib/spree_cm_commissioner/test_helper/factories/voting_credit_transaction_factory.rb +0 -11
  186. 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: 5d158fca1113f118470b6600ed6f324b99914f972e698064d7856fcdb224356c
4
- data.tar.gz: 879b3914b641f94ad83f85614fa663281781f509c85a8bdb4d2629edb38d710d
3
+ metadata.gz: 20f0a180f0ca53b77ce1c80dd3dc41e0d3049321aa02567ae35f23421828020c
4
+ data.tar.gz: 52f68c03ee83f7b0863ebcda54c301be0f13f1584f026a6f46d574933e0f368e
5
5
  SHA512:
6
- metadata.gz: 0ac785c46ebf44942bd86bac29fd17556bc8a6555bee9531f2814057018d46e66c17ba947c74a49d11feecd6edd62fea9b1ca77e589ca63c3a2710ff9b82fdd6
7
- data.tar.gz: 1e557a5f15d19153b8f2f4a917094f45767be18eb57aa491542c907f8017eba1af700e550b837142b77005c7681e75b33c752ac45a5cbd913ab4eb47a91ed473
6
+ metadata.gz: 64e7b779a99ea18dfc44ecd1953f4e7878087754c55fe13fe741304ca4566b027e301638d2954ebdd19d1dc0e014b5119bb2ca7e73e0a36be05570db66c2eae0
7
+ data.tar.gz: bf170f7c4fdba381ff89bd02393df9b5e8baad1d765d5542804beca8fef87f03088ac0a6903fd62e1017eba367d8653d1f44ad89b7396c91ed9fb1bc5f6c2f68
data/.env.example CHANGED
@@ -20,6 +20,8 @@ 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
23
25
 
24
26
  # Vattanac Bank
25
27
  VATTANAC_AES_SECRET_KEY= ""
@@ -63,3 +65,6 @@ PAYMENT_LOCK_MIN_DURATION_IN_MINUTES=5 # Minimum remaining hold time requir
63
65
  MAX_ACTIVE_HOLDS_PER_USER=3 # Max concurrent active holds allowed per user across all orders
64
66
  MAX_HOLDS_PER_IP_PER_HOUR=5 # Max hold attempts per IP address within a rolling 1-hour window (abuse prevention)
65
67
  HOLD_COOLDOWN_AFTER_EXPIRY_IN_MINUTES=2 # Cooldown period before a user can re-acquire a hold after one expired on them
68
+
69
+ # See: app/models/spree_cm_commissioner/payment_decorator.rb
70
+ PAYMENT_INCOMPLETE_NOTIFICATION_DELAY_IN_MINUTES=5 # Delay before notifying a customer that their order is still unpaid after payment is created
@@ -164,8 +164,7 @@ jobs:
164
164
  env:
165
165
  DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
166
166
  run: |
167
- # Using --no-threads to bypass a known Ruby 3.2.0 GC segmentation fault in CI.
168
- bundle exec brakeman --no-threads --no-exit-on-warn
167
+ bundle exec brakeman --no-exit-on-warn
169
168
 
170
169
  test:
171
170
  needs: [setup]
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.4.pre.pre3)
37
+ spree_cm_commissioner (2.8.4.pre.pre4)
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -4,13 +4,20 @@ module SpreeCmCommissioner
4
4
  class WaitingRoomSessionCreator < BaseInteractor
5
5
  delegate :remote_ip, :waiting_guest_firebase_doc_id, :page_path, :tenant_id, to: :context
6
6
 
7
+ SESSION_CREATION_LOCK_KEY = 7_331_001
8
+
7
9
  def call
8
10
  return context.fail!(message: 'must_provide_waiting_guest_firebase_doc_id') if waiting_guest_firebase_doc_id.blank?
9
11
  return context.fail!(message: 'must_provide_remote_ip') if remote_ip.blank?
10
- return context.fail!(message: 'sessions_reach_it_maximum') if full?
11
12
 
12
- generate_jwt_token
13
- assign_token_and_create_session_to_db
13
+ # Advisory lock ensures the capacity check and session creation are atomic across all app instances.
14
+ with_session_creation_lock do
15
+ return context.fail!(message: 'sessions_reach_it_maximum') if full?
16
+
17
+ generate_jwt_token
18
+ assign_token_and_create_session_to_db
19
+ end
20
+
14
21
  log_to_firebase
15
22
 
16
23
  # commented because of following bug: https://github.com/channainfo/commissioner/issues/2185
@@ -19,10 +26,20 @@ module SpreeCmCommissioner
19
26
  end
20
27
 
21
28
  def full?
22
- fetcher = SpreeCmCommissioner::WaitingRoomSystemMetadataFetcher.new(firestore: firestore)
23
- fetcher.load_document_data
29
+ max = Rails.cache.fetch('waiting_room/max_sessions_count', expires_in: 1.hour) do
30
+ fetcher = SpreeCmCommissioner::WaitingRoomSystemMetadataFetcher.new(firestore: firestore)
31
+ fetcher.load_document_data
32
+ fetcher.max_sessions_count_with_min
33
+ end
34
+
35
+ SpreeCmCommissioner::WaitingRoomSession.active.count >= max
36
+ end
24
37
 
25
- SpreeCmCommissioner::WaitingRoomSession.active.size >= fetcher.max_sessions_count_with_min
38
+ def with_session_creation_lock
39
+ SpreeCmCommissioner::WaitingRoomSession.transaction do
40
+ SpreeCmCommissioner::WaitingRoomSession.connection.execute("SELECT pg_advisory_xact_lock(#{SESSION_CREATION_LOCK_KEY})")
41
+ yield
42
+ end
26
43
  end
27
44
 
28
45
  def generate_jwt_token
@@ -0,0 +1,12 @@
1
+ module SpreeCmCommissioner
2
+ class PaymentIncompleteNotificationJob < ApplicationUniqueJob
3
+ queue_as :default
4
+
5
+ def perform(options = {})
6
+ order = Spree::Order.find_by(id: options[:order_id])
7
+ return unless order
8
+
9
+ SpreeCmCommissioner::PaymentIncompleteNotificationSender.call(notificable: order)
10
+ end
11
+ end
12
+ end
@@ -7,6 +7,9 @@ module SpreeCmCommissioner
7
7
 
8
8
  STAMP_LIMIT = (ENV['WAITING_ROOM_POSITION_STAMP_LIMIT'] || 1000).to_i
9
9
 
10
+ # Firestore bounds a batch update by payload size (10 MiB); this batch size leaves 500/commit far under that.
11
+ FIRESTORE_BATCH_SIZE = (ENV['WAITING_ROOM_FIRESTORE_BATCH_SIZE'] || 500).to_i
12
+
10
13
  def perform
11
14
  paths = stamp_paths
12
15
  avg_queue_to_enter = lobby_data[:avg_queue_to_enter_seconds].to_i
@@ -21,16 +24,32 @@ module SpreeCmCommissioner
21
24
  break if remaining <= 0
22
25
 
23
26
  docs = waiting_query(path).order('queued_at').limit(remaining).get.to_a
24
- docs.each_slice(500) do |slice|
27
+
28
+ # Stamp the docs in chunks: each_slice splits them into groups of at most
29
+ # FIRESTORE_BATCH_SIZE, and each group is written in one firestore.batch commit
30
+ # (so e.g. 1000 docs = 2 commits, not 1000 round-trips).
31
+ docs.each_slice(FIRESTORE_BATCH_SIZE) do |slice|
25
32
  firestore.batch do |b|
26
33
  slice.each do |doc|
27
34
  global += 1
35
+
36
+ # The app shows a progress bar via (queue_total - position + 1) / queue_total.
37
+ # `position` only ever counts down toward 1, so we keep `queue_total` from ever
38
+ # going down either (max of its old value and the current position) — it becomes
39
+ # the guest's starting depth. That keeps the bar moving only forward, reaching
40
+ # 100% at position 1, instead of jumping as the live waiting count shrinks.
41
+ #
42
+ # Example — a guest who started 8th, with the line draining from 8 to 4 people:
43
+ # live total: position 6 / total 8 = 38%, then position 4 / total 4 = 25% (drops!) ❌
44
+ # this fix: position 6 / total 8 = 38%, then position 4 / total 8 = 63% (climbs) ✅
45
+ # ...continuing to position 1 / total 8 = 100% at the front.
28
46
  data = {
29
47
  position: global,
30
- queue_total: total,
48
+ queue_total: [doc.data[:queue_total].to_i, global].max,
31
49
  estimated_wait_seconds: avg_queue_to_enter,
32
50
  status_updated_at: stamped_at
33
51
  }
52
+
34
53
  data[:position_set_at] = stamped_at unless doc.data[:position_set_at]
35
54
  b.update(doc.ref, data)
36
55
  end
@@ -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,8 @@ 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
33
31
 
34
32
  scope :accepted, -> { where(request_state: 'accepted') }
35
33
 
@@ -330,13 +328,5 @@ module SpreeCmCommissioner
330
328
  factory = OrderTelegramMessageFactory.new(title: title, order: self)
331
329
  TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory.message, parse_mode: factory.parse_mode)
332
330
  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
331
  end
342
332
  end
@@ -5,7 +5,7 @@ module SpreeCmCommissioner
5
5
  module ProductType
6
6
  extend ActiveSupport::Concern
7
7
 
8
- PRODUCT_TYPES = %i[accommodation service ecommerce transit show_episode vote_package advertisement].freeze
8
+ PRODUCT_TYPES = %i[accommodation service ecommerce transit].freeze
9
9
  PERMANENT_STOCK_PRODUCT_TYPES = %w[accommodation service transit].freeze
10
10
  PRE_INVENTORY_DAYS = { 'transit' => 90, 'accommodation' => 365, 'service' => 30 }.freeze
11
11
 
@@ -3,9 +3,7 @@ module SpreeCmCommissioner
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- enum kind: { category: 0, cms: 1, event: 2, occupation: 3, nationality: 4, organization: 5, transit: 6, industry: 7, agency_category: 8,
7
- ads: 9
8
- }
6
+ enum kind: { category: 0, cms: 1, event: 2, occupation: 3, nationality: 4, organization: 5, transit: 6, industry: 7, agency_category: 8 }
9
7
  end
10
8
  end
11
9
  end
@@ -3,7 +3,7 @@ module SpreeCmCommissioner
3
3
  extend FriendlyId
4
4
 
5
5
  enum :status, { :queue => 0, :progress => 1, :done => 2, :failed => 3 }
6
- enum :import_type, { :new_order => 0, :existing_order => 1, :aba_payment_reference => 2, :contestant_import => 3 }
6
+ enum :import_type, { :new_order => 0, :existing_order => 1, :aba_payment_reference => 2 }
7
7
  has_one_attached :imported_file
8
8
  friendly_id :name, use: :slugged
9
9
 
@@ -12,6 +12,7 @@ module SpreeCmCommissioner
12
12
  where(
13
13
  type: %w[
14
14
  order_complete_notification
15
+ order_general_notification
15
16
  customer_notification
16
17
  guest_dynamic_field_notification
17
18
  ticket_transfer_received_notification
@@ -12,6 +12,7 @@ module SpreeCmCommissioner
12
12
  # a real payment record permanently exists in the DB. Multiple payments for the
13
13
  # same order (retries) each refresh the 5-minute window — LockForPayment is idempotent.
14
14
  base.after_commit :lock_holds_for_payment, on: :create
15
+ base.after_commit :schedule_payment_incomplete_notification, on: :create
15
16
 
16
17
  base.whitelisted_ransackable_attributes |= %w[payable_id]
17
18
  end
@@ -56,6 +57,15 @@ module SpreeCmCommissioner
56
57
  order.lock_order_holds_for_payment
57
58
  end
58
59
 
60
+ def schedule_payment_incomplete_notification
61
+ # Delay gives the user time to finish paying; the job only notifies if the
62
+ # order is still unpaid by then.
63
+ delay = ENV.fetch('PAYMENT_INCOMPLETE_NOTIFICATION_DELAY_IN_MINUTES', '5').to_i.minutes
64
+ SpreeCmCommissioner::PaymentIncompleteNotificationJob
65
+ .set(wait: delay)
66
+ .perform_later(order_id: order.id)
67
+ end
68
+
59
69
  def ticket_transfer_completable?
60
70
  completed? && order.ticket_transfer? && order.complete? && order.payment_fulfilled?
61
71
  end
@@ -12,10 +12,6 @@ module SpreeCmCommissioner
12
12
  base.include SpreeCmCommissioner::StoreMetadata
13
13
  base.include SpreeCmCommissioner::HomepageSectionRelatableConcern
14
14
 
15
- base.delegate :is_open_dated, :is_open_dated?, to: :trip, allow_nil: true
16
-
17
- base.has_many :voting_sessions, class_name: 'SpreeCmCommissioner::VotingSession', foreign_key: :episode_id, dependent: :destroy
18
-
19
15
  base.has_many :variant_kind_option_types, -> { where(kind: :variant).order(:position) },
20
16
  through: :product_option_types, source: :option_type
21
17
 
@@ -63,45 +59,8 @@ module SpreeCmCommissioner
63
59
 
64
60
  base.belongs_to :event, class_name: 'Spree::Taxon', optional: true
65
61
 
66
- base.has_many :preview_roles, class_name: 'SpreeCmCommissioner::PreviewRole', as: :previewable
67
62
  base.has_many :industry_classifications, -> { joins(:taxon).where(spree_taxons: { kind: :industry }) }, class_name: 'Spree::Classification'
68
63
  base.has_many :industry_taxons, through: :industry_classifications, source: :taxon
69
- base.scope :visible_to, lambda { |user|
70
- publicly_available = where(status: :active, preview: false)
71
-
72
- if user
73
- # Resolved through associations — avoids hardcoding class names.
74
- taxon_type = reflect_on_association(:taxons).klass.polymorphic_name
75
-
76
- # IDs of products that have been explicitly assigned their own PreviewRole.
77
- products_with_own_roles = SpreeCmCommissioner::PreviewRole
78
- .where(previewable_type: polymorphic_name)
79
- .select(:previewable_id)
80
-
81
- # Path A: product has its own PreviewRole → user must hold that role directly.
82
- # Taxon membership is irrelevant for these products.
83
- via_product_role = where(status: :active, preview: true)
84
- .where(id: products_with_own_roles)
85
- .where(id: user.preview_roles.where(previewable_type: polymorphic_name).select(:previewable_id))
86
-
87
- # Path B: product has no own PreviewRole → inherit access from its event or taxons.
88
- # Accessible if the user holds a preview role for the product's event_id or any
89
- # taxon the product belongs to via the classifications join table.
90
- allowed_taxon_ids = user.preview_roles.where(previewable_type: taxon_type).select(:previewable_id)
91
- preview_no_own = where(status: :active, preview: true).where.not(id: products_with_own_roles)
92
-
93
- via_event_id = preview_no_own.where(event_id: allowed_taxon_ids)
94
- via_taxon_join = preview_no_own.joins(:taxons).where(spree_taxons: { id: allowed_taxon_ids })
95
-
96
- publicly_available
97
- .or(via_product_role)
98
- .or(where(id: via_event_id.select(:id)))
99
- .or(where(id: via_taxon_join.select(:id)))
100
- else
101
- publicly_available
102
- end
103
- }
104
-
105
64
  base.scope :min_price, lambda { |vendor|
106
65
  joins(:prices_including_master)
107
66
  .where(vendor_id: vendor.id, product_type: vendor.primary_product_type)
@@ -114,12 +73,6 @@ module SpreeCmCommissioner
114
73
  .maximum('spree_prices.price').to_f
115
74
  }
116
75
  base.scope :subscribable, -> { where(subscribable: 1) }
117
- base.scope :advertisements, lambda {
118
- advertisement
119
- .available
120
- .where('spree_products.available_on IS NULL OR spree_products.available_on <= CURRENT_TIMESTAMP')
121
- .where('spree_products.discontinue_on IS NULL OR spree_products.discontinue_on > CURRENT_TIMESTAMP')
122
- }
123
76
 
124
77
  base.before_validation :set_event_id
125
78
 
@@ -133,11 +86,6 @@ module SpreeCmCommissioner
133
86
  base.store_public_metadata :open_dated_validity_days, :integer, default: 90
134
87
  base.store_public_metadata :enable_inventory_hold, :boolean, default: false
135
88
 
136
- base.store_private_metadata :advertise_weight, :integer, default: 1
137
- base.store_private_metadata :video_url, :string
138
- base.store_private_metadata :skip_after_seconds, :integer, default: 5
139
- base.store_private_metadata :duration_seconds, :integer, default: 30
140
-
141
89
  base.after_update :update_variants_vendor_id, if: :saved_change_to_vendor_id?
142
90
  base.after_update :sync_event_id_to_children, if: :saved_change_to_event_id?
143
91
 
@@ -1,11 +1,10 @@
1
1
  module SpreeCmCommissioner
2
2
  module RoleDecorator
3
3
  def self.prepended(base)
4
- base.enum role_type: { internal: 0, external: 1, preview: 2 }
4
+ base.enum role_type: { internal: 0, external: 1 }
5
5
 
6
6
  base.has_many :role_permissions, class_name: 'SpreeCmCommissioner::RolePermission'
7
7
  base.has_many :permissions, through: :role_permissions, class_name: 'SpreeCmCommissioner::Permission'
8
- base.has_many :preview_roles, class_name: 'SpreeCmCommissioner::PreviewRole'
9
8
 
10
9
  base.belongs_to :vendor, optional: true
11
10
 
@@ -14,8 +13,6 @@ module SpreeCmCommissioner
14
13
  where(vendor_id: vendor)
15
14
  }
16
15
  base.scope :filter_external, -> { where(role_type: :external) }
17
- base.scope :filter_internal, -> { where(role_type: :internal) }
18
- base.scope :filter_preview, -> { where(role_type: :preview) }
19
16
 
20
17
  base.accepts_nested_attributes_for :role_permissions, allow_destroy: true
21
18
 
@@ -84,29 +84,7 @@ module SpreeCmCommissioner
84
84
 
85
85
  base.has_many :import_orders, as: :importable, class_name: 'SpreeCmCommissioner::Imports::ImportOrder', dependent: :destroy
86
86
 
87
- base.scope :shows, -> { where(taxonomy_id: Spree::Taxonomy.shows.id, depth: 1) }
88
- base.scope :events, -> { where(taxonomy_id: Spree::Taxonomy.events.id, depth: 1) }
89
- base.scope :ad_campaigns, -> { where(taxonomy_id: Spree::Taxonomy.ads.id, depth: 1) }
90
-
91
- base.has_many :advertisements,
92
- -> { where(product_type: :advertisement, deleted_at: nil) },
93
- through: :classifications,
94
- class_name: 'Spree::Product',
95
- source: :product
96
-
97
87
  base.has_many :agencies, class_name: 'SpreeCmCommissioner::Agency', foreign_key: :agency_category_id
98
- base.has_many :preview_roles, class_name: 'SpreeCmCommissioner::PreviewRole', as: :previewable
99
-
100
- base.scope :visible_to, lambda { |user|
101
- publicly_available = where(preview: false)
102
-
103
- if user
104
- allowed_ids = user.preview_roles.where(previewable_type: polymorphic_name).select(:previewable_id)
105
- publicly_available.or(where(preview: true, id: allowed_ids))
106
- else
107
- publicly_available
108
- end
109
- }
110
88
 
111
89
  # Create maintaining task to purge taxon related caches
112
90
  base.after_save { SpreeCmCommissioner::MaintenanceTasks::CacheInvalidation.pending.create_or_find_by(maintainable: self) }
@@ -119,7 +97,6 @@ module SpreeCmCommissioner
119
97
  .joins('INNER JOIN cm_homepage_sections ON cm_homepage_section_relatables.homepage_section_id = cm_homepage_sections.id')
120
98
  .where(cm_homepage_sections: { tenant_id: nil, active: true })
121
99
  .where(kind: :event)
122
- .where(preview: false)
123
100
  end
124
101
 
125
102
  def base.find_event(id)