spree_cm_commissioner 2.8.2 → 2.8.3.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 (193) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +0 -1
  3. data/.gitignore +4 -0
  4. data/Gemfile.lock +1 -1
  5. data/app/controllers/concerns/spree_cm_commissioner/content_cachable.rb +2 -0
  6. data/app/controllers/spree/admin/homepage_section_controller.rb +4 -1
  7. data/app/controllers/spree/admin/taxons_controller_decorator.rb +19 -0
  8. data/app/controllers/spree/api/v2/storefront/homepage_sections_controller.rb +1 -0
  9. data/app/controllers/spree/api/v2/storefront/preview_products_controller.rb +48 -0
  10. data/app/controllers/spree/api/v2/storefront/preview_sections_controller.rb +27 -0
  11. data/app/controllers/spree/api/v2/storefront/preview_taxons_controller.rb +18 -0
  12. data/app/controllers/spree/api/v2/storefront/products_controller_decorator.rb +15 -0
  13. data/app/controllers/spree/api/v2/storefront/taxons_controller_decorator.rb +15 -0
  14. data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +1 -0
  15. data/app/controllers/spree/api/v2/tenant/advertisements_controller.rb +24 -0
  16. data/app/controllers/spree/api/v2/tenant/base_controller.rb +4 -0
  17. data/app/controllers/spree/api/v2/tenant/free_vote_claims_controller.rb +37 -0
  18. data/app/controllers/spree/api/v2/tenant/homepage_sections_controller.rb +1 -0
  19. data/app/controllers/spree/api/v2/tenant/preview_products_controller.rb +47 -0
  20. data/app/controllers/spree/api/v2/tenant/preview_sections_controller.rb +26 -0
  21. data/app/controllers/spree/api/v2/tenant/preview_shows_controller.rb +19 -0
  22. data/app/controllers/spree/api/v2/tenant/preview_taxons_controller.rb +19 -0
  23. data/app/controllers/spree/api/v2/tenant/products_controller.rb +1 -1
  24. data/app/controllers/spree/api/v2/tenant/show_contestants_controller.rb +52 -0
  25. data/app/controllers/spree/api/v2/tenant/show_elimination_sessions_controller.rb +57 -0
  26. data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +49 -0
  27. data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +36 -0
  28. data/app/controllers/spree/api/v2/tenant/shows_controller.rb +34 -0
  29. data/app/controllers/spree/api/v2/tenant/taxons_controller.rb +1 -1
  30. data/app/controllers/spree/api/v2/tenant/votes_controller.rb +94 -0
  31. data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +40 -0
  32. data/app/controllers/spree/api/v2/tenant/voting_credit_transactions_controller.rb +41 -0
  33. data/app/controllers/spree/api/v2/tenant/voting_credits_controller.rb +31 -0
  34. data/app/controllers/spree_cm_commissioner/admin/products_controller_decorator.rb +19 -0
  35. data/app/finders/spree_cm_commissioner/events/find_matches.rb +1 -0
  36. data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +2 -0
  37. data/app/jobs/spree_cm_commissioner/cleanup_expired_access_tokens_job.rb +9 -0
  38. data/app/jobs/spree_cm_commissioner/show_contestants/import_job.rb +9 -0
  39. data/app/jobs/spree_cm_commissioner/vote_fraud_event_job.rb +9 -0
  40. data/app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb +10 -0
  41. data/app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb +10 -0
  42. data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +2 -1
  43. data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +0 -8
  44. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +2 -1
  45. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +10 -0
  46. data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
  47. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +0 -1
  48. data/app/models/concerns/spree_cm_commissioner/taxon_kind.rb +3 -1
  49. data/app/models/concerns/spree_cm_commissioner/voting_session_live_stream_concern.rb +41 -0
  50. data/app/models/spree_cm_commissioner/import.rb +1 -1
  51. data/app/models/spree_cm_commissioner/imports/import_contestant.rb +7 -0
  52. data/app/models/spree_cm_commissioner/inventory_item.rb +2 -17
  53. data/app/models/spree_cm_commissioner/maintenance_tasks/cache_invalidation.rb +0 -2
  54. data/app/models/spree_cm_commissioner/maintenance_tasks/voting_session.rb +36 -0
  55. data/app/models/spree_cm_commissioner/preview_role.rb +8 -0
  56. data/app/models/spree_cm_commissioner/product_decorator.rb +50 -0
  57. data/app/models/spree_cm_commissioner/role_decorator.rb +4 -1
  58. data/app/models/spree_cm_commissioner/role_user_decorator.rb +8 -0
  59. data/app/models/spree_cm_commissioner/route.rb +1 -6
  60. data/app/models/spree_cm_commissioner/show.rb +163 -0
  61. data/app/models/spree_cm_commissioner/show_contestant.rb +39 -0
  62. data/app/models/spree_cm_commissioner/show_contestant_image.rb +11 -0
  63. data/app/models/spree_cm_commissioner/show_contestant_video.rb +15 -0
  64. data/app/models/spree_cm_commissioner/show_episode.rb +110 -0
  65. data/app/models/spree_cm_commissioner/show_person.rb +15 -0
  66. data/app/models/spree_cm_commissioner/show_person_assignment.rb +20 -0
  67. data/app/models/spree_cm_commissioner/show_person_image.rb +11 -0
  68. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +3 -0
  69. data/app/models/spree_cm_commissioner/taxon_decorator.rb +18 -7
  70. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +19 -1
  71. data/app/models/spree_cm_commissioner/tenant.rb +9 -0
  72. data/app/models/spree_cm_commissioner/trip.rb +0 -5
  73. data/app/models/spree_cm_commissioner/user_decorator.rb +5 -0
  74. data/app/models/spree_cm_commissioner/variant_options.rb +4 -0
  75. data/app/models/spree_cm_commissioner/vendor_decorator.rb +4 -0
  76. data/app/models/spree_cm_commissioner/vote.rb +16 -0
  77. data/app/models/spree_cm_commissioner/vote_fraud_event.rb +20 -0
  78. data/app/models/spree_cm_commissioner/voting_contestant.rb +61 -0
  79. data/app/models/spree_cm_commissioner/voting_credit.rb +72 -0
  80. data/app/models/spree_cm_commissioner/voting_credit_transaction.rb +55 -0
  81. data/app/models/spree_cm_commissioner/voting_session.rb +215 -0
  82. data/app/models/spree_cm_commissioner/voting_session_stat.rb +8 -0
  83. data/app/overrides/spree/admin/products/_form/preview_checkbox.html.erb.deface +9 -0
  84. data/app/overrides/spree/admin/stores/_form/store_preferences.html.erb.deface +0 -9
  85. data/app/overrides/spree/admin/taxons/_form/preview_checkbox.html.erb.deface +7 -0
  86. data/app/queries/spree_cm_commissioner/multi_leg_trips_query.rb +44 -26
  87. data/app/queries/spree_cm_commissioner/single_leg_trips_query.rb +31 -36
  88. data/app/queries/spree_cm_commissioner/trip_query.rb +4 -2
  89. data/app/serializers/spree/v2/storefront/homepage_section_serializer.rb +1 -1
  90. data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +1 -1
  91. data/app/serializers/spree/v2/storefront/role_serializer.rb +1 -1
  92. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +2 -1
  93. data/app/serializers/spree/v2/tenant/advertisement_serializer.rb +11 -0
  94. data/app/serializers/spree/v2/tenant/campaign_serializer.rb +13 -0
  95. data/app/serializers/spree/v2/tenant/homepage_section_serializer.rb +1 -1
  96. data/app/serializers/spree/v2/tenant/role_serializer.rb +1 -1
  97. data/app/serializers/spree/v2/tenant/show_contestant_serializer.rb +21 -0
  98. data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +17 -0
  99. data/app/serializers/spree/v2/tenant/show_person_assignment_serializer.rb +16 -0
  100. data/app/serializers/spree/v2/tenant/show_person_serializer.rb +13 -0
  101. data/app/serializers/spree/v2/tenant/show_serializer.rb +19 -0
  102. data/app/serializers/spree/v2/tenant/video_serializer.rb +9 -0
  103. data/app/serializers/spree/v2/tenant/vote_serializer.rb +14 -0
  104. data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +22 -0
  105. data/app/serializers/spree/v2/tenant/voting_credit_serializer.rb +10 -0
  106. data/app/serializers/spree/v2/tenant/voting_credit_transaction_serializer.rb +14 -0
  107. data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +19 -0
  108. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +2 -7
  109. data/app/services/spree_cm_commissioner/advertisements/weight_validator.rb +43 -0
  110. data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +12 -0
  111. data/app/services/spree_cm_commissioner/cleanup_expired_access_tokens.rb +52 -0
  112. data/app/services/spree_cm_commissioner/fraud_check.rb +299 -0
  113. data/app/services/spree_cm_commissioner/imports/contestants/create.rb +153 -0
  114. data/app/services/spree_cm_commissioner/pricing_models/preview.rb +6 -15
  115. data/app/services/spree_cm_commissioner/routes/create.rb +5 -0
  116. data/app/services/spree_cm_commissioner/show_contestants/normalize_video_highlights.rb +57 -0
  117. data/app/services/spree_cm_commissioner/trips/create_multi_leg.rb +16 -5
  118. data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +1 -0
  119. data/app/services/spree_cm_commissioner/url_embed/youtube_embed.rb +44 -0
  120. data/app/services/spree_cm_commissioner/vote_counters/audit_counters.rb +43 -0
  121. data/app/services/spree_cm_commissioner/vote_counters/base.rb +31 -0
  122. data/app/services/spree_cm_commissioner/vote_counters/increment.rb +44 -0
  123. data/app/services/spree_cm_commissioner/vote_counters/per_contestant_counter.rb +68 -0
  124. data/app/services/spree_cm_commissioner/vote_counters/rebuild_from_db.rb +70 -0
  125. data/app/services/spree_cm_commissioner/vote_counters/snapshot_to_db.rb +113 -0
  126. data/app/services/spree_cm_commissioner/vote_credit_deductor.rb +68 -0
  127. data/app/services/spree_cm_commissioner/vote_package/create.rb +145 -0
  128. data/app/services/spree_cm_commissioner/vote_package/update.rb +91 -0
  129. data/app/services/spree_cm_commissioner/vote_processor.rb +144 -0
  130. data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +334 -0
  131. data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +32 -0
  132. data/app/services/spree_cm_commissioner/voting_contestants/bulk_updater.rb +106 -0
  133. data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +77 -0
  134. data/app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb +119 -0
  135. data/app/services/spree_cm_commissioner/voting_credits/credit_calculator.rb +35 -0
  136. data/app/services/spree_cm_commissioner/voting_credits/de_allocate.rb +87 -0
  137. data/app/services/spree_cm_commissioner/voting_leaderboards/calculate_score.rb +67 -0
  138. data/app/services/spree_cm_commissioner/voting_leaderboards/combined_result.rb +183 -0
  139. data/app/services/spree_cm_commissioner/voting_sessions/finalize.rb +66 -0
  140. data/app/views/spree/admin/homepage_section/_form.html.erb +5 -0
  141. data/config/initializers/spree_permitted_attributes.rb +8 -1
  142. data/config/locales/en.yml +36 -0
  143. data/config/locales/km.yml +18 -0
  144. data/config/routes.rb +27 -0
  145. data/db/migrate/20260309230148_create_cm_show_people.rb +14 -0
  146. data/db/migrate/20260309230149_create_cm_show_people_assignments.rb +16 -0
  147. data/db/migrate/20260310082711_create_cm_show_contestants.rb +28 -0
  148. data/db/migrate/20260310082720_create_cm_voting_sessions.rb +21 -0
  149. data/db/migrate/20260310082721_create_cm_voting_contestants.rb +23 -0
  150. data/db/migrate/20260310082734_add_voting_fields_to_spree_taxons.rb +9 -0
  151. data/db/migrate/20260310082735_add_type_to_spree_products.rb +6 -0
  152. data/db/migrate/20260310082749_create_cm_voting_credits.rb +27 -0
  153. data/db/migrate/20260326080200_create_cm_voting_credit_transactions.rb +27 -0
  154. data/db/migrate/20260330160000_create_cm_votes.rb +25 -0
  155. data/db/migrate/20260401072500_add_advanced_from_to_cm_voting_contestants.rb +7 -0
  156. data/db/migrate/20260402000001_add_voting_credit_scope_to_spree_taxons.rb +6 -0
  157. data/db/migrate/20260402000002_rename_scopeable_to_votable_in_cm_voting_credits.rb +12 -0
  158. data/db/migrate/20260403070000_add_name_to_cm_voting_sessions.rb +5 -0
  159. data/db/migrate/20260406000001_add_vendor_id_to_voting_tables.rb +6 -0
  160. data/db/migrate/20260406000001_rename_votes_remaining_to_amount_in_cm_voting_credits.rb +11 -0
  161. data/db/migrate/20260408085255_add_show_id_and_vendor_id_to_cm_voting_sessions.rb +9 -0
  162. data/db/migrate/20260420000001_rename_type_to_credit_type_in_cm_voting_credits.rb +25 -0
  163. data/db/migrate/20260422000001_create_cm_vote_fraud_events.rb +23 -0
  164. data/db/migrate/20260423000001_add_preview_to_taxons_products_and_sections.rb +11 -0
  165. data/db/migrate/20260423000002_create_preview_roles.rb +24 -0
  166. data/db/migrate/20260515120000_add_public_metadata_to_cm_voting_sessions.rb +5 -0
  167. data/db/migrate/20260518090920_add_unique_voter_count_to_voting_contestants.rb +5 -0
  168. data/db/migrate/20260518094322_create_cm_voting_session_stats.rb +17 -0
  169. data/db/migrate/20260520000001_add_scoring_model_to_cm_voting_sessions.rb +5 -0
  170. data/db/migrate/20260520000001_optimize_cm_votes_indexes.rb +22 -0
  171. data/db/migrate/20260525042257_add_vote_number_to_cm_voting_contestants.rb +18 -0
  172. data/db/migrate/20260527035430_add_confirmed_rank_to_cm_voting_contestants.rb +5 -0
  173. data/db/migrate/20260527062005_add_eliminated_at_to_cm_show_contestants.rb +5 -0
  174. data/db/migrate/20260603063652_add_parent_to_voting_sessions.rb +9 -0
  175. data/db/migrate/20260603090000_add_session_type_to_cm_voting_sessions.rb +6 -0
  176. data/docs/sql/jsonb_query_guide.md +57 -0
  177. data/lib/spree_cm_commissioner/test_helper/factories/show_episode_factory.rb +12 -0
  178. data/lib/spree_cm_commissioner/test_helper/factories/show_factory.rb +120 -0
  179. data/lib/spree_cm_commissioner/test_helper/factories/vote_credit_factory.rb +37 -0
  180. data/lib/spree_cm_commissioner/test_helper/factories/vote_factory.rb +28 -0
  181. data/lib/spree_cm_commissioner/test_helper/factories/voting_credit_transaction_factory.rb +11 -0
  182. data/lib/spree_cm_commissioner/test_helper/factories/voting_session_factory.rb +11 -0
  183. data/lib/spree_cm_commissioner/trip_result.rb +21 -48
  184. data/lib/spree_cm_commissioner/version.rb +1 -1
  185. data/lib/spree_cm_commissioner.rb +7 -1
  186. data/spree_cm_commissioner.gemspec +1 -1
  187. metadata +129 -10
  188. data/app/factory/spree_cm_commissioner/event_telegram_message_factory.rb +0 -68
  189. data/app/jobs/spree_cm_commissioner/event_creation_telegram_alert_sender_job.rb +0 -10
  190. data/app/jobs/spree_cm_commissioner/oauth_access_tokens/cleanup_expired_job.rb +0 -11
  191. data/app/models/concerns/spree_cm_commissioner/service_origin.rb +0 -22
  192. data/app/services/spree_cm_commissioner/oauth_access_tokens/cleanup_expired.rb +0 -53
  193. data/app/services/spree_cm_commissioner/telegram_alerts/event_creation.rb +0 -35
