spree_cm_commissioner 1.21.0 → 2.0.0.pre.pre1

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -5
  3. data/app/assets/images/mailer/mail.png +0 -0
  4. data/app/assets/images/mailer/tenant_phone.png +0 -0
  5. data/app/assets/images/mailer/tenant_user.png +0 -0
  6. data/app/controllers/spree/api/v2/operator/pie_chart_events_controller.rb +46 -0
  7. data/app/controllers/spree/api/v2/storefront/guests_controller.rb +13 -13
  8. data/app/factory/spree_cm_commissioner/invite_guest_claimed_telegram_message_factory.rb +28 -38
  9. data/app/interactors/spree_cm_commissioner/create_ticket.rb +6 -5
  10. data/app/interactors/spree_cm_commissioner/create_vendor.rb +44 -0
  11. data/app/mailers/spree/order_mailer_decorator.rb +18 -3
  12. data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +18 -0
  13. data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +1 -0
  14. data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +4 -2
  15. data/app/models/spree_cm_commissioner/chart_type.rb +10 -0
  16. data/app/models/spree_cm_commissioner/dynamic_field.rb +20 -0
  17. data/app/models/spree_cm_commissioner/guest.rb +56 -0
  18. data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +41 -1
  19. data/app/models/spree_cm_commissioner/product_decorator.rb +22 -0
  20. data/app/models/spree_cm_commissioner/user_decorator.rb +6 -0
  21. data/app/models/spree_cm_commissioner/vendor_decorator.rb +3 -0
  22. data/app/queries/spree_cm_commissioner/pie_chart_event_queries.rb +179 -0
  23. data/app/serializables/spree_cm_commissioner/pie_chart_event.rb +11 -0
  24. data/app/serializers/spree_cm_commissioner/v2/operator/chart_type_serializer.rb +10 -0
  25. data/app/serializers/spree_cm_commissioner/v2/storefront/dynamic_field_serializer.rb +1 -1
  26. data/app/views/spree/admin/tenant_vendors/index.html.erb +9 -2
  27. data/app/views/spree/admin/tenants/_form.html.erb +9 -0
  28. data/app/views/spree/admin/tenants/edit.html.erb +2 -1
  29. data/app/views/spree/admin/tenants/index.html.erb +7 -1
  30. data/app/views/spree/admin/vendors/_form.html.erb +14 -0
  31. data/app/views/spree/order_mailer/confirm_email.html.erb +27 -16
  32. data/app/views/spree_cm_commissioner/layouts/order_mailer.html.erb +5 -1
  33. data/app/views/spree_cm_commissioner/order_mailer/_mailer_stylesheets.html.erb +41 -4
  34. data/app/views/spree_cm_commissioner/order_mailer/tenant/_customer_info.html.erb +42 -0
  35. data/app/views/spree_cm_commissioner/order_mailer/tenant/_footer.html.erb +25 -0
  36. data/app/views/spree_cm_commissioner/order_mailer/tenant/_greeting.html.erb +19 -0
  37. data/app/views/spree_cm_commissioner/order_mailer/tenant/_support_contact.html.erb +33 -0
  38. data/config/locales/en.yml +5 -1
  39. data/config/routes.rb +1 -0
  40. data/db/migrate/20250709073455_add_email_fields_to_spree_vendors.rb +6 -0
  41. data/db/migrate/20250718071620_add_data_fill_stage_to_dynamic_fields.rb +5 -0
  42. data/lib/spree_cm_commissioner/version.rb +1 -1
  43. metadata +19 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7043a8acbe6da011c50dcf6d5f9c4fa4c09d44f0c273c7d8c5f35e9da6430628
4
- data.tar.gz: '049dc2649fbb41b2e43945741baf9c7815ddc11b38a83cec8157c2eac5ed8431'
3
+ metadata.gz: b85b4b7a446688bc4aed2974e7150a9155a2d919b998188fc7e575ccea0d2714
4
+ data.tar.gz: 134a3e165301c382a7e48016e768755420d32003a31cf3b3dac79f33b7a0d5a6
5
5
  SHA512:
