spree_cm_commissioner 2.5.2.pre.pre2 → 2.5.2

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_build_gem.yml +1 -1
  3. data/Gemfile.lock +1 -1
  4. data/app/controllers/spree/api/v2/storefront/homepage_data_controller.rb +35 -4
  5. data/app/controllers/spree_cm_commissioner/application_controller_decorator.rb +0 -2
  6. data/app/interactors/spree_cm_commissioner/conversion_pre_calculator.rb +12 -3
  7. data/app/interactors/spree_cm_commissioner/notification_reader.rb +3 -1
  8. data/app/jobs/spree_cm_commissioner/line_items/sync_event_date_job.rb +10 -0
  9. data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +30 -23
  10. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +1 -2
  11. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +28 -4
  12. data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +47 -7
  13. data/app/models/spree_cm_commissioner/export.rb +1 -3
  14. data/app/models/spree_cm_commissioner/guest.rb +0 -21
  15. data/app/models/spree_cm_commissioner/notification.rb +28 -0
  16. data/app/models/spree_cm_commissioner/taxon_decorator.rb +1 -1
  17. data/app/models/spree_cm_commissioner/variant_options.rb +0 -5
  18. data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +2 -1
  19. data/app/serializers/spree_cm_commissioner/v2/storefront/homepage_data_serializer.rb +3 -9
  20. data/app/services/spree_cm_commissioner/guests/finalize.rb +76 -0
  21. data/app/services/spree_cm_commissioner/homepage_data.rb +23 -0
  22. data/app/services/spree_cm_commissioner/line_items/sync_event_date.rb +22 -0
  23. data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +14 -19
  24. data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +2 -4
  25. data/app/services/spree_cm_commissioner/trips/variants/create.rb +6 -6
  26. data/app/services/spree_cm_commissioner/user_counters_service.rb +55 -0
  27. data/app/views/spree/admin/classifications/edit.html.erb +1 -1
  28. data/config/routes.rb +0 -27
  29. data/db/migrate/20260128043540_add_counter_cache_to_spree_users.rb +6 -0
  30. data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +0 -6
  31. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +0 -13
  32. data/lib/spree_cm_commissioner/transit/route_stop_collection.rb +0 -28
  33. data/lib/spree_cm_commissioner/transit/trip_form.rb +0 -24
  34. data/lib/spree_cm_commissioner/transit/trip_stop_form.rb +1 -5
  35. data/lib/spree_cm_commissioner/version.rb +1 -1
  36. data/lib/spree_cm_commissioner.rb +0 -1
  37. metadata +10 -35
  38. data/app/controllers/concerns/spree_cm_commissioner/events/role_authorization.rb +0 -36
  39. data/app/controllers/spree/events/base_controller.rb +0 -35
  40. data/app/controllers/spree/events/check_ins_controller.rb +0 -23
  41. data/app/controllers/spree/events/data_exports_controller.rb +0 -41
  42. data/app/controllers/spree/events/errors_controller.rb +0 -19
  43. data/app/controllers/spree/events/guests_controller.rb +0 -139
  44. data/app/controllers/spree/events/state_changes_controller.rb +0 -11
  45. data/app/errors/spree_cm_commissioner/unauthorized_event_error.rb +0 -4
  46. data/app/interactors/spree_cm_commissioner/event_line_items_date_syncer.rb +0 -19
  47. data/app/jobs/spree_cm_commissioner/event_line_items_date_syncer_job.rb +0 -8
  48. data/app/jobs/spree_cm_commissioner/export_csv_job.rb +0 -7
  49. data/app/models/spree_cm_commissioner/exports/export_guest_csv.rb +0 -33
  50. data/app/services/spree_cm_commissioner/exports/export_guest_csv_service.rb +0 -48
  51. data/app/views/spree/events/check_ins/index.html.erb +0 -59
  52. data/app/views/spree/events/data_exports/index.html.erb +0 -54
  53. data/app/views/spree/events/errors/_error.html.erb +0 -7
  54. data/app/views/spree/events/errors/forbidden.html.erb +0 -1
  55. data/app/views/spree/events/errors/resource_not_found.html.erb +0 -1
  56. data/app/views/spree/events/guests/_check_in_status.html.erb +0 -44
  57. data/app/views/spree/events/guests/_form.html.erb +0 -37
  58. data/app/views/spree/events/guests/_swap_guest_bib_number.html.erb +0 -18
  59. data/app/views/spree/events/guests/edit.html.erb +0 -86
  60. data/app/views/spree/events/guests/index.html.erb +0 -102
  61. data/app/views/spree/events/shared/_event_switcher.html.erb +0 -39
  62. data/app/views/spree/events/shared/_filters.html.erb +0 -50
  63. data/app/views/spree/events/shared/_guest_tabs.html.erb +0 -19
  64. data/app/views/spree/events/shared/_header.html.erb +0 -10
  65. data/app/views/spree/events/shared/_main_menu.html.erb +0 -22
  66. data/app/views/spree/events/state_changes/index.html.erb +0 -48
  67. data/lib/spree_cm_commissioner/test_helper/factories/export_guest_csv_factory.rb +0 -9
  68. data/lib/spree_cm_commissioner/transit/service_calendar_form.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 415383ae7339303ff881a06d0642275576a1aa2e0bd685d2dfbf239cda829f6e