@@ -0,0 +1,34 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class ShowsController < BaseController
6
+ private
7
+
8
+ # override
9
+ def model_class
10
+ SpreeCmCommissioner::Show
11
+ end
12
+
13
+ def resource
14
+ @resource ||= scope.find_by!(slug: params[:id])
15
+ end
16
+
17
+ # override
18
+ def scope
19
+ vendor_ids = @tenant.vendors.select(:id)
20
+ return model_class.none if vendor_ids.blank?
21
+
22
+ model_class.where(vendor_id: vendor_ids, preview: false)
23
+ .includes(:parent, :app_banner, episodes: :voting_sessions, seasons: { episodes: :voting_sessions })
24
+ end
25
+
26
+ # override
27
+ def resource_serializer
28
+ Spree::V2::Tenant::ShowSerializer
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -31,7 +31,7 @@ module Spree
31
31
 
32
32
  def scope
33
33
  MultiTenant.with(@tenant) do
34
- model_class
34
+ model_class.where(preview: false)
35
35
  end
36
36
  end
37
37
 
@@ -0,0 +1,94 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class VotesController < BaseController
6
+ before_action :require_spree_current_user
7
+
8
+ def index
9
+ render_serialized_payload do
10
+ serialize_collection(collection)
11
+ end
12
+ end
13
+
14
+ def create
15
+ voting_session = current_vendor.voting_sessions.find(params[:voting_session_id])
16
+ contestant = voting_session.voting_contestants.find(params[:contestant_id])
17
+
18
+ processor_params = vote_params.merge(
19
+ device_fingerprint: device_params[:fingerprint],
20
+ voter_identifier: device_params[:identifier],
21
+ public_metadata: params[:public_metadata] || {},
22
+ tenant_id: MultiTenant.current_tenant_id,
23
+ vendor_id: current_vendor.id
24
+ )
25
+
26
+ result = SpreeCmCommissioner::VoteProcessor.call(
27
+ voting_session: voting_session,
28
+ contestant: contestant,
29
+ user: spree_current_user,
30
+ params: processor_params,
31
+ request: request
32
+ )
33
+
34
+ if result.success?
35
+ render_serialized_payload(201) { serialize_resource(result.value) }
36
+ else
37
+ render_error_payload(result.error)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def collection
44
+ @collection ||= scope.page(params[:page])
45
+ .per(params[:per_page])
46
+ end
47
+
48
+ def scope
49
+ MultiTenant.with(@tenant) do
50
+ scope = spree_current_user.votes
51
+ .includes(:contestant, :voting_session, :vote_credit)
52
+ .order(created_at: :desc)
53
+
54
+ scope = scope.for_session(params[:voting_session_id]) if params[:voting_session_id]
55
+ scope
56
+ end
57
+ end
58
+
59
+ def model_class
60
+ SpreeCmCommissioner::Vote
61
+ end
62
+
63
+ def resource_serializer
64
+ Spree::V2::Tenant::VoteSerializer
65
+ end
66
+
67
+ def collection_serializer
68
+ Spree::V2::Tenant::VoteSerializer
69
+ end
70
+
71
+ # {
72
+ # "contestant_id": 1,
73
+ # "vote": {
74
+ # "quantity": 1,
75
+ # "channel": "mobile"
76
+ # },
77
+ # "device": {
78
+ # "fingerprint": "fp-xyz",
79
+ # "identifier": "device-abc",
80
+ # "platform": "android"
81
+ # }
82
+ # }
83
+ def vote_params
84
+ params.require(:vote).permit(:quantity, :channel)
85
+ end
86
+
87
+ def device_params
88
+ params.require(:device).permit(:fingerprint, :identifier, :platform)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,40 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class VotingContestantsController < BaseController
6
+ before_action :load_voting_session, only: %i[index]
7
+
8
+ # override
9
+ def collection
10
+ @collection ||= @voting_session
11
+ .voting_contestants
12
+ .includes(show_contestant: :show_contestant_images)
13
+ .order(:id)
14
+ end
15
+
16
+ # override
17
+ def collection_serializer
18
+ Spree::V2::Tenant::VotingContestantSerializer
19
+ end
20
+
21
+ private
22
+
23
+ # override
24
+ def model_class
25
+ SpreeCmCommissioner::VotingSession
26
+ end
27
+
28
+ # override
29
+ def scope
30
+ current_vendor.voting_sessions.includes(:episode)
31
+ end
32
+
33
+ def load_voting_session
34
+ @voting_session = scope.find(params[:voting_session_id])
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class VotingCreditTransactionsController < BaseController
6
+ before_action :require_spree_current_user
7
+
8
+ def index
9
+ render_serialized_payload do
10
+ serialize_collection(collection)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def collection
17
+ @collection ||= scope.page(params[:page])
18
+ .per(params[:per_page])
19
+ end
20
+
21
+ def scope
22
+ scope = spree_current_user.voting_credit_transactions
23
+ .includes(:vote_credit, :originator)
24
+ .order(created_at: :desc)
25
+
26
+ scope = scope.by_action(params[:actions].split(',')) if params[:actions].present?
27
+ scope
28
+ end
29
+
30
+ def model_class
31
+ SpreeCmCommissioner::VotingCreditTransaction
32
+ end
33
+
34
+ def collection_serializer
35
+ Spree::V2::Tenant::VotingCreditTransactionSerializer
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class VotingCreditsController < BaseController
6
+ before_action :require_spree_current_user
7
+
8
+ def index
9
+ render_serialized_payload do
10
+ serialize_resource(voting_credit)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def voting_credit
17
+ spree_current_user.voting_credits.active.eligible(load_voting_session).first
18
+ end
19
+
20
+ def load_voting_session
21
+ @voting_session ||= SpreeCmCommissioner::VotingSession.find(params[:voting_session_id])
22
+ end
23
+
24
+ def resource_serializer
25
+ Spree::V2::Tenant::VotingCreditSerializer
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -6,6 +6,7 @@ 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]
9
10
  end
