spree_cm_commissioner 2.6.2.pre.pre1 → 2.6.2.pre.pre.4

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +0 -29
  4. data/app/controllers/concerns/spree/admin/service_calendars_concern.rb +1 -3
  5. data/app/controllers/spree/admin/product_service_calendars_controller.rb +0 -1
  6. data/app/controllers/spree/api/v2/tenant/show_contestants_controller.rb +50 -0
  7. data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +54 -0
  8. data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +36 -0
  9. data/app/controllers/spree/api/v2/tenant/shows_controller.rb +39 -0
  10. data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +45 -0
  11. data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +2 -0
  12. data/app/interactors/spree_cm_commissioner/create_vendor.rb +0 -2
  13. data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +1 -2
  14. data/app/interactors/spree_cm_commissioner/sms.rb +2 -3
  15. data/app/jobs/spree_cm_commissioner/sms_pin_code_job.rb +1 -1
  16. data/app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb +10 -0
  17. data/app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb +10 -0
  18. data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +2 -1
  19. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +2 -1
  20. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +10 -0
  21. data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
  22. data/app/models/spree_cm_commissioner/pin_code.rb +1 -1
  23. data/app/models/spree_cm_commissioner/role_decorator.rb +0 -3
  24. data/app/models/spree_cm_commissioner/show.rb +140 -0
  25. data/app/models/spree_cm_commissioner/show_contestant.rb +29 -0
  26. data/app/models/spree_cm_commissioner/show_contestant_image.rb +11 -0
  27. data/app/models/spree_cm_commissioner/show_episode.rb +70 -0
  28. data/app/models/spree_cm_commissioner/show_person.rb +15 -0
  29. data/app/models/spree_cm_commissioner/show_person_assignment.rb +20 -0
  30. data/app/models/spree_cm_commissioner/show_person_image.rb +11 -0
  31. data/app/models/spree_cm_commissioner/taxon_decorator.rb +3 -0
  32. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +10 -1
  33. data/app/models/spree_cm_commissioner/tenant.rb +0 -1
  34. data/app/models/spree_cm_commissioner/user_decorator.rb +0 -1
  35. data/app/models/spree_cm_commissioner/variant_options.rb +4 -0
  36. data/app/models/spree_cm_commissioner/voting_contestant.rb +23 -0
  37. data/app/models/spree_cm_commissioner/voting_credit.rb +67 -0
  38. data/app/models/spree_cm_commissioner/voting_credit_transaction.rb +31 -0
  39. data/app/models/spree_cm_commissioner/voting_session.rb +97 -0
  40. data/app/serializers/spree/v2/tenant/line_item_serializer.rb +1 -1
  41. data/app/serializers/spree/v2/tenant/show_contestant_serializer.rb +19 -0
  42. data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +19 -0
  43. data/app/serializers/spree/v2/tenant/show_person_assignment_serializer.rb +16 -0
  44. data/app/serializers/spree/v2/tenant/show_person_serializer.rb +13 -0
  45. data/app/serializers/spree/v2/tenant/show_serializer.rb +25 -0
  46. data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +17 -0
  47. data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +13 -0
  48. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vehicle_type_serializer.rb +1 -1
  49. data/app/services/spree_cm_commissioner/agency_categories/create.rb +4 -1
  50. data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +33 -51
  51. data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +80 -0
  52. data/app/services/spree_cm_commissioner/voting_credits/credit_calculator.rb +35 -0
  53. data/app/services/spree_cm_commissioner/voting_credits/de_allocate.rb +88 -0
  54. data/app/views/spree/admin/exports/index.html.erb +10 -0
  55. data/app/views/spree/admin/product_service_calendars/index.html.erb +0 -1
  56. data/app/views/spree/admin/shared/service_calendars/_exception_rules.html.erb +7 -20
  57. data/config/initializers/spree_permitted_attributes.rb +3 -0
  58. data/config/locales/en.yml +0 -6
  59. data/config/locales/km.yml +0 -5
  60. data/config/routes.rb +12 -3
  61. data/db/migrate/20260309230148_create_cm_show_people.rb +14 -0
  62. data/db/migrate/20260309230149_create_cm_show_people_assignments.rb +16 -0
  63. data/db/migrate/20260310082711_create_cm_show_contestants.rb +28 -0
  64. data/db/migrate/20260310082720_create_cm_voting_sessions.rb +21 -0
  65. data/db/migrate/20260310082721_create_cm_voting_contestants.rb +23 -0
  66. data/db/migrate/20260310082734_add_voting_fields_to_spree_taxons.rb +9 -0
  67. data/db/migrate/20260310082735_add_type_to_spree_products.rb +6 -0
  68. data/db/migrate/20260310082749_create_cm_voting_credits.rb +27 -0
  69. data/db/migrate/20260326080200_create_cm_voting_credit_transactions.rb +27 -0
  70. data/db/migrate/20260402000001_add_voting_credit_scope_to_spree_taxons.rb +6 -0
  71. data/db/migrate/20260402000002_rename_scopeable_to_votable_in_cm_voting_credits.rb +12 -0
  72. data/docs/sql/jsonb_query_guide.md +57 -0
  73. data/lib/spree_cm_commissioner/test_helper/factories/show_episode_factory.rb +12 -0
  74. data/lib/spree_cm_commissioner/test_helper/factories/show_factory.rb +92 -0
  75. data/lib/spree_cm_commissioner/test_helper/factories/vote_credit_factory.rb +38 -0
  76. data/lib/spree_cm_commissioner/test_helper/factories/voting_session_factory.rb +11 -0
  77. data/lib/spree_cm_commissioner/version.rb +1 -1
  78. metadata +46 -15
  79. data/app/controllers/concerns/spree_cm_commissioner/turnstile_protectable.rb +0 -42
  80. data/app/controllers/spree/api/v2/organizer/invite_guests_controller.rb +0 -91
  81. data/app/controllers/spree/api/v2/storefront/saved_guests_controller.rb +0 -77
  82. data/app/interactors/spree_cm_commissioner/turnstile_token_validator.rb +0 -55
  83. data/app/jobs/spree_cm_commissioner/cleanup_expired_access_tokens_job.rb +0 -9
  84. data/app/models/spree_cm_commissioner/agency.rb +0 -12
  85. data/app/serializers/spree/v2/organizer/invite_guest_serializer.rb +0 -13
  86. data/app/services/spree_cm_commissioner/cleanup_expired_access_tokens.rb +0 -52
  87. data/app/views/spree/admin/product_service_calendars/edit.html.erb +0 -9
  88. data/db/migrate/20260311105437_add_tenant_to_cm_sms_logs.rb +0 -5
  89. data/db/migrate/20260318081516_create_cm_agencies.rb +0 -18
  90. data/db/migrate/20260319090000_add_role_type_to_spree_roles.rb +0 -5
  91. data/db/migrate/20260320103313_add_index_to_spree_oauth_access_tokens_created_at.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e063a62652e0598802f2cacf7f08755457cbf97a5702c0d2ec862adff84238ee