4
- data.tar.gz: da42ba2da856fff29f83d67f574d6e07cd06c1066a1baa0ef30b92268533d8b0
3
+ metadata.gz: 847e4b0b554ef076b2daeafe2d90509a434254ef6bb7bc19d34905d9a72f5a42
4
+ data.tar.gz: ce1c22521e38c5d012d2cb3fbe1ac041958a6054808c9b1786f4446a3046483d
5
5
  SHA512:
6
- metadata.gz: b3590bf3744645b2af02f779ccf4d6797879e71a47c9c01e524514428396813cc6752f25e361cf1489e0f1a2f106ba9987f43581d97788ce19ed290848a7fc68
7
- data.tar.gz: 6d0b09ce6143a18db3118c284ae0c1b334bbd29baf9dcf106ec4253144afbdb5da6e78f952219821ccc782601eee2ac20f7f1ccf7f22148290ac9ae0f1a3a012
6
+ metadata.gz: 1bfbb01640f43c2b9540063a8c5e368b44b4009ecd27653f739c030c65ceffcdc48d1141d466f90e2870d3c088f933260366f9db3f3bfb4b1c68dba402f5522e
7
+ data.tar.gz: 7f128de24e2ad50a06c9a148dc37c2ccc4e8396077a32c2456a89a35b45236562073a185f67e8e50dca5b1a59efd9b6dbcfeef2018aa63becc88b334fa070609
@@ -164,7 +164,7 @@ jobs:
164
164
  env:
165
165
  DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
166
166
  run: |
167
- bundle exec brakeman
167
+ bundle exec brakeman --no-exit-on-warn
168
168
 
169
169
  test:
170
170
  needs: [setup]
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.5.2.pre.pre2)
37
+ spree_cm_commissioner (2.5.2)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -3,12 +3,43 @@ module Spree
3
3
  module V2
4
4
  module Storefront
5
5
  class HomepageDataController < ::Spree::Api::V2::ResourceController
6
+ before_action :load_homepage_background, only: [:show]
7
+ before_action :load_menu, only: [:show]
8
+
6
9
  def show
7
- home_data_loader = SpreeCmCommissioner::HomepageDataLoader.with_cache
10
+ resource = SpreeCmCommissioner::HomepageData.new(
11
+ menu: @menu,
12
+ homepage_background: @homepage_background
13
+ )
14
+ render_serialized_payload { serialize_resource(resource) }
15
+ end
16
+
17
+ private
18
+
19
+ def serialize_resource(resource)
20
+ resource_serializer.new(
21
+ resource,
22
+ params: serializer_params,
23
+ include: resource_includes
24
+ ).serializable_hash
25
+ end
26
+
27
+ def load_homepage_background
28
+ @homepage_background ||= SpreeCmCommissioner::HomepageBackground
29
+ .active
30
+ .includes(:app_image, :web_image)
31
+ .where(segment: params[:segment] || :general)
32
+ .order(priority: :asc)
33
+ .first
34
+ end
8
35
 