10
11
 
11
12
  # Override
@@ -51,6 +52,24 @@ module SpreeCmCommissioner
51
52
 
52
53
  protected
53
54
 
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
+
54
73
  def merge_industry_taxons_into_taxons
55
74
  return if params[:product].blank?
56
75
 
@@ -7,6 +7,7 @@ module SpreeCmCommissioner
7
7
  def execute
8
8
  scope.joins(:integration_mappings).where(
9
9
  kind: :event,
10
+ preview: false,
10
11
  integration_mappings: { status: :active }
11
12
  ).distinct
12
13
  end
@@ -15,6 +15,8 @@ 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'
18
20
  else
19
21
  'badge'
20
22
  end
@@ -0,0 +1,9 @@
1
+ module SpreeCmCommissioner
2
+ class CleanupExpiredAccessTokensJob < SpreeCmCommissioner::ApplicationJob
3
+ queue_as :default
4
+
5
+ def perform
6
+ SpreeCmCommissioner::CleanupExpiredAccessTokens.new.call
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module SpreeCmCommissioner
2
+ module ShowContestants
3
+ class ImportJob < ApplicationJob
4
+ def perform(import_id)
5
+ SpreeCmCommissioner::Imports::Contestants::Create.new(import_id: import_id).call
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module SpreeCmCommissioner
2
+ class VoteFraudEventJob < ApplicationJob
3
+ queue_as :default
4
+
5
+ def perform(options = {})
6
+ SpreeCmCommissioner::VoteFraudEvent.create!(options)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module SpreeCmCommissioner
2
+ class VotingCreditAllocationJob < ApplicationUniqueJob
3
+ def perform(options = {})
4
+ order = Spree::Order.find_by(id: options[:order_id])
5
+ return if order.nil?
6
+
7
+ SpreeCmCommissioner::VotingCredits::Allocate.new(order).call
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module SpreeCmCommissioner
2
+ class VotingCreditDeAllocationJob < ApplicationUniqueJob
3
+ def perform(options = {})
4
+ order = Spree::Order.find_by(id: options[:order_id])
5
+ return if order.nil?
6
+
7
+ SpreeCmCommissioner::VotingCredits::DeAllocate.new(order).call
8
+ end
9
+ end
10
+ end
@@ -8,7 +8,8 @@ module SpreeCmCommissioner
8
8
  tour: 0b00100,