6
- metadata.gz: 17233d10bfd72385a100faf262a0c236017970a4429e33098a7992dd64c217ff02c06168931f3472ef8e5a3245786df65c72f0123f2cacca58223b66c5ca6a3f
7
- data.tar.gz: 68bac4de8bf2b866b75ccbcfef5c53b50786427d7cbe87e4d17fa0b284186cfbd08d71fbe8c5fe496dfce6b1e6340a6bb0be4cbac00103388c7545397ad417d1
6
+ metadata.gz: fd91cb46cb05faec83d0978df8312e272265ad03d1d14c1319ea0303081521dac9468a4e9e6fd4e72698e61c175fcddfc3bed53527e7f0ebe1a23f81062bf9c3
7
+ data.tar.gz: 1d04b75c972c6cff71fa6e95ca3882411a8a54b6b0b226183369a97b1360fa00c2c334e728203f647405313da8deec7298e4662dedfb068cc51540e4caf548d7
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (1.21.0)
37
+ spree_cm_commissioner (2.0.0.pre.pre1)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -572,10 +572,6 @@ GEM
572
572
  nokogiri (1.15.4)
573
573
  mini_portile2 (~> 2.8.2)
574
574
  racc (~> 1.4)
575
- nokogiri (1.15.4-x86_64-darwin)
576
- racc (~> 1.4)
577
- nokogiri (1.15.4-x86_64-linux)
578
- racc (~> 1.4)
579
575
  noticed (1.6.3)
580
576
  http (>= 4.0.0)
581
577
  rails (>= 5.2.0)
Binary file
@@ -0,0 +1,46 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Operator
5
+ class PieChartEventsController < ::Spree::Api::V2::ResourceController
6
+ ACCEPTED_KYC_FIELDS = %i[guest_gender guest_occupation guest_nationality].freeze
7
+ ADDITIONAL_FIELDS = %i[participation entry_type].freeze
8
+
9
+ def index
10
+ kyc_fields = fetch_filtered_kyc_fields
11
+ types = ADDITIONAL_FIELDS + kyc_fields
12
+
13
+ resources = types.map { |type| SpreeCmCommissioner::ChartType.new(type) }
14
+
15
+ render json: SpreeCmCommissioner::V2::Operator::ChartTypeSerializer.new(resources).serializable_hash
16
+ end
17
+
18
+ def resource
19
+ @resource = pie_chart_event_aggregator_queries.call
20
+ end
21
+
22
+ def resource_serializer
23
+ SpreeCmCommissioner::V2::Operator::PieChartEventAggregatorSerializer
24
+ end
25
+
26
+ private
27
+
28
+ def pie_chart_event_aggregator_queries
29
+ SpreeCmCommissioner::PieChartEventQueries.new(
30
+ taxon_id: params[:taxon_id],
31
+ chart_type: params[:id] || 'participation',
32
+ refreshed: params[:refreshed] || false
33
+ )
34
+ end
35
+
36
+ def fetch_filtered_kyc_fields
37
+ Spree::Product
38
+ .where(event_id: params[:taxon_id])
39
+ .flat_map(&:kyc_fields)
40
+ .uniq & ACCEPTED_KYC_FIELDS
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -39,22 +39,22 @@ module Spree
39
39
  spree_authorize! :update, spree_current_order, order_token
40
40
 
41
41
  begin
42
- if guest_params[:guest_dynamic_fields_attributes]
43
- resource.guest_dynamic_fields.destroy_all
44
-
45
- guest_params[:guest_dynamic_fields_attributes].each do |attr|
46
- field = build_dynamic_field(attr)
47
- field.save!
42
+ ActiveRecord::Base.transaction do
43
+ if guest_params[:guest_dynamic_fields_attributes]
44
+ dynamic_field_ids = guest_params[:guest_dynamic_fields_attributes].pluck(:dynamic_field_id)
45
+ resource.guest_dynamic_fields.where(dynamic_field_id: dynamic_field_ids).destroy_all
46
+
47
+ guest_params[:guest_dynamic_fields_attributes].each do |attr|
48
+ field = build_dynamic_field(attr)
49
+ field.save!
50
+ end
48
51
  end
49
- end
50
52
 