9
- render_serialized_payload do
10
- serialize_resource(home_data_loader)
11
- end
36
+ def load_menu
37
+ # Spree::Menu::MENU_LOCATIONS = ['Header', 'Footer']
38
+ # Current our homepage use only location: header
39
+ @menu ||= Spree::Menu.by_locale(I18n.locale)
40
+ .includes({ menu_items: %i[children parent icon] })
41
+ .where(location: 'header')
42
+ .first
12
43
  end
13
44
 
14
45
  def resource_serializer
@@ -13,8 +13,6 @@ module SpreeCmCommissioner
13
13
  def after_sign_in_path_for(_)
14
14
  if spree_current_user.admin?
15
15
  admin_path
16
- elsif spree_current_user.organizer? && spree_current_user.events.present?
17
- event_guests_path(spree_current_user.events.first.slug)
18
16
  else
19
17
  '/'
20
18
  end
@@ -5,7 +5,7 @@ module SpreeCmCommissioner
5
5
  def call
6
6
  update_conversion
7
7
  reassign_guests_event_id
8
- generate_guests_bib_number
8
+ refinalize_guests
9
9
  end
10
10
 
11
11
  def update_conversion
@@ -28,14 +28,23 @@ module SpreeCmCommissioner
28
28
  end
29
29
  end
30
30
 
31
- def generate_guests_bib_number
31
+ def refinalize_guests
32
32
  return if event_id.blank?
33
33
 
34
34
  SpreeCmCommissioner::Guest
35
35
  .complete
36
36
  .where(event_id: event_id)
37
37
  .none_bib
38
- .find_each(&:generate_bib!)
38
+ .find_each do |guest|
39
+ result = SpreeCmCommissioner::Guests::Finalize.call(
40
+ guest: guest,
41
+ user: guest.line_item.order.user,
42
+ event: guest.event,
43
+ variant: guest.variant
44
+ )
45
+
46
+ result.success? || raise(ActiveRecord::RecordInvalid, guest)
47
+ end
39
48
  end
40
49
 
41
50
  def event_id
@@ -15,9 +15,11 @@ module SpreeCmCommissioner
15
15
  end
16
16
 
17
17
  def read_all_user_unread_notifications
18
- # mark all unread notifications accept type 'guest_dynamic_field_notification'
18
+ # mark all unread notifications except type 'guest_dynamic_field_notification'
19
19
  # due to it requires user to fill info before mark as read
20
20
  user.notifications.markable_notifications.mark_as_read!
21
+
22
+ SpreeCmCommissioner::UserCountersService.reset_unread_notifications_count(user)
21
23
  end
22
24
  end
23
25
  end
@@ -0,0 +1,10 @@
1
+ module SpreeCmCommissioner
2
+ module LineItems
3
+ class SyncEventDateJob < ApplicationJob
4
+ def perform(options = {})
5
+ event = Spree::Taxon.event.find(options[:event_id])
6
+ SpreeCmCommissioner::LineItems::SyncEventDate.call(event: event)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -52,30 +52,37 @@ module SpreeCmCommissioner
52
52
  self.event_id ||= product.event_id
53
53
  end
54
54
 