9
9
  accommodation: 0b01000,
10
10
  things_to_do: 0b10000,
11
- football: 0b100000
11
+ football: 0b100000,
12
+ show: 0b1000000
12
13
  }.freeze
13
14
 
14
15
  BIT_SEGMENT.each do |segment, bit_value|
@@ -23,8 +23,6 @@ module SpreeCmCommissioner
23
23
  TripKey = Struct.new(:trip_id, :direction, :boarding_trip_stop_id, :drop_off_trip_stop_id)
24
24
 
25
25
  included do
26
- include SpreeCmCommissioner::ServiceOrigin
27
-
28
26
  validates :direction, inclusion: { in: DIRECTION }, allow_nil: true
29
27
  validates :trip_id, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
30
28
  validates :service_origin_id, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
@@ -127,12 +125,6 @@ module SpreeCmCommissioner
127
125
 
128
126
  private
129
127
 
130
- def resolve_service_origin_id
131
- return nil if trip_id.blank?
132
-
133
- SpreeCmCommissioner::Trip.find_by(id: trip_id)&.service_origin_id
134
- end
135
-
136
128
  def validate_trip_stops_for_scheduled_trips
137
129
  return if trip_id.blank? || trip_id.zero?
138
130
 
@@ -45,7 +45,8 @@ module SpreeCmCommissioner
45
45
  'color' => 'color',