51
- resource.assign_attributes(guest_params.except(:guest_dynamic_fields_attributes))
52
-
53
- if resource.save
54
- render_serialized_payload { serialize_resource(resource) }
55
- else
56
- render_error_payload(resource.errors, 400)
53
+ resource.assign_attributes(guest_params.except(:guest_dynamic_fields_attributes))
54
+ resource.save!
57
55
  end
56
+
57
+ render_serialized_payload { serialize_resource(resource) }
58
58
  rescue ActiveRecord::RecordInvalid => e
59
59
  render_error_payload(e.record.errors, 400)
60
60
  end
@@ -1,17 +1,16 @@
1
- # <p>📣 --- <b>[NEW GUEST CLAIMED INVITATION]</b> ---</p>
2
-
3
- # <p>✨ <b>Ticket Successfully Claimed!</b> ✨</p>
4
-
5
- # <p><b>🙍 Claimed By</b><br>
6
- # Name: Orng SreyMoch<br>
7
- # Tel: <code>0966827830</code></p>
8
-
9
- # <p><b>Run Ticket + 2 way bus ticket + Tent</b><br>
10
- # <i>👉 Distance: 5km, Tent: tent (1 people), and T-shirt: S</i><br>
11
- # <i>🗓️ May 30, 2025 -> Jun 30, 2025</i></p>
12
-
13
- # <p><b>📑 Order Number:</b><br>
14
- # <code>R172627517</code></p>
1
+ # 🎫 --- [NEW INVITATION CLAIMED] ---
2
+ #
3
+ # <b>🙍 Claimed By</b>
4
+ # Name: Orng SreyMoch
5
+ # Tel: <code>+85570760548</code>
6
+ #
7
+ # <b>5km Running Ticket</b>
8
+ # Quantity: 1
9
+ # <i>👉 Distance: 5km, T-shirt: S</i>
10
+ # <i>🗓️ Nov 15, 2023 -> Nov 17, 2023</i>
11
+ #
12
+ # Order Number:
13
+ # <code>R621016753</code>
15
14
 
16
15
  module SpreeCmCommissioner
17
16
  class InviteGuestClaimedTelegramMessageFactory < TelegramMessageFactory
@@ -27,40 +26,31 @@ module SpreeCmCommissioner
27
26
  end
28
27
 
29
28
  def selected_line_items
30
- return order.line_items.for_vendor(vendor) if vendor.present?
31
-
32
- order.line_items
29
+ vendor.present? ? order.line_items.for_vendor(vendor) : order.line_items
33
30
  end
34
31
 
35
32
  # override
36
33
  def body
37
34
  text = []
38
- text << '<p>📣 --- <b>[NEW GUEST CLAIMED INVITATION]</b> ---</p>'
39
- text << '<p>✨ <b>Ticket Successfully Claimed!</b> ✨</p>'
40
- text << '<p><b>🙍 Claimed By</b><br>'
41
- text << "Name: #{guest.first_name} #{guest.last_name}<br>"
42
- text << "Tel: <code>#{guest.phone_number}</code></p>"
43
35
 
36
+ text << bold('🙍 Claimed By')
37
+ text << "Name: #{guest.first_name} #{guest.last_name}"
38
+ text << "Tel: #{inline_code(guest.phone_number)}"
39
+ text << ''
40
+
41
+ # Each invitation_link leads to one order with one line_item, claimed by one guest so quantity is always 1.
44
42
  selected_line_items.each do |item|
45
- text << line_item_block(item)
43
+ text << bold(item.product.name.to_s)
44
+ text << '🎟️ Quantity: 1'
45
+ text << italic("👉 #{item.options_text}") if item.options_text.present?
46
+ text << italic(pretty_date_for(item)) if item.date_present?
47
+ text << ''
46
48
  end
47
49
 
48
- text << "<p><b>📑 Order Number:</b><br><code>#{order.number}</code></p>"
49
-
50
- text.join("\n")
51
- end
52
-
53
- def line_item_block(item)
54
- parts = []
55
- parts << "<p><b>#{item.product.name}</b><br>"
56
-
57
- details = []
58
- details << "👉 #{item.options_text}" if item.options_text.present?
59
- details << pretty_date_for(item) if item.date_present?
50
+ text << bold('Order Number:')
51
+ text << inline_code(order.number)
60
52
 