55
- # Line item date now depends directly on the event date and variant date.
56
- # No longer depend on the event section date.
57
- # This keeps the setup simple for the organizer and consistent for users.
55
+ # Calculates line item duration with priority:
56
+ # - Date: variant date > event date > user input (from_date/to_date)
57
+ # - Time: always uses variant time configuration
58
+ #
59
+ # The variant date takes highest priority. If variant has no date configured,
60
+ # falls back to event date, then to user-provided date.
61
+ #
62
+ # Examples:
63
+ # 1. Variant has full date/time config:
64
+ # Variant: 2024-01-15 14:00 to 2024-01-16 18:00
65
+ # Event: 2024-07-20, User: 2024-06-15
66
+ # → Result: 2024-01-15 14:00 to 2024-01-16 18:00 (variant wins)
67
+ #
68
+ # 2. Variant has only time, event provides date:
69
+ # Variant: 14:00 (3 hours duration)
70
+ # Event: 2024-07-20, User: 2024-06-15
71
+ # → Result: 2024-07-20 14:00 to 2024-07-20 17:00 (event date + variant time)
72
+ #
73
+ # 3. Variant has only time, no event, user provides date:
74
+ # Variant: 14:00 (3 hours duration)
75
+ # No event, User: 2024-06-15
76
+ # → Result: 2024-06-15 14:00 to 2024-06-15 17:00 (user date + variant time)
58
77
  def set_duration
59
- self.from_date ||= variant.start_date_time || event&.from_date
60
- self.to_date ||= variant.end_date_time || event&.to_date
61
-
62
- return if persisted?
63
-
64
- apply_variant_time(:from_date, variant.start_date_time)
65
- apply_variant_time(:to_date, variant.end_date_time)
66
- end
67
-
68
- def apply_variant_time(field, variant_time)
69
- field_value = send(field)
70
-
71
- return unless field_value.present? && field_value.hour.zero? && variant_time.present?
72
-
73
- send("#{field}=", field_value.change(
74
- hour: variant_time.hour,
75
- min: variant_time.min,
76
- sec: variant_time.sec
77
- )
78
- )
78
+ # Prepare fallback dates: event date > user input
79
+ base_start_date = event&.from_date || from_date
80
+ base_end_date = event&.to_date || to_date
81
+
82
+ # Variant will use its own date if configured, otherwise uses base_*_date
83
+ # Variant time is always applied to the final date
84
+ self.from_date = variant.start_date_time(start_date: base_start_date)
85
+ self.to_date = variant.end_date_time(start_date: base_start_date, end_date: base_end_date)
79
86
  end
80
87
  end
81
88
  end
@@ -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
- 'rules' => 'string'
48
+ 'intercity-taxi' => 'string'
50
49
  }.freeze
51
50
 
52
51
  included do
@@ -4,7 +4,7 @@ module SpreeCmCommissioner
4
4
  module OrderStateMachine
5
5
  extend ActiveSupport::Concern
6
6
 
7
- included do
7
+ included do # rubocop:disable Metrics/BlockLength
8
8
  state_machine.before_transition to: :address, do: :hold_blocks
9
9
  state_machine.before_transition to: :payment, do: :ensure_blocks_held
10
10
 
@@ -43,12 +43,14 @@ module SpreeCmCommissioner
43
43
  event :request do
44
44
  transition from: nil, to: :requested
45
45
  end
46
+ after_transition to: :requested, do: :increment_user_request_orders_count
46
47
  after_transition to: :requested, do: :send_order_requested_app_notification_to_user
47
48
  after_transition to: :requested, do: :send_order_requested_telegram_alert_to_store
48
49
 
49
50
  event :accept do
50
51
  transition from: :requested, to: :accepted
51
52
  end
53
+ after_transition from: :requested, to: :accepted, do: :decrement_user_request_orders_count
52
54
  after_transition to: :accepted, do: :send_order_accepted_app_notification_to_user
53
55
  after_transition to: :accepted, do: :send_order_accepted_telegram_alert_to_store
54
56
 
@@ -59,6 +61,8 @@ module SpreeCmCommissioner
59
61
  transition from: :requested, to: :rejected
60
62
  end
61
63
 
64
+ after_transition from: :requested, to: :rejected, do: :decrement_user_request_orders_count
65
+
62
66
  after_transition to: :rejected, do: :send_order_rejected_app_notification_to_user
63
67
  after_transition to: :rejected, do: :send_order_rejected_telegram_alert_to_store
64
68
 
@@ -72,6 +76,18 @@ module SpreeCmCommissioner
72
76
  end