46
46
  'ticket-type' => 'string',
47
47
  'seat-type' => 'string',
48
- 'intercity-taxi' => 'string'
48
+ 'intercity-taxi' => 'string',
49
+ 'vote-package' => 'integer'
49
50
  }.freeze
50
51
 
51
52
  included do
@@ -26,8 +26,10 @@ 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
29
30
 
30
31
  state_machine.after_transition to: :complete, do: :increment_route_fulfilled_order_count
32
+ state_machine.after_transition to: :complete, do: :allocate_resources
31
33
 
32
34
  scope :accepted, -> { where(request_state: 'accepted') }
33
35
 
@@ -328,5 +330,13 @@ module SpreeCmCommissioner
328
330
  factory = OrderTelegramMessageFactory.new(title: title, order: self)
329
331
  TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory.message, parse_mode: factory.parse_mode)
330
332
  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
331
341
  end
332
342
  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].freeze
8
+ PRODUCT_TYPES = %i[accommodation service ecommerce transit show_episode vote_package advertisement].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
 
@@ -7,7 +7,6 @@ module SpreeCmCommissioner
7
7
  preference :telegram_order_alert_chat_id, :string
8
8
  preference :telegram_order_request_alert_chat_id, :string
9
9
  preference :telegram_new_vendor_alert_chat_id, :string