61
- parts << details.map { |d| "<i>#{d}</i><br>" }.join
62
- parts << '</p>'
63
- parts.join("\n")
53
+ text.compact.join("\n")
64
54
  end
65
55
 
66
56
  def pretty_date_for(line_item)
@@ -9,7 +9,8 @@ module SpreeCmCommissioner
9
9
  set_option_value
10
10
  create_variant
11
11
  create_stock_item
12
- upload_image
12
+ upload_image_to(@ticket.master)
13
+ upload_image_to(@variant)
13
14
  create_place
14
15
  end
15
16
  end
@@ -77,13 +78,13 @@ module SpreeCmCommissioner
77
78
  context.fail!(message: @stock_item.errors.full_messages.join(', '))
78
79
  end
79
80
 
80
- def upload_image
81
+ def upload_image_to(target)
81
82
  return if context.params[:ticket_image].blank?
82
83
 
83
- @image = @ticket.master.images.new(attachment: context.params[:ticket_image])
84
- return if @image.save
84
+ image = target.images.new(attachment: context.params[:ticket_image])
85
+ return if image.save
85
86
 
86
- context.fail!(message: @image.errors.full_messages.join(', '))
87
+ context.fail!(message: image.errors.full_messages.join(', '))
87
88
  end
88
89
 
89
90
  def create_place
@@ -0,0 +1,44 @@
1
+ module SpreeCmCommissioner
2
+ class CreateVendor
3
+ include Interactor
4
+
5
+ def call
6
+ validate_context!
7
+
8
+ ActiveRecord::Base.transaction do
9
+ create_vendor
10
+ assign_vendor_to_user
11
+ create_role
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def validate_context!
18
+ context.fail!(message: 'User is missing') unless context.user
19
+ context.fail!(message: 'Vendor parameters are missing') unless context.vendor_params
20
+ end
21
+
22
+ def create_vendor
23
+ @vendor = Spree::Vendor.new(context.vendor_params)
24
+ return if @vendor.save
25
+
26
+ context.fail!(message: @vendor.errors.full_messages.join(', '))
27
+ end
28
+
29
+ def assign_vendor_to_user
30
+ context.user.vendors << @vendor
31
+ end
32
+
33
+ def create_role
34
+ role = context.user.spree_roles.create(
35
+ name: "#{@vendor.name.downcase.parameterize(separator: '-')}-organizer",
36
+ vendor_id: @vendor.id,
37
+ presentation: 'Organizer'
38
+ )
39
+ return if role.persisted?
40
+
41
+ context.fail!(message: role.errors.full_messages.to_sentence)
42
+ end
43
+ end
44
+ end
@@ -1,6 +1,5 @@
1
1
  module Spree
2
2
  module OrderMailerDecorator
3
- # overrided
4
3
  def cancel_email(order, resend: false)
5
4
  @order = order.respond_to?(:id) ? order : Spree::Order.find(order)
6
5
  return false if @order.email.blank?
@@ -12,18 +11,34 @@ module Spree
12
11
  @order = order.respond_to?(:id) ? order : Spree::Order.find(order)
13
12
  return false if @order.email.blank?
14
13
 
14
+ @tenant = @order.tenant
15
+ if @tenant.present?
16
+ @brand_color = @tenant.preferences[:brand_primary_color]
17
+ @vendor_logo_url = @tenant.active_vendor&.logo&.original_url
18
+ end
19
+
15
20
  @current_store = @order.store
16
21
  @product_type = @order.products.first&.product_type || 'accommodation'
17
22
 
18
- subject = (resend ? "[#{Spree.t(:resend).upcase}] " : '')
23
+ subject = resend ? "[#{Spree.t(:resend).upcase}] " : ''
19
24
  subject += "#{@current_store&.name} Booking Confirmation ##{@order.number}"
20
25
 
21
- mail(to: @order.email, from: from_address, subject: subject, store_url: @current_store.url) do |format|
26
+ mail(to: @order.email, from: from_email_address, subject: subject, store_url: @current_store.url) do |format|
22
27
  format.html { render layout: 'spree_cm_commissioner/layouts/order_mailer' }
