spree_cm_commissioner 2.5.2.pre.pre2 → 2.5.2.pre.pre3

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 (60) 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/concerns/spree/api/v2/product_list_includes_decorator.rb +37 -0
  5. data/app/controllers/spree/api/v2/storefront/active_homepage_events_controller.rb +6 -1
  6. data/app/controllers/spree/api/v2/storefront/homepage_sections_controller.rb +41 -4
  7. data/app/controllers/spree/api/v2/storefront/invite_guests_controller.rb +11 -2
  8. data/app/controllers/spree/api/v2/storefront/products_controller_decorator.rb +27 -0
  9. data/app/controllers/spree/api/v2/storefront/taxons_controller_decorator.rb +92 -0
  10. data/app/controllers/spree_cm_commissioner/application_controller_decorator.rb +0 -2
  11. data/app/jobs/spree_cm_commissioner/line_items/sync_event_date_job.rb +10 -0
  12. data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +30 -23
  13. data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +47 -6
  14. data/app/models/spree/stock/quantifier_decorator.rb +26 -0
  15. data/app/models/spree_cm_commissioner/export.rb +1 -3
  16. data/app/models/spree_cm_commissioner/guest.rb +3 -3
  17. data/app/models/spree_cm_commissioner/taxon_decorator.rb +27 -1
  18. data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +36 -7
  19. data/app/services/spree_cm_commissioner/line_items/sync_event_date.rb +22 -0
  20. data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +14 -19
  21. data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +2 -4
  22. data/app/services/spree_cm_commissioner/trips/variants/create.rb +6 -6
  23. data/config/routes.rb +0 -27
  24. data/lib/spree_cm_commissioner/transit/route_stop_collection.rb +0 -28
  25. data/lib/spree_cm_commissioner/transit/trip_form.rb +0 -24
  26. data/lib/spree_cm_commissioner/transit/trip_stop_form.rb +1 -5
  27. data/lib/spree_cm_commissioner/version.rb +1 -1
  28. data/lib/spree_cm_commissioner.rb +0 -1
  29. metadata +8 -33
  30. data/app/controllers/concerns/spree_cm_commissioner/events/role_authorization.rb +0 -36
  31. data/app/controllers/spree/events/base_controller.rb +0 -35
  32. data/app/controllers/spree/events/check_ins_controller.rb +0 -23
  33. data/app/controllers/spree/events/data_exports_controller.rb +0 -41
  34. data/app/controllers/spree/events/errors_controller.rb +0 -19
  35. data/app/controllers/spree/events/guests_controller.rb +0 -139
  36. data/app/controllers/spree/events/state_changes_controller.rb +0 -11
  37. data/app/errors/spree_cm_commissioner/unauthorized_event_error.rb +0 -4
  38. data/app/interactors/spree_cm_commissioner/event_line_items_date_syncer.rb +0 -19
  39. data/app/jobs/spree_cm_commissioner/event_line_items_date_syncer_job.rb +0 -8
  40. data/app/jobs/spree_cm_commissioner/export_csv_job.rb +0 -7
  41. data/app/models/spree_cm_commissioner/exports/export_guest_csv.rb +0 -33
  42. data/app/services/spree_cm_commissioner/exports/export_guest_csv_service.rb +0 -48
  43. data/app/views/spree/events/check_ins/index.html.erb +0 -59
  44. data/app/views/spree/events/data_exports/index.html.erb +0 -54
  45. data/app/views/spree/events/errors/_error.html.erb +0 -7
  46. data/app/views/spree/events/errors/forbidden.html.erb +0 -1
  47. data/app/views/spree/events/errors/resource_not_found.html.erb +0 -1
  48. data/app/views/spree/events/guests/_check_in_status.html.erb +0 -44
  49. data/app/views/spree/events/guests/_form.html.erb +0 -37
  50. data/app/views/spree/events/guests/_swap_guest_bib_number.html.erb +0 -18
  51. data/app/views/spree/events/guests/edit.html.erb +0 -86
  52. data/app/views/spree/events/guests/index.html.erb +0 -102
  53. data/app/views/spree/events/shared/_event_switcher.html.erb +0 -39
  54. data/app/views/spree/events/shared/_filters.html.erb +0 -50
  55. data/app/views/spree/events/shared/_guest_tabs.html.erb +0 -19
  56. data/app/views/spree/events/shared/_header.html.erb +0 -10
  57. data/app/views/spree/events/shared/_main_menu.html.erb +0 -22
  58. data/app/views/spree/events/state_changes/index.html.erb +0 -48
  59. data/lib/spree_cm_commissioner/test_helper/factories/export_guest_csv_factory.rb +0 -9
  60. 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: aa0e45576d6b86ad5c668c0ffe85f437e4e0e0ee01dcab1bef8c3cb4628950f9