10
- preference :telegram_new_event_alert_chat_id, :string
11
10
  preference :assetlinks, :string, default: ''
12
11
  preference :apple_app_site_association, :string, default: ''
13
12
  end
@@ -3,7 +3,9 @@ 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 }
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
+ }
7
9
  end
8
10
  end
9
11
  end
@@ -0,0 +1,41 @@
1
+ module SpreeCmCommissioner
2
+ module VotingSessionLiveStreamConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ store_public_metadata :live_stream_url, :string
7
+ store_public_metadata :live_stream_enabled, :boolean
8
+ store_public_metadata :live_stream_thumbnail, :string
9
+ store_public_metadata :live_stream_title, :string
10
+ store_public_metadata :live_stream_description, :text
11
+
12
+ validate :validate_live_stream_url, if: :live_stream_url_changed?
13
+ before_save :update_live_stream_thumbnail
14
+ end
15
+
16
+ def embedded_live_stream_url
17
+ return nil if live_stream_url.blank?
18
+
19
+ result = SpreeCmCommissioner::UrlEmbed::YoutubeEmbed.call(url: live_stream_url)
20
+ result.success? ? result.value : nil
21
+ end
22
+
23
+ private
24
+
25
+ def validate_live_stream_url
26
+ return if live_stream_url.blank?
27
+
28
+ result = SpreeCmCommissioner::UrlEmbed::YoutubeEmbed.call(url: live_stream_url)
29
+ return if result.success?
30
+
31
+ errors.add(:live_stream_url, result.error)
32
+ end
33
+
34
+ def update_live_stream_thumbnail
35
+ return if live_stream_url.blank?
36
+
37
+ video_id = SpreeCmCommissioner::UrlEmbed::YoutubeEmbed.extract_video_id(live_stream_url)
38
+ self.live_stream_thumbnail = "https://img.youtube.com/vi/#{video_id}/hqdefault.jpg" if video_id
39
+ end
40
+ end
41
+ 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 }
6
+ enum :import_type, { :new_order => 0, :existing_order => 1, :aba_payment_reference => 2, :contestant_import => 3 }
7
7
  has_one_attached :imported_file