23
28
  format.text
24
29
  end
25
30
  end
26
31
 
32
+ private
33
+
34
+ def from_email_address
35
+ if @order.tenant.nil?
36
+ from_address
37
+ else
38
+ @order.tenant.active_vendor&.from_email
39
+ end
40
+ end
41
+
27
42
  def ticket_email(guest, email)
28
43
  @guest = guest
29
44
  @event = @guest.event
@@ -55,6 +55,24 @@ module SpreeCmCommissioner
55
55
  def set_duration
56
56
  self.from_date ||= variant.start_date_time || event&.from_date
57
57
  self.to_date ||= variant.end_date_time || event&.to_date
58
+
59
+ return if persisted?
60
+
61
+ apply_variant_time(:from_date, variant.start_date_time)
62
+ apply_variant_time(:to_date, variant.end_date_time)
63
+ end
64
+
65
+ def apply_variant_time(field, variant_time)
66
+ field_value = send(field)
67
+
68
+ return unless field_value.present? && field_value.hour.zero? && variant_time.present?
69
+
70
+ send("#{field}=", field_value.change(
71
+ hour: variant_time.hour,
72
+ min: variant_time.min,
73
+ sec: variant_time.sec
74
+ )
75
+ )
58
76
  end
59
77
  end
60
78
  end
@@ -9,6 +9,7 @@ module SpreeCmCommissioner
9
9
  preference :payment_failed_image, :string, default: ''
10
10
  preference :payment_success_image, :string, default: ''
11
11
  preference :payment_loader, :string, default: ''
12
+ preference :brand_primary_color, :string, default: ''
12
13
  end
13
14
  end
14
15
  end
@@ -53,8 +53,9 @@ module SpreeCmCommissioner
53
53
  # No need to fallback to event start date to avoid n+1.
54
54
  # To get end_time duration of event, include event when needed instead.
55
55
  def start_date_time
56
- return nil if start_date.blank?
56
+ return nil if start_date.blank? && start_time.blank?
57
57
  return start_date if start_time.blank?
58
+ return start_time if start_date.blank?
58
59
 
59
60
  start_date.change(hour: start_time.hour, min: start_time.min, sec: start_time.sec)
60
61
  end
@@ -62,8 +63,9 @@ module SpreeCmCommissioner
62
63
  # No need to fallback to event end date to avoid n+1.
63
64
  # To get end_time duration of event, include event when needed instead.
64
65
  def end_date_time
65
- return nil if end_date.blank?
66
+ return nil if end_date.blank? && end_time.blank?
66
67
  return end_date if end_time.blank?
68
+ return end_time if end_date.blank?
67
69
 
68
70
  end_date.change(hour: end_time.hour, min: end_time.min, sec: end_time.sec)
69
71
  end
@@ -0,0 +1,10 @@
1
+ module SpreeCmCommissioner
2
+ class ChartType
3
+ attr_reader :chart_type, :name
4
+
5
+ def initialize(chart_type)
6
+ @chart_type = chart_type.to_s
7
+ @name = @chart_type.humanize
8
+ end
9
+ end
10
+ end
@@ -9,12 +9,14 @@ module SpreeCmCommissioner
9
9
  has_many :product_dynamic_fields, class_name: 'SpreeCmCommissioner::ProductDynamicField', dependent: :destroy
10
10
 
11
11
  enum data_type: { text: 0, number: 1, boolean: 2, checkbox: 3, radio: 4, selection: 5, textarea: 6 }
12
+ enum data_fill_stage: { pre_registration: 0, post_registration: 1, during_check_in: 2 }
12
13
 
13
14
  accepts_nested_attributes_for :dynamic_field_options, allow_destroy: true, reject_if: :all_blank
14
15
 
15
16
  validates :label, presence: true
16
17
  validates :data_type, presence: true
17
18
  validates :configurations, presence: true, allow_blank: false
19
+ validate :validate_data_fill_stage_phase
18
20
 
19
21
  def requires_dynamic_field_options?