4
+ data.tar.gz: b0d0683d2b81ede7e5200c270ac8d0b52d9c48dcb7e3223c94cf9f944bafe864
5
5
  SHA512:
6
- metadata.gz: b3590bf3744645b2af02f779ccf4d6797879e71a47c9c01e524514428396813cc6752f25e361cf1489e0f1a2f106ba9987f43581d97788ce19ed290848a7fc68
7
- data.tar.gz: 6d0b09ce6143a18db3118c284ae0c1b334bbd29baf9dcf106ec4253144afbdb5da6e78f952219821ccc782601eee2ac20f7f1ccf7f22148290ac9ae0f1a3a012
6
+ metadata.gz: 6304eee33f234cd8bcd652faefe614e48060b4bb0b0d61a7e5343935d9228f5fd66fcf46a6a24877cac346c161f5863f4bc84f3fe0021715d668f5021e64343c
7
+ data.tar.gz: aaeb3660e2c2cd02e453e4fc83daae2be76cb7611c12ee2bc2f15de05a0172258a2bc336e05366544060611a827d04c19fa4726fe41c8b3ae5606909c7488eb9
@@ -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.pre.pre3)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -0,0 +1,37 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module ProductListIncludesDecorator
5
+ # OVERRIDE: Extends base product_list_includes from Spree::Api::V2::ProductListIncludes
6
+ def product_list_includes
7
+ # Gets base from ProductListIncludes concern
8
+ base_includes = super
9
+
10
+ # Preload taxons with their taxonomies
11
+ base_includes[:classifications] = { taxon: :taxonomy }
12
+
13
+ # Add missing associations
14
+ base_includes[:vendor] = [:default_state]
15
+ base_includes[:venue] = []
16
+ base_includes[:variants_including_master] = { prices: [] }
17
+ base_includes[:product_option_types] = :option_type
18
+ base_includes[:product_promotion_rules] = {
19
+ promotion_rule: {
20
+ promotion: []
21
+ }
22
+ }
23
+ base_includes[:product_dynamic_fields] = :dynamic_field
24
+
25
+ %i[master variants variants_including_master].each do |key|
26
+ base_includes[key][:stock_items] = :stock_location if base_includes[key]
27
+ base_includes[key][:stock_locations] = []
28
+ end
29
+
30
+ base_includes
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ Spree::Api::V2::ProductListIncludes.prepend Spree::Api::V2::ProductListIncludesDecorator
@@ -6,7 +6,12 @@ module Spree
6
6
  private
7
7
 
8
8
  def collection
9
- @collection ||= model_class.active_homepage_events
9
+ @collection ||= model_class.active_homepage_events.includes(category_icon: { attachment_attachment: :blob })
10
+ end
11
+
12
+ def serialize_collection(collection)
13
+ model_class.preload_ancestors_for(collection)
14
+ super(collection)
10
15
  end
11
16
 
12
17
  def model_class
@@ -16,10 +16,47 @@ module Spree
16
16
  end
17
17
 
18
18
  def collection