8
8
  friendly_id :name, use: :slugged
9
9
 
@@ -0,0 +1,7 @@
1
+ module SpreeCmCommissioner
2
+ module Imports
3
+ class ImportContestant < Import
4
+ preference :fail_rows, :string
5
+ end
6
+ end
7
+ end
@@ -23,23 +23,8 @@ module SpreeCmCommissioner
23
23
 
24
24
  before_save -> { self.product_type = variant.product_type }, if: -> { product_type.nil? }
25
25
 
26
- after_commit :schedule_product_cache_invalidation
27
-
28
- # Only for ecommerce: the app uses product.in_stock? (derived from quantity_available) for product cards.
29
- # Other types (e.g. accommodation, bus) have many inventory items (including daily-generated ones),
30
- # so invalidating per item would flood the task queue.
31
- #
32
- # This is temporary until we have a more robust cache invalidation strategy in place.
33
- def schedule_product_cache_invalidation
34
- return unless ecommerce?
35
- return unless saved_change_to_quantity_available?
36
-
37
- # Only fires on in_stock status transition (0 ↔ N); non-zero to non-zero changes are skipped.
38
- old_val, new_val = saved_change_to_quantity_available
39
- return if old_val.to_i.positive? && new_val.to_i.positive?
40
-
41
- SpreeCmCommissioner::MaintenanceTasks::CacheInvalidation.pending.create_or_find_by(maintainable: variant.product)
42
- end
26
+ # Create maintaining task to purge product related caches
27
+ after_commit { SpreeCmCommissioner::MaintenanceTasks::CacheInvalidation.pending.create_or_find_by(maintainable: variant.product) }
43
28
 