73
77
  end
74
78
 
79
+ def increment_user_request_orders_count
80
+ return if user.nil?
81
+
82
+ SpreeCmCommissioner::UserCountersService.increment_request_orders_count(user)
83
+ end
84
+
85
+ def decrement_user_request_orders_count
86
+ return if user.nil?
87
+
88
+ SpreeCmCommissioner::UserCountersService.decrement_request_orders_count(user)
89
+ end
90
+
75
91
  def product_completion_steps_exist?
76
92
  product_completion_steps.any?
77
93
  end
@@ -174,9 +190,17 @@ module SpreeCmCommissioner
174
190
  line_item.generate_remaining_guests
175
191
 
176
192
  line_item.guests.each do |guest|
177
- guest.user = user if user.present?
178
- guest.generate_bib_if_needed
179
- guest.save!
193
+ result = SpreeCmCommissioner::Guests::Finalize.call(
194
+ guest: guest,
195
+ user: user,
196
+ event: line_item.event,
197
+ variant: line_item.variant
198
+ )
199
+
200
+ unless result.success?
201
+ guest.errors.add(:base, result.error.value || result.error.to_s)
202
+ raise(ActiveRecord::RecordInvalid, guest)
203
+ end
180
204
  end
181
205
  end
182
206
  end
@@ -26,7 +26,6 @@ module SpreeCmCommissioner
26
26
  :bib_prefix,
27
27
  :bib_zerofill,
28
28
  :bib_display_prefix?,
29
- :bib_pre_generation_on_create?,
30
29
  :seat_number_positions,
31
30
  :seat_number_layouts,
32
31
  :color,
@@ -53,9 +52,28 @@ module SpreeCmCommissioner
53
52
  option_values.detect { |o| o.option_type.name.downcase.strip == option_type_name.downcase.strip }.try(:name)
54
53
  end
55
54
 
56
- # No need to fallback to event start date to avoid n+1.
57
- # To get end_time duration of event, include event when needed instead.
58
- def start_date_time
55
+ # Determines the start date/time with priority:
56
+ # - Date priority: variant date > event date > user input date
57
+ # - Time: always uses variant time (if configured)
58
+ #
59
+ # Examples:
60
+ # 1. Variant date takes priority:
61
+ # Variant: 2024-01-15, Event: 2024-02-20, User: 2024-03-10, Variant time: 14:00
62
+ # → Returns "2024-01-15 14:00:00" (variant date + variant time)
63
+ #
64
+ # 2. Event date used when variant has no date:
65
+ # Variant: only time (14:00), Event: 2024-02-20, User: 2024-03-10
66
+ # → Returns "2024-02-20 14:00:00" (event date + variant time)
67
+ #
68
+ # 3. User date used when no variant/event date:
69
+ # Variant: only time (14:00), No event, User: 2024-03-10
70
+ # → Returns "2024-03-10 14:00:00" (user date + variant time)
71
+ #
72
+ # Note: No need to fallback to event start date to avoid n+1.
73
+ # To get start_time duration of event, include event when needed instead.
74
+ def start_date_time(start_date: nil)
75
+ start_date = self.start_date if self.start_date.present?
76
+
59
77
  return nil if start_date.blank? && start_time.blank?
60
78
  return start_date if start_time.blank?
61
79
  return start_time if start_date.blank?
@@ -63,9 +81,29 @@ module SpreeCmCommissioner
63
81
  start_date.change(hour: start_time.hour, min: start_time.min, sec: start_time.sec)
64
82
  end
65
83
 