19
- @collection ||= model_class.filter_by_segment(params[:homepage_id] || :general)
20
- .active
21
- .where(tenant_id: nil)
22
- .order(position: :asc)
19
+ @collection = model_class.filter_by_segment(params[:homepage_id] || :general)
20
+ .active
21
+ .where(tenant_id: nil)
22
+ .order(position: :asc)
23
+ .includes(preload_associations)
24
+ end
25
+
26
+ def preload_associations
27
+ {
28
+ homepage_section_relatables: {
29
+ relatable: [
30
+ :icon,
31
+ {
32
+ home_banner: { attachment_attachment: :blob }
33
+ },
34
+ {
35
+ category_icon: { attachment_attachment: :blob }
36
+ },
37
+ {
38
+ video_banner: { attachment_attachment: :blob }
39
+ },
40
+ {
41
+ app_banner: { attachment_attachment: :blob }
42
+ },
43
+ {
44
+ web_banner: { attachment_attachment: :blob }
45
+ },
46
+ {
47
+ children: [
48
+ :icon,
49
+ {
50
+ vendors: [
51
+ { photos: { attachment_attachment: :blob } },
52
+ :default_state
53
+ ]
54
+ }
55
+ ]
56
+ }
57
+ ]
58
+ }
59
+ }
23
60
  end
24
61
  end
25
62
  end
@@ -42,8 +42,17 @@ module Spree
42
42
  end
43
43
 
44
44
  def load_invite_guest_by_token
45
- @invite_guest = SpreeCmCommissioner::InviteGuest.find_by!(token: params[:id])
46
- rescue ActiveRecord::RecordNotFound
45
+ @invite_guest = SpreeCmCommissioner::InviteGuest.includes(
46
+ order: {
47
+ line_items: {
48
+ guests: %i[
49
+ guest_dynamic_fields
50
+ id_card
51
+ check_in
52
+ ]
53
+ }
54
+ }
55
+ ).find_by(token: params[:id])
47
56
  render_error_payload(I18n.t('invite.url_not_found'))
48
57
  end
49
58
 
@@ -0,0 +1,27 @@
1
+ # gems/spree_cm_commissioner/app/controllers/spree/api/v2/storefront/products_controller_decorator.rb
2
+ module Spree
3
+ module Api
4
+ module V2
5
+ module Storefront
6
+ module ProductsControllerDecorator
7
+ def scope_includes
8
+ base_includes = super
9
+
10
+ base_includes[:vendor] = { default_state: [] }
11
+ base_includes[:venue] = []
12
+ base_includes[:variants_including_master] = { prices: [] }
13
+
14
+ %i[master variants variants_including_master].each do |key|
15
+ base_includes[key][:stock_items] = :stock_location if base_includes[key]
16
+ end
17
+
18
+ base_includes
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ Spree::Api::V2::Storefront::ProductsController
27
+ .prepend Spree::Api::V2::Storefront::ProductsControllerDecorator
@@ -0,0 +1,92 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module TaxonsControllerDecorator
6
+ def resource
7
+ @resource ||= begin
8
+ taxon = scope.find_by(permalink: params[:id]) || scope.find(params[:id])
9
+
10
+ if action_name == 'show' && should_include_products?
11
+ taxon.products = taxon.products
12
+ .includes(product_includes_for_show)
13
+ .order('spree_products_taxons.position ASC')
14
+ end
15
+
16
+ if action_name == 'show' && should_include_visible_products?
17
+ taxon.visible_products = taxon.visible_products
18
+ .includes(product_includes_for_show)
19
+ .order('spree_products_taxons.position ASC')
20
+ end
21
+
22
+ taxon
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def scope(skip_cancancan: false)
29
+ base_scope = super(skip_cancancan: skip_cancancan)
30
+ base_scope = base_scope.includes(asset_includes) if action_name == 'show'
31
+ base_scope
32
+ end
33
+
34
+ private
35
+
36
+ def should_include_products?
37
+ params[:include]&.include?('products')
38
+ end
39
+
40
+ def should_include_visible_products?
41
+ params[:include]&.include?('visible_products')
42
+ end
43
+
44
+ def product_includes_for_show
45
+ {
46
+ product_properties: [],
47
+ option_types: [],
48
+ variant_images: [],
49
+ classifications: :taxon,
50
+ variants_including_master: product_variant_includes,
51
+ master: product_variant_includes,
52
+ variants: product_variant_includes,
53
+ vendor: { default_state: [] },
54
+ venue: []
55
+ }
56
+ end
57
+
58
+ def product_variant_includes
59
+ {
60
+ prices: [],
61
+ option_values: :option_type,
62
+ images: [],
63
+ stock_items: :stock_location
64
+ }
65
+ end
66
+
67
+ def asset_includes
68
+ {
69
+ category_icon: { attachment_attachment: :blob },
70
+ app_banner: { attachment_attachment: :blob },
71
+ web_banner: { attachment_attachment: :blob },
72
+ home_banner: { attachment_attachment: :blob },
73
+ video_banner: { attachment_attachment: :blob },
74
+ children: {
75
+ icon: { attachment_attachment: :blob },
76
+ category_icon: { attachment_attachment: :blob },
77
+ app_banner: { attachment_attachment: :blob },
78
+ web_banner: { attachment_attachment: :blob },
79
+ home_banner: { attachment_attachment: :blob },
80
+ video_banner: { attachment_attachment: :blob }
81
+ }
82
+ }
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ Spree::Api::V2::Storefront::TaxonsController.prepend(
91
+ Spree::Api::V2::Storefront::TaxonsControllerDecorator
92
+ )
@@ -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
@@ -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
@@ -53,9 +53,28 @@ module SpreeCmCommissioner
53
53
  option_values.detect { |o| o.option_type.name.downcase.strip == option_type_name.downcase.strip }.try(:name)