20
22
  checkbox? || radio? || selection?
@@ -30,5 +32,23 @@ module SpreeCmCommissioner
30
32
  self.configurations ||= {}
31
33
  self.configurations['multiple_select'] = ActiveModel::Type::Boolean.new.cast(value)
32
34
  end
35
+
36
+ def required_at_stage?(stage)
37
+ data_fill_stage == stage.to_s
38
+ end
39
+
40
+ def display_label
41
+ label + " (#{data_fill_stage.humanize})"
42
+ end
43
+
44
+ private
45
+
46
+ def validate_data_fill_stage_phase
47
+ return if data_fill_stage.blank?
48
+
49
+ return if data_fill_stage.in?(self.class.data_fill_stages.keys)
50
+
51
+ errors.add(:data_fill_stage, "must be one of: #{self.class.data_fill_stages.keys.join(', ')}")
52
+ end
33
53
  end
34
54
  end
@@ -213,6 +213,62 @@ module SpreeCmCommissioner
213
213
  save!
214
214
  end
215
215
 
216
+ def missing_dynamic_fields_for_stage(stage)
217
+ required_fields = line_item.product.dynamic_fields.where(data_fill_stage: stage)
218
+ filled_ids = guest_dynamic_fields.map(&:dynamic_field_id)
219
+
220
+ required_fields.reject { |field| filled_ids.include?(field.id) }
221
+ end
222
+
223
+ def all_required_dynamic_fields_completed?(stage)
224
+ missing_dynamic_fields_for_stage(stage).blank?
225
+ end
226
+
227
+ def validate_dynamic_fields_for_phase(phase)
228
+ required_fields = line_item.product.dynamic_fields.where(data_fill_stage: phase)
229
+ filled_ids = guest_dynamic_fields.map(&:dynamic_field_id)
230
+
231
+ required_fields.each do |field|
232
+ errors.add(:base, "#{field.label} is required for #{phase} phase") unless filled_ids.include?(field.id)
233
+ end
234
+ end
235
+
236
+ def pre_registration_completed?
237
+ return true unless line_item.product.dynamic_fields.pre_registration.any?
238
+
239
+ pre_registration_fields_completed?
240
+ end
241
+
242
+ def post_registration_completed?
243
+ return true unless line_item.product.dynamic_fields.post_registration.any?
244
+
245
+ post_registration_fields_completed?
246
+ end
247
+
248
+ def check_in_completed?
249
+ return true unless line_item.product.dynamic_fields.during_check_in.any?
250
+
251
+ check_in_fields_completed?
252
+ end
253
+
254
+ def current_collection_phase
255
+ if check_in.present?
256
+ :during_check_in
257
+ elsif line_item.order.completed?
258
+ :post_registration
259
+ else
260
+ :pre_registration
261
+ end
262
+ end
263
+
264
+ def phase_completion_status
265
+ {
266
+ pre_registration: pre_registration_completed?,
267
+ post_registration: post_registration_completed?,
268
+ during_check_in: check_in_completed?
269
+ }
270
+ end
271
+
216
272
  # bib_number: 345, bib_prefix: 5KM, bib_zerofill: 5 => return 5KM00345
217
273
  # bib_number: 345, bib_prefix: 5KM, bib_zerofill: 2 => return 5KM345
218
274
  def formatted_bib_number
@@ -7,6 +7,7 @@ module SpreeCmCommissioner
7
7
  validates :value, presence: true
8
8
  validate :validate_value_format, if: -> { value.present? && dynamic_field.present? }
9
9
  validate :validate_option_reference, if: -> { dynamic_field_option.present? }
10
+ validate :check_required_fields_based_on_phase
10
11
 
11
12
  private
12
13
 
@@ -34,10 +35,49 @@ module SpreeCmCommissioner
34
35
 
35
36
  def validate_option_reference
36
37
  return if dynamic_field.blank? || dynamic_field_option.blank?
37
-
38
38
  return if dynamic_field_option.dynamic_field_id == dynamic_field_id
39
39
 
40
40
  errors.add(:dynamic_field_option, 'must belong to the same dynamic field')
41
41
  end