66
- # No need to fallback to event end date to avoid n+1.
84
+ # Determines the end date/time with priority:
85
+ # - Date priority: variant date > event date > user input date
86
+ # - Time: always uses variant time (if configured) or calculated from duration
87
+ #
88
+ # Examples:
89
+ # 1. Explicit variant end_date:
90
+ # Variant: end_date = 2024-01-20, end_time = 18:00, Event: 2024-02-25
91
+ # → Returns "2024-01-20 18:00:00" (variant date + variant time)
92
+ #
93
+ # 2. Duration-based calculation (no explicit end_date):
94
+ # Variant: start_date = 2024-01-15, start_time = 14:00, duration = 3 hours
95
+ # → Returns "2024-01-15 17:00:00" (start + duration)
96
+ #
97
+ # 3. Event date when variant has no date but has time:
98
+ # Variant: only end_time (18:00), Event: 2024-02-20
99
+ # → Returns "2024-02-20 18:00:00" (event date + variant time)
100
+ #
101
+ # Note: No need to fallback to event end date to avoid n+1.
67
102
  # To get end_time duration of event, include event when needed instead.
68
- def end_date_time
103
+ def end_date_time(start_date: nil, end_date: nil)
104
+ start_date = self.start_date if self.start_date.present?
105
+ end_date = self.end_date(start_date: start_date) if self.end_date(start_date: start_date).present?
106
+
69
107
  return nil if end_date.blank? && end_time.blank?
70
108
  return end_date if end_time.blank?
71
109
  return end_time if end_date.blank?
@@ -73,7 +111,9 @@ module SpreeCmCommissioner
73
111
  end_date.change(hour: end_time.hour, min: end_time.min, sec: end_time.sec)
74
112
  end
75
113
 
76
- def end_date
114
+ def end_date(start_date: nil)
115
+ start_date = self.start_date if self.start_date.present?
116
+
77
117
  return start_date + options.total_duration_in_seconds.seconds if start_date.present? && options.total_duration_in_seconds.positive?
78
118
 
79
119
  options.end_date
@@ -21,8 +21,6 @@ module SpreeCmCommissioner
21
21
  self.uuid = SecureRandom.uuid
22
22
  end
23
23
 
24
- def export_csv
25
- SpreeCmCommissioner::ExportCsvJob.perform_later(export_id: id)
26
- end
24
+ def export_csv; end
27
25
  end
28
26
  end
@@ -62,7 +62,6 @@ module SpreeCmCommissioner
62
62
  before_validation :assign_seat_number_with_block, if: -> { will_save_change_to_block_id? }
63
63
 
64
64
  before_save :preload_eligible_check_in_session_ids, if: -> { new_record? }
65
- before_create :generate_bib_if_needed, if: -> { line_item.reload && variant.bib_pre_generation_on_create? }
66
65
  after_create :preload_order_block_ids, if: -> { block_id.present? }
67
66
  after_update :preload_order_block_ids, if: :saved_change_to_block_id?
68
67
  before_destroy :cancel_reserved_block!, if: -> { reserved_block.present? }
@@ -248,26 +247,6 @@ module SpreeCmCommissioner
248
247
  line_item.variant.bib_display_prefix?
249
248
  end
250
249
 
251
- def generate_bib_if_needed
252
- return if bib_prefix.present?
253
- return unless bib_required?
254
- return if event_id.blank?
255
-
256
- self.bib_prefix = line_item.variant.bib_prefix
257
-
258
- last_bib_number = event.guests
259
- .where(bib_prefix: bib_prefix)
260
- .maximum(:bib_number) || 0
261
-
262
- self.bib_number = last_bib_number + 1
263
- self.bib_index = "#{event_id}-#{bib_prefix}-#{bib_number}"
264
- end
265
-
266
- def generate_bib!
267
- generate_bib_if_needed
268
- save!
269
- end
270
-
271
250
  # bib_number: 345, bib_prefix: 5KM, bib_zerofill: 5 => return 5KM00345
272
251
  # bib_number: 345, bib_prefix: 5KM, bib_zerofill: 2 => return 5KM345
273
252
  def formatted_bib_number
@@ -26,5 +26,33 @@ module SpreeCmCommissioner
26
26
 
27
27
  belongs_to :recipient, polymorphic: true
28
28
  belongs_to :notificable, polymorphic: true