54
54
  end
55
55
 
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
56
+ # Determines the start date/time with priority:
57
+ # - Date priority: variant date > event date > user input date
58
+ # - Time: always uses variant time (if configured)
59
+ #
60
+ # Examples:
61
+ # 1. Variant date takes priority:
62
+ # Variant: 2024-01-15, Event: 2024-02-20, User: 2024-03-10, Variant time: 14:00
63
+ # → Returns "2024-01-15 14:00:00" (variant date + variant time)
64
+ #
65
+ # 2. Event date used when variant has no date:
66
+ # Variant: only time (14:00), Event: 2024-02-20, User: 2024-03-10
67
+ # → Returns "2024-02-20 14:00:00" (event date + variant time)
68
+ #
69
+ # 3. User date used when no variant/event date:
70
+ # Variant: only time (14:00), No event, User: 2024-03-10
71
+ # → Returns "2024-03-10 14:00:00" (user date + variant time)
72
+ #
73
+ # Note: No need to fallback to event start date to avoid n+1.
74
+ # To get start_time duration of event, include event when needed instead.
75
+ def start_date_time(start_date: nil)
76
+ start_date = self.start_date if self.start_date.present?
77
+
59
78
  return nil if start_date.blank? && start_time.blank?
60
79
  return start_date if start_time.blank?
61
80
  return start_time if start_date.blank?
@@ -63,9 +82,29 @@ module SpreeCmCommissioner
63
82
  start_date.change(hour: start_time.hour, min: start_time.min, sec: start_time.sec)
64
83
  end
65
84
 
66
- # No need to fallback to event end date to avoid n+1.
85
+ # Determines the end date/time with priority:
86
+ # - Date priority: variant date > event date > user input date
87
+ # - Time: always uses variant time (if configured) or calculated from duration
88
+ #
89
+ # Examples:
90
+ # 1. Explicit variant end_date:
91
+ # Variant: end_date = 2024-01-20, end_time = 18:00, Event: 2024-02-25
92
+ # → Returns "2024-01-20 18:00:00" (variant date + variant time)
93
+ #
94
+ # 2. Duration-based calculation (no explicit end_date):
95
+ # Variant: start_date = 2024-01-15, start_time = 14:00, duration = 3 hours
96
+ # → Returns "2024-01-15 17:00:00" (start + duration)
97
+ #
98
+ # 3. Event date when variant has no date but has time:
99
+ # Variant: only end_time (18:00), Event: 2024-02-20
100
+ # → Returns "2024-02-20 18:00:00" (event date + variant time)
101
+ #
102
+ # Note: No need to fallback to event end date to avoid n+1.
67
103
  # To get end_time duration of event, include event when needed instead.