4
- data.tar.gz: 9a3a9cb10a7c94363a79c5ebb22fcf71c6854e688b9a17d220de72566c562bba
3
+ metadata.gz: 4c381df113afbf82a9586168ede0e86f1bfee87849e50d3f9aed7a4b5455c2ab
4
+ data.tar.gz: 8466ba5865693c20fe012ca0463b584c9be3fe1aed6e25f3d37d31ef3ad76121
5
5
  SHA512:
6
- metadata.gz: 2ed2e276d121939627ca4d38480eada55aa4a60b7933891b987d0337a74fdfefe8deea48b424ee20a66a30b1a44f09369330263433a76baf14ae26c2a5301bdb
7
- data.tar.gz: dded17adb10e46eee97c2eff877a1520a897b84cf71695ea00a2d418a83131ad9c03b713c6379804f27270f61426a1bf44249b73ab18c74aea74131c4b5398a5
6
+ metadata.gz: e8ad8ac4e47e7a75a68f60244bf4033fb5d035bbd722e7f7d8ecebad7f81d08634990087a47e8702d47fb919bea1c60e3ba81f35fc1f772c2d0e0efa41c59b2f
7
+ data.tar.gz: 4a0ae2cd87ccca4f91cd2604a979771192cabffaf63978290a86dae624e25e18c1cf581f2d09a5f9805fa32f159b6e7f4b1982b3bc85958dd53da3b1385764c3
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.6.2.pre.pre1)
37
+ spree_cm_commissioner (2.6.2.pre.pre.4)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
data/README.md CHANGED
@@ -153,37 +153,8 @@ PIN_CODE_DEBUG_NOTIFIY_TELEGRAM_ENABLE="yes"
153
153
  EXCEPTION_NOTIFY_ENABLE="yes" # yes or no