29
+
30
+ after_create :increment_user_counters
31
+ after_update :decrement_user_counters, if: :saved_change_to_read_at?
32
+ after_destroy :decrement_user_counters
33
+
34
+ private
35
+
36
+ def increment_user_counters
37
+ return unless recipient.is_a?(Spree::User)
38
+
39
+ return unless unread?
40
+
41
+ SpreeCmCommissioner::UserCountersService.increment_unread_notifications_count(recipient)
42
+ end
43
+
44
+ def decrement_user_counters
45
+ return unless recipient.is_a?(Spree::User)
46
+
47
+ # On update: decrement only if transitioning from unread (nil) to read (present)
48
+ if saved_change_to_read_at
49
+ return unless saved_change_to_read_at[0].nil? && saved_change_to_read_at[1].present?
50
+ # On destroy: decrement only if currently unread
51
+ else
52
+ return unless read_at.nil?
53
+ end
54
+
55
+ SpreeCmCommissioner::UserCountersService.decrement_unread_notifications_count(recipient)
56
+ end
29
57
  end
30
58
  end
@@ -136,7 +136,7 @@ module SpreeCmCommissioner
136
136
  def sync_event_dates_to_line_items
137
137
  return unless event? && depth == 1
138
138
 
139
- ::SpreeCmCommissioner::EventLineItemsDateSyncerJob.perform_later(event_id: id)
139
+ ::SpreeCmCommissioner::LineItems::SyncEventDateJob.perform_later(event_id: id)
140
140
  end
141
141
 
142
142
  private
@@ -126,11 +126,6 @@ module SpreeCmCommissioner
126
126
  @bib_display_prefix == 1
127
127
  end
128
128
 
129
- def bib_pre_generation_on_create?
130
- @bib_pre_generation_on_create ||= option_value_name_for(option_type_name: 'bib-pre-generation-on-create')&.to_i || 0
131
- @bib_pre_generation_on_create == 1
132
- end
133
-
134
129
  def seat_number_positions
135
130
  @seat_number_positions ||= option_value_name_for(option_type_name: 'seat-number-positions')&.split(',')
136
131
  end
@@ -6,7 +6,8 @@ module Spree
6
6
  base.attributes :first_name, :last_name, :gender, :phone_number, :intel_phone_number,
7
7
  :country_code, :otp_enabled, :otp_email, :otp_phone_number,
8
8
  :confirm_pin_code_enabled, :tenant_id, :has_incomplete_guest_info,
9
- :login, :qr_data, :qr_data_version, :qr_data_invalidated_at
9
+ :login, :qr_data, :qr_data_version, :qr_data_invalidated_at,
10
+ :unread_notifications_count, :request_orders_count
10
11
 
11
12
  base.has_one :profile, serializer: ::Spree::V2::Storefront::UserProfileSerializer
12
13
  base.has_many :device_tokens, serializer: Spree::V2::Storefront::UserDeviceTokenSerializer
@@ -2,16 +2,10 @@ module SpreeCmCommissioner
2
2
  module V2
3
3
  module Storefront
4
4
  class HomepageDataSerializer < BaseSerializer
5
- set_type :homepage_data
5
+ attributes :menu_id, :homepage_background_id
6
6
 
7
- has_many :homepage_backgrounds, serializer: :homepage_background
8
- has_many :homepage_banners, serializer: :homepage_banner
9
- has_many :featured_vendors, serializer: ::Spree::V2::Storefront::AccommodationSerializer
10
-
11
- # has_many :trending_categories, serializer: :category_taxon
12
- # has_many :top_categories, serializer: :category_taxon
13
- # has_many :display_products, serializer: :taxon_include_product
14
- # has_many :featured_brands, serializer: :brand_taxon
7
+ has_one :menu, serializer: Spree::Api::Dependencies.storefront_menu_serializer.constantize
8
+ has_one :homepage_background, serializer: SpreeCmCommissioner::V2::Storefront::HomepageBackgroundSerializer
15
9
  end
16
10
  end
17
11
  end