68
- def end_date_time
104
+ def end_date_time(start_date: nil, end_date: nil)
105
+ start_date = self.start_date if self.start_date.present?
106
+ end_date = self.end_date(start_date: start_date) if self.end_date(start_date: start_date).present?
107
+
69
108
  return nil if end_date.blank? && end_time.blank?
70
109
  return end_date if end_time.blank?
71
110
  return end_time if end_date.blank?
@@ -73,7 +112,9 @@ module SpreeCmCommissioner
73
112
  end_date.change(hour: end_time.hour, min: end_time.min, sec: end_time.sec)
74
113
  end
75
114
 
76
- def end_date
115
+ def end_date(start_date: nil)
116
+ start_date = self.start_date if self.start_date.present?
117
+
77
118
  return start_date + options.total_duration_in_seconds.seconds if start_date.present? && options.total_duration_in_seconds.positive?
78
119
 
79
120
  options.end_date
@@ -0,0 +1,26 @@
1
+ module Spree
2
+ module Stock
3
+ module QuantifierDecorator
4
+ private
5
+
6
+ def scope_to_location(collection)
7
+ # If stock_items are already loaded (preloaded via includes), filter in memory
8
+ if collection.loaded?
9
+ if stock_location.blank?
10
+ # Filter to only active stock locations in memory
11
+ collection.select { |si| si.stock_location&.active? }
12
+ else
13
+ collection.select { |si| si.stock_location_id == stock_location.id }
14
+ end
15
+ else
16
+ # Fall back to original behavior if not preloaded
17
+ return collection.with_active_stock_location if stock_location.blank?
18
+
19
+ collection.where(stock_location: stock_location)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ Spree::Stock::Quantifier.prepend Spree::Stock::QuantifierDecorator
@@ -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
@@ -446,15 +446,15 @@ module SpreeCmCommissioner
446
446
  end
447
447
 
448
448
  def pre_registration_fields?
449
- line_item&.product&.dynamic_fields&.pre_registration&.any?
449
+ line_item&.product&.dynamic_fields&.select(&:pre_registration?)&.any?
450
450
  end
451
451
 
452
452
  def post_registration_fields?
453
- line_item&.product&.dynamic_fields&.post_registration&.any?
453
+ line_item&.product&.dynamic_fields&.select(&:post_registration?)&.any?
454
454
  end
455
455
 
456
456
  def during_check_in_fields?
457
- line_item&.product&.dynamic_fields&.during_check_in&.any?
457
+ line_item&.product&.dynamic_fields&.select(&:during_check_in?)&.any?
458
458
  end
459
459
 
460
460
  def product_dynamic_fields
@@ -94,6 +94,32 @@ module SpreeCmCommissioner
94
94
  def base.find_event(id)
95
95
  find_by(slug: "events-#{id}")
96
96
  end
97
+
98
+ def base.preload_ancestors_for(taxons)
99
+ return taxons if taxons.empty?
100
+
101
+ # Build a single query to fetch all ancestors for all taxons
102
+ conditions = taxons.map do |t|
103
+ sanitize_sql_array(['(lft <= ? AND rgt >= ?)', t.lft, t.rgt])
104
+ end.join(' OR ')
105
+ all_ancestors = where(conditions).distinct.order(:lft).to_a
106
+
107
+ # Store preloaded ancestors for each taxon
108
+ taxons.each do |t|
109
+ taxon_ancestors = all_ancestors.select { |a| a.lft <= t.lft && a.rgt >= t.rgt && a.id != t.id }
110
+ t.instance_variable_set(:@preloaded_ancestors, taxon_ancestors)
111
+ end
112
+
113
+ taxons
114
+ end
115
+ end
116
+
117
+ def pretty_name
118
+ ancestor_chain = @preloaded_ancestors || ancestors
119
+ ancestor_chain = ancestor_chain.inject('') do |chain, ancestor|
120
+ chain + "#{ancestor.name} -> "
121
+ end
122
+ ancestor_chain + name.to_s
97
123
  end