154
154
  EXCEPTION_TELEGRAM_BOT_TOKEN=""
155
155
  EXCEPTION_NOTIFIER_TELEGRAM_CHANNEL_ID=""
156
-
157
- TURNSTILE_SECRET_KEY="" # Cloudflare Turnstile secret key (server-side verification)
158
156
  ```
159
157
 
160
- ### Cloudflare Turnstile
161
-
162
- Cloudflare Turnstile is used to protect sensitive API endpoints from bots. The server-side validation is handled by `SpreeCmCommissioner::TurnstileTokenValidator` and the concern `SpreeCmCommissioner::TurnstileProtectable`.
163
-
164
- **Protected endpoints:**
165
-
166
- - `POST /api/v2/storefront/pin_code_generators`
167
- - `POST /api/v2/storefront/pin_code_otp_generators`
168
- - `POST /api/v2/storefront/user_registration_with_pin_codes`
169
- - `PUT /api/v2/storefront/reset_passwords`
170
- - `PATCH /api/v2/storefront/checkout/complete`
171
-
172
- **How it works:**
173
-
174
- 1. The client sends a Turnstile token via the `X-Turnstile-Token` HTTP header
175
- 2. `TurnstileProtectable` reads the header and calls `TurnstileTokenValidator`
176
- 3. The validator POSTs to Cloudflare's siteverify API with the token and `TURNSTILE_SECRET_KEY`
177
- 4. Returns 403 if verification fails
178
-
179
- **Configuration:**
180
-
181
- 1. Set `TURNSTILE_SECRET_KEY` in `.env` (obtained from [Cloudflare Turnstile dashboard](https://dash.cloudflare.com/))
182
- 2. For local development testing, use Cloudflare's test keys:
183
- - Always pass: `1x0000000000000000000000000000000AA`
184
- - Always fail: `2x0000000000000000000000000000000AA`
185
- - Token spent: `3x0000000000000000000000000000000AA`
186
-
187
158
  ## JSON Format Examples For Store and Tenant Preferences
188
159
 
189
160
  ### Asset Links Format
@@ -44,8 +44,6 @@ module Spree
44
44
  private
45
45
 
46
46
  def build_exception_rules(exception_rules)
47
- return [] if exception_rules.nil?
48
-
49
47
  exception_rules.values.reject! { |rule| rule['from'].blank? || rule['to'].blank? || rule['type'].blank? } || exception_rules.values
50
48
  end
51
49
 
@@ -59,7 +57,7 @@ module Spree
59
57
  end
60
58
 
61
59
  def set_exception_rules
62
- @exception_rules = (@object.exception_rules.presence || [{ from: DateTime.now, to: DateTime.now, type: 'exclusion', reason: nil }])
60
+ @exception_rules = [{ from: DateTime.now, to: DateTime.now, type: 'exclusion', reason: nil }]
63
61
  end
64
62
 
65
63
  def build_resource
@@ -6,7 +6,6 @@ module Spree
6
6
  before_action :load_product
7
7
  before_action :ensure_product_type_supports_calendar
8
8
  before_action :ensure_product_has_no_calendar, only: %i[new create]
9
- before_action :set_exception_rules, only: %i[edit update]
10
9
 
11
10
  create.before :set_calendarable
12
11
  update.before :set_calendarable
@@ -0,0 +1,50 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class ShowContestantsController < BaseController
6
+ private
7
+
8
+ # override
9
+ def model_class
10
+ SpreeCmCommissioner::ShowContestant
11
+ end
12
+
13
+ # override
14
+ def scope
15
+ return model_class.none unless current_show
16
+
17
+ model_class
18
+ .where(show_id: current_show.id)
19
+ .includes(
20
+ :show_contestant_images,
21
+ :current_voting_session,
22
+ assigned_show_person: :show_person_images,
23
+ show: [
24
+ :parent,
25
+ { episodes: :voting_sessions },
26
+ { seasons: { episodes: :voting_sessions } }
27
+ ]
28
+ )
29
+ end
30
+
31
+ def current_show
32
+ @current_show ||= show_scope.find_by!(slug: params[:show_id])
33
+ end
34
+
35
+ def show_scope
36
+ vendor_ids = @tenant.vendors.select(:id)
37
+ return SpreeCmCommissioner::Show.none if vendor_ids.blank?
38
+
39
+ SpreeCmCommissioner::Show.where(vendor_id: vendor_ids)
40
+ end
41
+
42
+ # override
43
+ def resource_serializer
44
+ Spree::V2::Tenant::ShowContestantSerializer
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class ShowPeopleController < BaseController
6
+ private
7
+
8
+ # override
9
+ def model_class
10
+ SpreeCmCommissioner::ShowPerson
11
+ end
12
+
13
+ # override
14
+ def scope
15
+ scope = current_show.show_people
16
+ .joins(:assignments)
17
+ .distinct
18
+ .includes(
19
+ :assignments,
20
+ assigned_contestants: %i[show_contestant_images current_voting_session]
21
+ )
22
+
23
+ role = params[:role].presence
24
+ return scope if role.blank?
25
+
26
+ role_value = SpreeCmCommissioner::ShowPersonAssignment.roles[role.to_s]
27
+ return scope.none if role_value.nil?
28
+
29
+ scope.where(cm_show_people_assignments: { role: role_value })
30
+ end
31
+
32
+ # override
33
+ def resource_serializer
34
+ Spree::V2::Tenant::ShowPersonSerializer
35
+ end
36
+
37
+ # override
38
+ def collection_serializer
39
+ Spree::V2::Tenant::ShowPersonSerializer
40
+ end
41
+
42
+ def current_show
43
+ @current_show ||= show_scope.find_by!(slug: params[:show_id])
44
+ end
45
+
46
+ def show_scope
47
+ vendor_ids = @tenant.vendors.select(:id)
48
+ SpreeCmCommissioner::Show.where(vendor_id: vendor_ids)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class ShowPersonAssignmentsController < BaseController
6
+ private
7
+
8
+ def model_class
9
+ SpreeCmCommissioner::ShowPersonAssignment
10
+ end
11
+
12
+ def scope
13
+ vendor_ids = @tenant.vendors.select(:id)
14
+ show_ids = SpreeCmCommissioner::Show.where(vendor_id: vendor_ids).select(:id)
15
+
16
+ model_class.where(show_id: show_ids)
17
+ .where(show_person_id: params[:person_id], show_id: current_show.id)
18
+ .includes(:show_person, :show)
19
+ end
20
+
21
+ def current_show
22
+ @current_show ||= SpreeCmCommissioner::Show.find_by!(slug: params[:show_id])
23
+ end
24
+
25
+ def resource_serializer
26
+ Spree::V2::Tenant::ShowPersonAssignmentSerializer
27
+ end
28
+
29
+ def collection_serializer
30
+ Spree::V2::Tenant::ShowPersonAssignmentSerializer
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
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)
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
+
31
+ # override
32
+ def collection_serializer
33
+ Spree::V2::Tenant::ShowSerializer
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
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
+ vendor_ids = @tenant.vendors.select(:id)
31
+ show_ids = SpreeCmCommissioner::Show.where(vendor_id: vendor_ids).select(:id)
32
+ episode_ids = Spree::Product.where(event_id: show_ids, type: 'SpreeCmCommissioner::ShowEpisode').select(:id)
33
+ model_class
34
+ .where(episode_id: episode_ids)
35
+ .includes(:episode)
36
+ end
37
+
38
+ def load_voting_session
39
+ @voting_session = scope.find(params[:voting_session_id])
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ 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
@@ -11,8 +11,6 @@ module SpreeCmCommissioner
11
11
  create_logo
12
12
  create_role
13
13
  end
14
-
15
- context.vendor = @vendor
16
14
  end
17
15
 
18
16
  private
@@ -20,8 +20,7 @@ module SpreeCmCommissioner
20
20
  options = {
21
21
  from: context.pin_code&.application&.sms_sender_id,
22
22
  to: context.pin_code.contact,
23
- body: I18n.t('pincode_sender.sms.body', code: context.pin_code.code, readable_type: context.pin_code.readable_type),
24
- tenant_id: context.pin_code&.application&.tenant_id
23
+ body: I18n.t('pincode_sender.sms.body', code: context.pin_code.code, readable_type: context.pin_code.readable_type)
25
24
  }
26
25
 
27
26
  SpreeCmCommissioner::SmsPinCodeJob.perform_later(options)
@@ -36,13 +36,12 @@ module SpreeCmCommissioner
36
36
  from_number: sms_options[:from],
37
37
  to_number: sms_options[:to],
38
38
  body: sms_options[:body],
39
- adapter_name: adapter.class.name.demodulize,
40
- tenant_id: sms_options[:tenant_id]
39
+ adapter_name: adapter.class.name.demodulize
41
40
  )
42
41
  end
43
42
 
44
43
  def sms_options
45
- opts = { to: context.to, body: context.body, tenant_id: context.tenant_id }
44
+ opts = { to: context.to, body: context.body }
46
45
  opts[:to] = sanitize(opts[:to]) if opts[:to].present?
47
46
  opts[:from] = from_number
48
47
  opts
@@ -1,6 +1,6 @@
1
1
  module SpreeCmCommissioner
2
2
  class SmsPinCodeJob < SpreeCmCommissioner::SmsJob
3
- # options = { from: xxxx, to: xxxx, body: xxxx , tenant_id: xxxx}
3
+ # options = { from: xxxx, to: xxxx, body: xxxx }
4
4
  def perform(options = {})
5
5
  SpreeCmCommissioner::Sms.call(options)
6
6
  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|
@@ -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
@@ -27,8 +27,10 @@ module SpreeCmCommissioner
27
27
 
28
28
  state_machine.after_transition to: :canceled, do: :precalculate_conversion
29
29
  state_machine.after_transition to: :canceled, do: :restock_inventory!
30
+ state_machine.after_transition to: :canceled, do: :de_allocate_resources
30
31
 
31
32
  state_machine.after_transition to: :complete, do: :increment_route_fulfilled_order_count
33
+ state_machine.after_transition to: :complete, do: :allocate_resources
32
34
 
33
35
  scope :accepted, -> { where(request_state: 'accepted') }
34
36
 
@@ -322,5 +324,13 @@ module SpreeCmCommissioner
322
324
  factory = OrderTelegramMessageFactory.new(title: title, order: self)
323
325
  TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory.message, parse_mode: factory.parse_mode)
324
326
  end
327
+
328
+ def allocate_resources
329
+ SpreeCmCommissioner::VotingCreditAllocationJob.perform_later(order_id: id)
330
+ end
331
+
332
+ def de_allocate_resources
333
+ SpreeCmCommissioner::VotingCreditDeAllocationJob.perform_later(order_id: id)
334
+ end
325
335
  end
326
336
  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 vote_package].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
 
@@ -132,7 +132,7 @@ module SpreeCmCommissioner
132
132
  'SpreeCmCommissioner::PinCodeMobileConfirm' => I18n.t('pincode.readable_type.confirmation_code'),
133
133
  'SpreeCmCommissioner::PinCodeUpdateUserLogin' => I18n.t('pincode.readable_type.update_user_login'),
134
134
  'SpreeCmCommissioner::PinCodeEmailConfirm' => I18n.t('pincode.readable_type.confirmation_code'),
135
- 'SpreeCmCommissioner::PinCodeOtp' => I18n.t('pincode.readable_type.otp_code'),
135
+ 'SpreeCmCommissioner::OtpCode' => I18n.t('pincode.readable_type.otp_code'),
136
136
  'SpreeCmCommissioner::PinCodeTelegram' => I18n.t('pincode.readable_type.telegram_verification')
137
137
  }
138
138
  end
@@ -1,8 +1,6 @@
1
1
  module SpreeCmCommissioner
2
2
  module RoleDecorator
3
3
  def self.prepended(base)
4
- base.enum role_type: { internal: 0, external: 1 }
5
-
6
4
  base.has_many :role_permissions, class_name: 'SpreeCmCommissioner::RolePermission'
7
5
  base.has_many :permissions, through: :role_permissions, class_name: 'SpreeCmCommissioner::Permission'
8
6
 
@@ -12,7 +10,6 @@ module SpreeCmCommissioner
12
10
  base.scope :filter_by_vendor, lambda { |vendor|
13
11
  where(vendor_id: vendor)
14
12
  }
15
- base.scope :filter_external, -> { where(role_type: :external) }
16
13
 
17
14
  base.accepts_nested_attributes_for :role_permissions, allow_destroy: true
18
15
 
@@ -0,0 +1,140 @@
1
+ module SpreeCmCommissioner
2
+ class Show < Spree::Taxon
3
+ include SpreeCmCommissioner::StoreMetadata
4
+
5
+ has_many :seasons, class_name: 'SpreeCmCommissioner::Show', foreign_key: :parent_id, inverse_of: :parent
6
+ has_many :show_contestants, class_name: 'SpreeCmCommissioner::ShowContestant', inverse_of: :show, dependent: :destroy
7
+ has_many :show_people_assignments, class_name: 'SpreeCmCommissioner::ShowPersonAssignment', dependent: :destroy
8
+ has_many :show_people, through: :show_people_assignments, source: :show_person
9
+ has_many :episodes, class_name: 'SpreeCmCommissioner::ShowEpisode', foreign_key: :event_id
10
+ has_one :current_episode, -> { current_or_next_upcoming }, class_name: 'SpreeCmCommissioner::ShowEpisode', foreign_key: :event_id
11
+ has_one :current_voting_session, through: :current_episode
12
+
13
+ before_validation { self.type = self.class.name }
14
+ before_validation { self.kind = :event }
15
+ before_validation :assign_taxonomy_and_parent, on: :create
16
+
17
+ store_private_metadata :season_number, :integer
18
+ store_private_metadata :season_year, :integer
19
+
20
+ # Define standard voting configuration keys
21
+ store_accessor :voting_config,
22
+ :free_vote_limit,
23
+ :free_vote_limit_type,
24
+ :paid_vote_enabled,
25
+ :max_paid_votes_per_session,
26
+ :sms_vote_enabled,
27
+ :sms_vote_cost_usd,
28
+ :operator_vote_enabled
29
+
30
+ # voting_credit_scope determines how purchased vote credits are grouped (scoped).
31
+ # Changing this after credits have been allocated will cause inconsistencies:
32
+ # - existing credits are scoped to the old type (e.g. ShowEpisode),
33
+ # but new purchases will create credits under the new type (e.g. Show),
34
+ # leading to split balances that cannot be merged.
35
+ # UI rule: once voting_credit_scope is set and any VotingCredit exists for this show, the field must be read-only — do not allow changes.
36
+ # Note: VoteCredit of user should base on CREDIT_SCOPES
37
+ # - if scope is Show, User's credits should be only 1 per show, and can be used for any episode under the show.
38
+ # - if scope is ShowEpisode, User's credits should be N per show (N is the number of episodes), and can be only used for specific episode.
39
+ # - if scope is VotingSession, User's credits should be N per show (N is the number of voting sessions), and can be only used for specific voting session. # rubocop:disable Layout/LineLength
40
+ CREDIT_SCOPES = %w[Show ShowEpisode VotingSession].freeze
41
+
42
+ def self.polymorphic_name
43
+ name
44
+ end
45
+
46
+ validate :voting_credit_scope_immutable, if: :voting_credit_scope_changed?
47
+
48
+ def credit_votable_type
49
+ voting_credit_scope = self[:voting_credit_scope] || 'ShowEpisode'
50
+
51
+ type = CREDIT_SCOPES.include?(voting_credit_scope) ? voting_credit_scope : 'ShowEpisode'
52
+ "SpreeCmCommissioner::#{type}"
53
+ end
54
+
55
+ # Define standard fraud configuration keys
56
+ store_accessor :fraud_config,
57
+ :max_votes_per_minute_per_user,
58
+ :max_votes_per_minute_per_ip,
59
+ :max_accounts_per_device,
60
+ :block_vpn
61
+
62
+ def show?
63
+ depth == 1
64
+ end
65
+
66
+ def season?
67
+ depth == 2
68
+ end
69
+
70
+ def display_name
71
+ if season? && parent.present?
72
+ "#{parent.name} #{name}"
73
+ else
74
+ name
75
+ end
76
+ end
77
+
78
+ def contestants
79
+ show_contestants
80
+ end
81
+
82
+ def effective_voting_config
83
+ config = parse_json_config(voting_config)
84
+ return config if show?
85
+
86
+ config.reverse_merge(parent&.effective_voting_config || {})
87
+ end
88
+
89
+ def effective_fraud_config
90
+ config = parse_json_config(fraud_config)
91
+ return config if show?
92
+
93
+ config.reverse_merge(parent&.effective_fraud_config || {})
94
+ end
95
+
96
+ def build_next_season(attributes = {})
97
+ last_season = seasons.order(created_at: :desc).first
98
+
99
+ default_number = (last_season&.season_number || 0) + 1
100
+ default_year = last_season&.season_year || Time.current.year
101
+
102
+ seasons.new(
103
+ {
104
+ season_number: default_number,
105
+ season_year: default_year
106
+ }.merge(attributes)
107
+ )
108
+ end
109
+
110
+ private
111
+
112
+ def assign_taxonomy_and_parent
113
+ if parent.present?
114
+ self.taxonomy ||= parent.taxonomy
115
+ elsif taxonomy_id.blank?
116
+ self.taxonomy = Spree::Taxonomy.shows
117
+ self.parent = taxonomy.root
118
+ end
119
+ end
120
+
121
+ def voting_credit_scope_immutable
122
+ show_has_credits = SpreeCmCommissioner::VotingCredit.exists?(votable_type: 'SpreeCmCommissioner::Show', votable_id: id)
123
+ episode_has_credits = SpreeCmCommissioner::VotingCredit.exists?(votable_type: 'SpreeCmCommissioner::ShowEpisode',
124
+ votable_id: episodes.select(:id)
125
+ )
126
+ return unless show_has_credits || episode_has_credits
127
+
128
+ errors.add(:voting_credit_scope, 'cannot be changed after voting credits have been allocated')
129
+ end
130
+
131
+ def parse_json_config(value)
132
+ return {} if value.nil? || value == ''
133
+ return value if value.is_a?(Hash)
134
+
135
+ JSON.parse(value)
136
+ rescue JSON::ParserError
137
+ {}
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,29 @@
1
+ module SpreeCmCommissioner
2
+ class ShowContestant < SpreeCmCommissioner::Base
3
+ belongs_to :show, class_name: 'SpreeCmCommissioner::Show', optional: false
4
+ belongs_to :assigned_show_person, class_name: 'SpreeCmCommissioner::ShowPerson', optional: true
5
+ belongs_to :user, class_name: 'Spree::User', optional: true
6
+ has_many :show_contestant_images, lambda {
7
+ order(:position)
8
+ }, as: :viewable, class_name: 'SpreeCmCommissioner::ShowContestantImage', dependent: :destroy
9
+ has_one :show_person_images, -> { order(:position) }, as: :viewable, class_name: 'SpreeCmCommissioner::ShowPersonImage', dependent: :destroy
10
+
11
+ has_many :voting_contestants, class_name: 'SpreeCmCommissioner::VotingContestant', dependent: :destroy
12
+ has_one :current_voting_session, through: :show
13
+
14
+ enum status: { active: 0, eliminated: 1, advanced: 2, winner: 3, withdrawn: 4 }
15
+ enum gender: { :other => 0, :male => 1, :female => 2 }
16
+
17
+ enum category: { boys: 0, girls: 1, groups: 2, overs: 3 }
18
+
19
+ # Define standard Social Link Keys
20
+ store_accessor :social_links, :facebook, :instagram, :twitter, :linkedin, :youtube, :tiktok, :telegram
21
+
22
+ validates :name, presence: true
23
+ validates :contestant_number, uniqueness: { scope: :show_id }, allow_nil: true
24
+
25
+ scope :ordered, -> { order(:position, :id) }
26
+
27
+ self.whitelisted_ransackable_attributes = %w[name contestant_number status gender category]
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module SpreeCmCommissioner
2
+ class ShowContestantImage < Asset
3
+ def asset_styles
4
+ {
5
+ thumb: '60x60>',
6
+ small: '180x180>',
7
+ medium: '480x480>'
8
+ }
9
+ end
10
+ end
11
+ end