44
29
  def public_quantity_available
45
30
  [quantity_available, MAX_DISPLAY_STOCK].min
@@ -3,8 +3,6 @@ module SpreeCmCommissioner
3
3
  module MaintenanceTasks
4
4
  class CacheInvalidation < MaintenanceTask
5
5
  def maintain
6
- return if ENV['CACHE_AUTO_INVALIDATION_DISABLED'] == 'yes'
7
-
8
6
  if maintainable.is_a?(SpreeCmCommissioner::HomepageBackground) || maintainable.is_a?(Spree::Menu)
9
7
  SpreeCmCommissioner::HomepageDataLoader.clear_cache
10
8
  end
@@ -0,0 +1,36 @@
1
+ # Reconciles Redis vote counters against cm_votes for a specific voting session.
2
+ #
3
+ # Created automatically by VoteProcessor when Redis is unavailable during a vote —
4
+ # VoteProcessor skips async_execute (Sidekiq also needs Redis) and just creates
5
+ # the DB row so OrchestrateJob's cron can pick it up once Redis recovers.
6
+ #
7
+ # `maintain` runs three steps in order:
8
+ # 1. RebuildFromDb — overwrites Redis counters with DB totals (source of truth)
9
+ # 2. AuditCounters — logs any remaining mismatches between Redis and DB
10
+ # 3. SnapshotToDb — writes final aggregates to cm_voting_contestants and cm_voting_session_stats
11
+ #
12
+ # maintainable: SpreeCmCommissioner::VotingSession
13
+ #
14
+ # Trigger on-demand:
15
+ # MaintenanceTasks::VotingSession.create!(maintainable: session).async_execute
16
+ module SpreeCmCommissioner
17
+ module MaintenanceTasks
18
+ class VotingSession < MaintenanceTask
19
+ def maintain
20
+ ttl = rebuild_ttl
21
+ SpreeCmCommissioner::VoteCounters::RebuildFromDb.call(voting_session_id: maintainable.id, ttl: ttl)
22
+ SpreeCmCommissioner::VoteCounters::AuditCounters.call(voting_session_id: maintainable.id)
23
+ SpreeCmCommissioner::VoteCounters::SnapshotToDb.call(voting_session_id: maintainable.id)
24
+ end
25
+
26
+ # For closed sessions use the fixed 24h constant — closes_at is in the past
27
+ # so the dynamic formula would produce a TTL shorter than intended.
28
+ # For active sessions use closes_at + 24h buffer (same formula as Increment).
29
+ def rebuild_ttl
30
+ return SpreeCmCommissioner::VoteCounters::Base::REDIS_KEY_TTL if maintainable.closed?
31
+
32
+ [(maintainable.closes_at - Time.current).to_i + 1.day.to_i, 1].max
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ module SpreeCmCommissioner
2
+ class PreviewRole < Base
3
+ belongs_to :previewable, polymorphic: true, optional: false
4
+ belongs_to :role, class_name: 'Spree::Role', optional: false
5
+
6
+ validates :role_id, uniqueness: { scope: %i[previewable_type previewable_id] }
7
+ end
8
+ end