@@ -0,0 +1,76 @@
1
+ module SpreeCmCommissioner
2
+ module Guests
3
+ class Finalize
4
+ prepend ::Spree::ServiceModule::Base
5
+
6
+ MAX_RETRY_ATTEMPTS = 5
7
+
8
+ def call(guest:, variant:, event: nil, user: nil) # rubocop:disable Metrics/MethodLength,Metrics/PerceivedComplexity
9
+ attempts = 0
10
+
11
+ begin
12
+ attempts += 1
13
+
14
+ guest.user = user if user.present?
15
+ assign_bib(guest: guest, event: event, variant: variant)
16
+ guest.save!
17
+
18
+ success(guest)
19
+ rescue ActiveRecord::RecordInvalid => e
20
+ # Handle Rails-level validation error
21
+ if e.record.errors[:bib_index].any? && attempts < MAX_RETRY_ATTEMPTS
22
+ # Reload guest to get fresh data and retry
23
+ if guest.persisted?
24
+ guest.reload
25
+ else
26
+ # Clear bib values for new records so they get recalculated on retry
27
+ guest.bib_prefix = nil
28
+ guest.bib_number = nil
29
+ guest.bib_index = nil
30
+ end
31
+ retry
32
+ else
33
+ # Return failure if not a bib_index error or exceeded max attempts
34
+ failure(nil, e.message)
35
+ end
36
+ rescue ActiveRecord::RecordNotUnique => e
37
+ # Handle database-level uniqueness constraint violation
38
+ # Return failure if not a bib_index error or exceeded max attempts
39
+ return failure(nil, e.message) unless e.message.include?('bib_index') && attempts < MAX_RETRY_ATTEMPTS
40
+
41
+ # Reload guest to get fresh data and retry
42
+ if guest.persisted?
43
+ guest.reload
44
+ else
45
+ # Clear bib values for new records so they get recalculated on retry
46
+ guest.bib_prefix = nil
47
+ guest.bib_number = nil
48
+ guest.bib_index = nil
49
+ end
50
+ retry
51
+ end
52
+ end
53
+
54
+ def assign_bib(guest:, event:, variant:)
55
+ bib_prefix = variant.bib_prefix
56
+
57
+ return false unless variant.bib_required?
58
+
59
+ return false if guest.bib_prefix.present?
60
+ return false if guest.event_id.blank?
61
+ return false if event.nil? || guest.event_id != event.id
62
+
63
+ guest.bib_prefix = bib_prefix
64
+
65
+ last_bib_number = event.guests
66
+ .where(bib_prefix: bib_prefix)
67
+ .maximum(:bib_number) || 0
68
+
69
+ guest.bib_number = last_bib_number + 1
70
+ guest.bib_index = "#{event.id}-#{bib_prefix}-#{guest.bib_number}"
71
+
72
+ true
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,23 @@
1
+ module SpreeCmCommissioner
2
+ class HomepageData
3
+ attr_reader :menu, :homepage_background
4
+
5
+ # Plain Old Ruby Object (PORO) for homepage data
6
+ def initialize(menu:, homepage_background:)
7
+ @menu = menu
8
+ @homepage_background = homepage_background
9
+ end
10
+
11
+ def id
12
+ @id ||= SecureRandom.uuid
13
+ end
14
+
15
+ def menu_id
16
+ menu&.id
17
+ end
18
+
19
+ def homepage_background_id
20
+ homepage_background&.id
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module SpreeCmCommissioner
2
+ module LineItems
3
+ class SyncEventDate
4
+ prepend ::Spree::ServiceModule::Base
5
+
6
+ def call(event:)
7
+ return failure(:invalid_event_kind) unless event.event?
8
+ return failure(:invalid_event_depth) unless event.depth == 1
9
+
10
+ event.event_line_items.includes(:variant).find_each do |line_item|
11
+ line_item.send(:set_duration)
12
+
13
+ # Update could be failed here if case line item does not allowed to change or no longer available.
14
+ # We can ignore this line item in this case.
15
+ line_item.save if line_item.from_date_changed? || line_item.to_date_changed?
16
+ end
17
+
18
+ success(nil)
19
+ end
20
+ end
21
+ end
22
+ end