98
124
 
99
125
  def set_kind
@@ -136,7 +162,7 @@ module SpreeCmCommissioner
136
162
  def sync_event_dates_to_line_items
137
163
  return unless event? && depth == 1
138
164
 
139
- ::SpreeCmCommissioner::EventLineItemsDateSyncerJob.perform_later(event_id: id)
165
+ ::SpreeCmCommissioner::LineItems::SyncEventDateJob.perform_later(event_id: id)
140
166
  end
141
167
 
142
168
  private
@@ -2,24 +2,52 @@ module Spree
2
2
  module V2
3
3
  module Storefront
4
4
  module ProductSerializerDecorator
5
- def self.prepended(base) # rubocop:disable Metrics/MethodLength
5
+ def self.prepended(base)
6
+ add_option_type_associations(base)
7
+ add_promotion_associations(base)
8
+ add_dynamic_field_associations(base)
9
+ add_taxon_associations(base)
10
+ add_basic_attributes(base)
11
+ add_custom_attributes(base)
12
+ add_cache_and_action_button(base)
13
+ end
14
+
15
+ def self.add_option_type_associations(base)
6
16
  base.has_many :variant_kind_option_types, serializer: :option_type
7
17
  base.has_many :product_kind_option_types, serializer: :option_type
8
18
  base.has_many :promoted_option_types, serializer: :option_type
19
+ end
20
+
21
+ def self.add_promotion_associations(base)
9
22
  base.has_many :possible_promotions, serializer: ::SpreeCmCommissioner::V2::Storefront::PromotionSerializer
23
+ end
24
+
25
+ def self.add_dynamic_field_associations(base)
10
26
  base.has_many :dynamic_fields, serializer: SpreeCmCommissioner::V2::Storefront::DynamicFieldSerializer
27
+ end
11
28
 
29
+ def self.add_taxon_associations(base)
30
+ base.has_many :taxons, serializer: :taxon, record_type: :taxon do |object, params|
31
+ filter_store_taxons(object, params)
32
+ end
33
+ end
34
+
35
+ def self.filter_store_taxons(object, params)
36
+ object.classifications
37
+ .map(&:taxon)
38
+ .select { |taxon| taxon.taxonomy&.store_id == params[:store]&.id }
39
+ .sort_by(&:id)
40
+ end
41
+
42
+ def self.add_basic_attributes(base)
12
43
  base.has_one :default_state, serializer: :state
13
44
  base.has_one :venue, serializer: ::SpreeCmCommissioner::V2::Storefront::ProductPlaceSerializer
14
-
15
45
  base.attributes :need_confirmation, :product_type, :kyc, :kyc_fields, :allowed_upload_later, :allow_anonymous_booking, :use_video_as_default
16
46
  base.attributes :reveal_description, :discontinue_on, :public_metadata, :purchasable_on
17
-
18
- # Expose only the `event_id` here instead of the full event object.
19
- # This lets the client fetch event details separately (usually already cached),
20
- # avoids extra DB queries from a `has_one :event` association, and keeps this response lightweight.
21
47
  base.attributes :event_id
48
+ end
22
49
 
50
+ def self.add_custom_attributes(base)
23
51
  base.attribute :purchasable_on_app do |product|
24
52
  product.purchasable_on == 'app' || product.purchasable_on == 'both'
25
53
  end
@@ -47,9 +75,10 @@ module Spree
47
75
  value = product.available?
48
76
  [true, false].include?(value) ? value : nil
49
77
  end
78
+ end
50
79
 
80
+ def self.add_cache_and_action_button(base)
51
81
  base.cache_options store: nil
52
-
53
82
  base.attribute :action_button, &:action_button
54
83
  end
55
84
  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