42
+
43
+ def before_checkin_phase?
44
+ guest.present? && guest.line_item.present? && guest.check_in.nil?
45
+ end
46
+
47
+ def after_purchase_phase?
48
+ guest.present? && guest.line_item.present? && guest.line_item.completed? && guest.check_in.nil?
49
+ end
50
+
51
+ def during_checkin_phase?
52
+ guest.present? && guest.check_in.present?
53
+ end
54
+
55
+ def pre_registration_required?
56
+ dynamic_field.data_fill_stage.to_sym == :pre_registration &&
57
+ guest.line_item.order.completed? && guest.check_in.blank?
58
+ end
59
+
60
+ def post_registration_required?
61
+ dynamic_field.data_fill_stage.to_sym == :post_registration &&
62
+ guest.line_item.order.completed? && guest.check_in.blank?
63
+ end
64
+
65
+ def during_check_in_required?
66
+ dynamic_field.data_fill_stage.to_sym == :during_check_in &&
67
+ guest.check_in.present?
68
+ end
69
+
70
+ def check_required_fields_based_on_phase
71
+ return if dynamic_field.blank? || value.present?
72
+
73
+ case dynamic_field.data_fill_stage.to_sym
74
+ when :pre_registration
75
+ errors.add(:value, I18n.t('guest_dynamic_field.errors.pre_registration_required')) if pre_registration_required?
76
+ when :post_registration
77
+ errors.add(:value, I18n.t('guest_dynamic_field.errors.post_registration_required')) if post_registration_required?
78
+ when :during_check_in
79
+ errors.add(:value, I18n.t('guest_dynamic_field.errors.during_check_in_required')) if during_check_in_required?
80
+ end
81
+ end
42
82
  end
43
83
  end
@@ -86,6 +86,28 @@ module SpreeCmCommissioner
86
86
  "#{Spree::Store.default.formatted_url}/tickets/#{slug}"
87
87
  end
88
88
 
89
+ def dynamic_fields_by_collection_phase
90
+ {
91
+ pre_registration: dynamic_fields.pre_registration.order(:position),
92
+ post_registration: dynamic_fields.post_registration.order(:position),
93
+ during_check_in: dynamic_fields.during_check_in.order(:position)
94
+ }
95
+ end
96
+
97
+ def required_dynamic_fields_completed?(guest, phase)
98
+ required_fields = dynamic_fields.where(data_fill_stage: phase)
99
+ return true if required_fields.empty?
100
+
101
+ filled_ids = guest.guest_dynamic_fields.where(dynamic_field_id: required_fields.select(:id)).pluck(:dynamic_field_id)
102
+ (required_fields.pluck(:id) - filled_ids).empty?
103
+ end
104
+
105
+ def required_fields_for_guest(guest)
106
+ dynamic_fields_by_collection_phase.transform_values do |fields|
107
+ fields.select { |field| guest.guest_dynamic_fields.where(dynamic_field: field).empty? }
108
+ end
109
+ end
110
+
89
111
  private
90
112
 
91
113
  def set_tenant
@@ -88,6 +88,12 @@ module SpreeCmCommissioner
88
88
  has_spree_role?('operator')
89
89
  end
90
90
 
91
+ def vendor_organizer?(vendor)
92
+ spree_roles.any? do |role|
93
+ role.name == "#{vendor.name.downcase.parameterize(separator: '-')}-organizer"
94
+ end
95
+ end
96
+
91
97
  def full_name
92
98
  [first_name, last_name].compact_blank.join(' ')
93
99
  end
@@ -93,6 +93,7 @@ module SpreeCmCommissioner
93
93
  }
94
94
 
95
95
  base.validates :commission_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }
96
+ base.validates :from_email, presence: true, if: :tenant_present?
96
97
 
97
98
  def base.by_vendor_id!(vendor_id)
98
99
  if vendor_id.to_s =~ /^\d+$/
@@ -195,6 +196,8 @@ module SpreeCmCommissioner
195
196
 
196
197
  vendor_kind_option_values.where(option_type_id: rules_option_type.id)
197
198
  end
199
+
200
+ delegate :present?, to: :tenant, prefix: true
198
201
  end
199
202
  end
200
203