spree_cm_commissioner 1.12.0 → 1.14.0

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 (25) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/controllers/spree/admin/product_places_controller.rb +2 -0
  4. data/app/controllers/spree/api/v2/organizer/invite_guests_controller.rb +90 -0
  5. data/app/factory/spree_cm_commissioner/invite_guest_claimed_telegram_message_factory.rb +120 -0
  6. data/app/finders/spree_cm_commissioner/products/find_decorator.rb +16 -0
  7. data/app/helpers/spree_cm_commissioner/transit/trip_helper.rb +12 -0
  8. data/app/interactors/spree_cm_commissioner/order_importer/multi_guest.rb +31 -0
  9. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +2 -3
  10. data/app/models/spree_cm_commissioner/cms_page_decorator.rb +16 -1
  11. data/app/models/spree_cm_commissioner/invite_guest.rb +23 -0
  12. data/app/models/spree_cm_commissioner/place.rb +1 -1
  13. data/app/models/spree_cm_commissioner/product_place.rb +1 -0
  14. data/app/models/spree_cm_commissioner/taxon_decorator.rb +2 -0
  15. data/app/queries/spree_cm_commissioner/trip_query.rb +35 -25
  16. data/app/serializers/spree/v2/organizer/invite_guest_serializer.rb +13 -0
  17. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +2 -1
  18. data/app/views/spree_cm_commissioner/event_transactional_mailer/_mailer_stylesheets.html.erb +2 -0
  19. data/config/routes.rb +1 -0
  20. data/db/migrate/20250422014417_create_spree_cm_commissioner_invite_guest.rb +21 -0
  21. data/db/migrate/20250528073748_remove_unique_slug_index_on_spree_cms_pages.rb +12 -0
  22. data/db/migrate/20250528074043_add_unique_index_on_slug_tenant_id_deleted_at_to_spree_cms_pages.rb +13 -0
  23. data/lib/spree_cm_commissioner/test_helper/factories/cms_page_factory.rb +11 -0
  24. data/lib/spree_cm_commissioner/version.rb +1 -1
  25. metadata +13 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d84958e990097b26c5a1550acf0683bb9901bfbb7dc3cc2e5e0057eb9f292a3
4
- data.tar.gz: 9e5112083a317fb9ae6e7bf07802d9e02cd86ae1fe0589e2214fc19c7dd476e4
3
+ metadata.gz: a83f128539c20da0667aaa913954b8d686df38054b90ea51ede36b2967a96c4b
4
+ data.tar.gz: 46fa67f9aff722e83efd60ae52bb640f5ba9b2576294625a3c07093cbf0c2784
5
5
  SHA512:
6
- metadata.gz: 8cbed043afc749ce07bf954a558fe4a1f692c6ae0d06b06d1c8f54bdd0f1069e1ea8d956ce2bb90240ba781a14afc55400d7167f1d4604b0127b3bb79142cb23
7
- data.tar.gz: f4dec3ddd14ac3654c0434cc2c9c81fcb7b6a16d10a87cdd89c353bc0c0f7571867e73494564149b5fb7fc983619f4dcb839bd52af5cc9bfb1da8f181de55757
6
+ metadata.gz: d48ff6f2945c262955121a7f7144c5fc4ad53afa23bb2b093baaefd985346276a60b82de985b1912ec5a8fb64e6e86d37eb163638c2261446c2f85945165c3df
7
+ data.tar.gz: 7e21994489d8d36559a632a0008a92ecf9902c95be865190624173b89e1c8ad6146f405744971dbd448935cf9a9557b60401f34b6d211ce833eca61880082719
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (1.12.0)
37
+ spree_cm_commissioner (1.14.0)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -49,6 +49,8 @@ module Spree
49
49
  place_json = Base64.strict_decode64(place_base_64_content)
50
50
  place_hash = JSON.parse(place_json)
51
51
 
52
+ place_hash = place_hash.except('lft', 'rgt', 'parent_id', 'depth')
53
+
52
54
  @place = SpreeCmCommissioner::Place.where(reference: place_hash['reference']).first_or_initialize do |place|
53
55
  place.assign_attributes(place_hash)
54
56
  end
@@ -0,0 +1,90 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Organizer
5
+ class InviteGuestsController < ::Spree::Api::V2::Organizer::BaseController
6
+ before_action :load_invite_guest_by_token, only: :show
7
+ before_action :load_invite_guest, :assign_line_item_data, only: :update
8
+
9
+ def show
10
+ render_serialized_payload { serialize_resource(@invite_guest) }
11
+ end
12
+
13
+ def update
14
+ return render_error(:revoked) if @invite_guest.revoked?
15
+ return render_error(:closed) if @invite_guest.expired?
16
+ return render_error(:fully_claimed) if @invite_guest.fully_claimed?
17
+
18
+ guest = @line_item.guests.new(guest_params)
19
+ guest.event_id = @invite_guest.event_id
20
+
21
+ if guest.save
22
+ @invite_guest.update(claimed_status: :claimed) if @line_item.guests.count == @invite_guest.quantity
23
+ send_guest_claimed_invitation_telegram_alert_to_vendor(guest)
24
+
25
+ render json: SpreeCmCommissioner::V2::Storefront::GuestSerializer.new(guest.reload).serializable_hash
26
+ else
27
+ render_error_payload(guest.errors.full_messages.to_sentence)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def send_guest_claimed_invitation_telegram_alert_to_vendor(guest)
34
+ title = '📣 --- [NEW GUEST CLAIMED INVITATION] ---' # Style/StringLiterals
35
+ chat_id = guest.event.vendor.preferred_telegram_chat_id
36
+ return if chat_id.blank?
37
+
38
+ factory = SpreeCmCommissioner::InviteGuestClaimedTelegramMessageFactory.new(
39
+ title: title,
40
+ order: @invite_guest.order,
41
+ vendor: guest.event.vendor
42
+ )
43
+ SpreeCmCommissioner::TelegramNotificationSenderJob.perform_later(
44
+ chat_id: chat_id,
45
+ message: factory.message,
46
+ parse_mode: factory.parse_mode
47
+ )
48
+ end
49
+
50
+ def load_invite_guest_by_token
51
+ @invite_guest = SpreeCmCommissioner::InviteGuest.find_by(token: params[:id])
52
+ rescue ActiveRecord::RecordNotFound
53
+ render_error_payload(I18n.t('invite.url_not_found'))
54
+ end
55
+
56
+ def load_invite_guest
57
+ @invite_guest = SpreeCmCommissioner::InviteGuest.find(params[:id])
58
+ end
59
+
60
+ def assign_line_item_data
61
+ @line_item = @invite_guest.order.line_items.first
62
+ @guests = @line_item.guests
63
+ end
64
+
65
+ def render_error(message)
66
+ render json: { errors: message }
67
+ end
68
+
69
+ def guest_params
70
+ params.require(:invite_guest).permit(
71
+ :first_name,
72
+ :last_name,
73
+ :dob,
74
+ :gender,
75
+ :age,
76
+ :emergency_contact,
77
+ :phone_number,
78
+ :address,
79
+ :other_organization
80
+ )
81
+ end
82
+
83
+ def resource_serializer
84
+ ::Spree::V2::Organizer::InviteGuestSerializer
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,120 @@
1
+ # 🎟️ --- [TICKET CLAIMED] ---
2
+ #
3
+ # Order Number:
4
+ # <code>R621016753</code>
5
+ #
6
+ # ✅ <b>Ticket Successfully Claimed!</b>
7
+ #
8
+ # [ x1 ]
9
+ # <b>5km Running Ticket</b>
10
+ # <i>👉 Distance: 5km, T-shirt: M</i>
11
+ # <i>🗓️ Nov 15, 2023</i>
12
+ # <i>🏪 Temple Run with Sai</i>
13
+ #
14
+ # [ x2 ]
15
+ # <b>5km Running Ticket</b>
16
+ # <i>👉 Distance: 5km, T-shirt: S</i>
17
+ # <i>🗓️ Nov 15, 2023 -> Nov 17, 2023</i>
18
+ # <i>🏪 Temple Run with Sai</i>
19
+ #
20
+ # <b>🙍 Claimed By</b>
21
+ # Name: Vaneath Awesome
22
+ # Tel: <code>+85570760548</code>
23
+ # Email: <code>admin_dev@cm.com</code>
24
+
25
+ module SpreeCmCommissioner
26
+ class InviteGuestClaimedTelegramMessageFactory < TelegramMessageFactory
27
+ attr_reader :order, :vendor, :show_details_link
28
+
29
+ def initialize(title:, order:, subtitle: nil, show_details_link: nil, vendor: nil)
30
+ @order = order
31
+ @vendor = vendor
32
+ @show_details_link = show_details_link || false
33
+
34
+ super(title: title, subtitle: subtitle)
35
+ end
36
+
37
+ def selected_line_items
38
+ return order.line_items.for_vendor(vendor) if vendor.present?
39
+
40
+ order.line_items
41
+ end
42
+
43
+ # override
44
+ def body
45
+ text = []
46
+ text << "Order Number:\n<code>#{order.number}</code>\n"
47
+ text << "\n✅ <b>Ticket Successfully Claimed!</b>\n"
48
+ text << selected_line_items.map { |item| line_item_content(item) }.compact.join("\n\n")
49
+ text.compact.join("\n")
50
+ end
51
+
52
+ def line_item_content(line_item)
53
+ text = []
54
+
55
+ text << bold(line_item.product.name.to_s)
56
+ text << "Quantity: #{line_item.quantity}"
57
+ text << italic("👉 #{line_item.options_text}") if line_item.options_text.present?
58
+ text << italic(pretty_date_for(line_item)) if pretty_date_for(line_item).present?
59
+ text << italic("🏪 #{line_item.vendor.name}") if line_item.vendor&.name.present? && vendor.blank?
60
+
61
+ text.compact.join("\n")
62
+ end
63
+
64
+ def pretty_date_for(line_item)
65
+ return nil unless line_item.date_present?
66
+
67
+ from_date = pretty_date(line_item.from_date)
68
+ to_date = pretty_date(line_item.to_date)
69
+
70
+ if from_date == to_date
71
+ "🗓️ #{from_date}"
72
+ else
73
+ "🗓️ #{from_date} -> #{to_date}"
74
+ end
75
+ end
76
+
77
+ # override
78
+ def footer
79
+ text = []
80
+
81
+ text << bold('🙍 Claimed By')
82
+ text << "Name: #{order.name}"
83
+ text << "Tel: #{inline_code(order.intel_phone_number || order.phone_number)}"
84
+ text << "Email: #{inline_code(order.email)}" if order.email.present?
85
+
86
+ if show_details_link && order.guests.any?
87
+ text << ''
88
+ text << 'View Tickets:'
89
+ text += generate_guests_links(order.guests)
90
+ end
91
+
92
+ text.compact.join("\n")
93
+ end
94
+
95
+ # Result:
96
+ # | No. A24 | No. A24 |
97
+ # | No. A24 | No. A24 |
98
+ # | No. A24 | No. A24 |
99
+ # | No. A24 | No. A24 |
100
+ # | No. A24 | No. A24 |
101
+ # | No. A24 |
102
+ def generate_guests_links(guests, guests_per_row = 2)
103
+ rows = (guests.size.to_f / guests_per_row).ceil
104
+ formatted_rows = []
105
+
106
+ rows.times do |i|
107
+ row_guests = guests.slice(i * guests_per_row, guests_per_row)
108
+ formatted_row = row_guests.map do |guest|
109
+ button_label = guest.seat_number.present? ? "No. #{guest.seat_number}" : "No. #{guest.formatted_bib_number || 'N/A'}"
110
+ link = Rails.application.routes.url_helpers.guest_cards_url(guest.token)
111
+ "| <a href='#{link}'>#{button_label}</a>"
112
+ end.join(' ')
113
+
114
+ formatted_rows << ("#{formatted_row} |")
115
+ end
116
+
117
+ formatted_rows
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,16 @@
1
+ module SpreeCmCommissioner
2
+ module Products
3
+ module FindDecorator
4
+ # override: to return only visble products
5
+ def by_taxons(products)
6
+ return products unless taxons?
7
+
8
+ products.joins(:classifications).where(::Spree::Classification.table_name => { taxon_id: taxons, visible: true })
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ unless Spree::Products::Find.included_modules.include?(SpreeCmCommissioner::Products::FindDecorator)
15
+ Spree::Products::Find.prepend(SpreeCmCommissioner::Products::FindDecorator)
16
+ end
@@ -0,0 +1,12 @@
1
+ module SpreeCmCommissioner
2
+ module Transit
3
+ module TripHelper
4
+ def parse_time(time, timezone = nil)
5
+ return nil if time.blank?
6
+
7
+ formatted_time = Time.zone.parse(time.to_s)
8
+ timezone ? formatted_time.in_time_zone(timezone) : formatted_time
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ module SpreeCmCommissioner
2
+ module OrderImporter
3
+ class MultiGuest < BaseInteractor
4
+ delegate :params, :quantity, to: :context
5
+
6
+ def call
7
+ return context.fail!(message: 'variant_id_is_required') if params[:variant_id].blank?
8
+ return context.fail!(message: 'email_or_phone_is_required') if params[:order_email].blank? && params[:order_phone_number].blank?
9
+
10
+ context.order = construct_order
11
+ context.fail!(message: context.order.errors.full_messages.to_sentence) unless context.order.save
12
+ end
13
+
14
+ def construct_order
15
+ Spree::Order.new(
16
+ email: params[:order_email],
17
+ phone_number: params[:order_phone_number],
18
+ completed_at: Date.current,
19
+ state: 'complete',
20
+ payment_state: 'paid',
21
+ line_items_attributes: [
22
+ {
23
+ quantity: quantity.to_i,
24
+ variant_id: params[:variant_id]
25
+ }
26
+ ]
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
@@ -98,13 +98,12 @@ module SpreeCmCommissioner
98
98
  line_items.each do |line_item|
99
99
  next if line_item.event.nil?
100
100
 
101
- event_id = line_item.event.parent_id
102
- SpreeCmCommissioner::EventTransactionalMailer.send_to_participant(event_id, user.id).deliver_later
101
+ SpreeCmCommissioner::EventTransactionalMailer.send_to_participant(line_item.event_id, user.id).deliver_later
103
102
  end
104
103
  end
105
104
 
106
105
  def user_has_email?
107
- user.email.present?
106
+ user.present? && user.email.present?
108
107
  end
109
108
 
110
109
  # allow authorized user to accept all requested line items
@@ -2,8 +2,23 @@ module SpreeCmCommissioner
2
2
  module CmsPageDecorator
3
3
  def self.prepended(base)
4
4
  base.belongs_to :tenant, class_name: 'SpreeCmCommissioner::Tenant'
5
+
6
+ base.validates :slug,
7
+ uniqueness: {
8
+ scope: %i[tenant_id deleted_at],
9
+ allow_nil: true,
10
+ case_sensitive: true
11
+ }
5
12
  end
6
13
  end
7
14
  end
8
15
 
9
- Spree::CmsPage.prepend(SpreeCmCommissioner::CmsPageDecorator) unless Spree::CmsPage.included_modules.include?(SpreeCmCommissioner::CmsPageDecorator)
16
+ unless Spree::CmsPage.included_modules.include?(SpreeCmCommissioner::CmsPageDecorator)
17
+ # remove old :slug validations
18
+ Spree::CmsPage._validators.reject! { |key, _| key == :slug }
19
+ Spree::CmsPage._validate_callbacks.each do |c|
20
+ c.filter.attributes.delete(:slug) if c.filter.respond_to?(:attributes)
21
+ end
22
+
23
+ Spree::CmsPage.prepend(SpreeCmCommissioner::CmsPageDecorator)
24
+ end
@@ -0,0 +1,23 @@
1
+ module SpreeCmCommissioner
2
+ class InviteGuest < SpreeCmCommissioner::Base
3
+ enum invite_type: { invite: 0, open: 1, sponsor: 2 }
4
+ enum claimed_status: { active: 0, claimed: 1, revoked: 2 }
5
+
6
+ belongs_to :variant, class_name: 'Spree::Variant'
7
+ belongs_to :order, class_name: 'Spree::Order'
8
+ belongs_to :event, class_name: 'Spree::Taxon'
9
+
10
+ self.whitelisted_ransackable_attributes = %w[claimed_status]
11
+
12
+ def expired?
13
+ expiration_date.present? && expiration_date < Time.current
14
+ end
15
+
16
+ def fully_claimed?
17
+ return false unless order.present? && order.line_items.any?
18
+
19
+ line_item = order.line_items.first
20
+ line_item.guests.count >= order.line_items.first.number_of_guests
21
+ end
22
+ end
23
+ end
@@ -17,7 +17,7 @@ module SpreeCmCommissioner
17
17
  has_many :products, through: :product_places
18
18
 
19
19
  has_many :children, -> { order(:lft) }, class_name: 'SpreeCmCommissioner::Place', foreign_key: :parent_id, dependent: :destroy
20
- has_many :vendor_stops, class_name: 'SpreeCmCommissioner::VendorStop', dependent: :destroy
20
+ has_many :vendor_stops, class_name: 'SpreeCmCommissioner::VendorStop', dependent: :destroy, foreign_key: :stop_id
21
21
 
22
22
  def self.ransackable_attributes(auth_object = nil)
23
23
  super & %w[name code]
@@ -1,6 +1,7 @@
1
1
  module SpreeCmCommissioner
2
2
  class ProductPlace < Base
3
3
  self.inheritance_column = '_type_disabled'
4
+ acts_as_list column: :position
4
5
 
5
6
  belongs_to :product, class_name: 'Spree::Product', optional: false
6
7
  belongs_to :place, class_name: 'SpreeCmCommissioner::Place', optional: false
@@ -55,6 +55,8 @@ module SpreeCmCommissioner
55
55
  base.has_many :crew_invites, class_name: 'SpreeCmCommissioner::CrewInvite', dependent: :destroy
56
56
  base.has_many :invite_user_events, through: :user_events, class_name: 'SpreeCmCommissioner::InviteUserEvent'
57
57
 
58
+ base.has_many :invite_guests, class_name: 'SpreeCmCommissioner::InviteGuest', foreign_key: :event_id
59
+
58
60
  base.has_many :line_items, through: :products
59
61
  base.has_many :event_line_items, class_name: 'Spree::LineItem', foreign_key: :event_id
60
62
 
@@ -1,21 +1,23 @@
1
1
  module SpreeCmCommissioner
2
2
  class TripQuery
3
- attr_reader :origin_id, :destination_id, :travel_date
3
+ include SpreeCmCommissioner::Transit::TripHelper
4
4
 
5
- def initialize(origin_id:, destination_id:, travel_date: Time.zone.now)
5
+ attr_reader :origin_id, :destination_id, :travel_date, :params
6
+
7
+ def initialize(origin_id:, destination_id:, travel_date:, params: {})
6
8
  @origin_id = origin_id
7
9
  @destination_id = destination_id
8
10
  @travel_date = travel_date
11
+ @travel_date = Time.zone.now if travel_date.to_date == Time.zone.now.to_date
12
+ @params = params
9
13
  end
10
14
 
11
15
  def call
12
- trips = direct_trips
13
- connections = connected_trips if trips.size < 3
14
-
15
- trip_results = trips.map { |trip| SpreeCmCommissioner::TripQueryResult.new([trip]) }
16
+ return [] if travel_date.to_date < Date.current
16
17
 
17
- trip_results += connections if connections
18
- trip_results
18
+ direct_results = direct_trips.map { |trip| SpreeCmCommissioner::TripQueryResult.new([trip]) }
19
+ connected_results = connected_trips || []
20
+ direct_results + connected_results
19
21
  end
20
22
 
21
23
  def direct_trips
@@ -38,7 +40,12 @@ module SpreeCmCommissioner
38
40
  .joins('INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = spree_variants.id AND drop_off.stop_type = 1')
39
41
  .joins('INNER JOIN spree_vendors AS vendors ON vendors.id = spree_variants.vendor_id')
40
42
  .joins('INNER JOIN spree_products AS routes ON routes.id = spree_variants.product_id')
41
- .where('boarding.stop_id = ? AND drop_off.stop_id = ?', origin_id, destination_id)
43
+ result = result.where(vendors: { id: params[:vendor_id] }) if params[:vendor_id].present?
44
+ result = result.where('boarding.stop_id = ? AND drop_off.stop_id = ?', origin_id, destination_id)
45
+ .where('trips.departure_time > ? AND trips.departure_time <= ?',
46
+ travel_date.strftime('%H:%M:%S'),
47
+ travel_date.end_of_day.strftime('%H:%M:%S')
48
+ )
42
49
 
43
50
  result.map do |trip|
44
51
  trip_result_options = {
@@ -53,7 +60,7 @@ module SpreeCmCommissioner
53
60
  destination_id: trip[:destination_id],
54
61
  destination: trip[:destination],
55
62
  vehicle_id: trip[:vehicle_id],
56
- departure_time: trip[:departure_time],
63
+ departure_time: parse_time(trip[:departure_time]),
57
64
  duration: trip[:duration]
58
65
  }
59
66
  SpreeCmCommissioner::TripResult.new(trip_result_options)
@@ -63,17 +70,17 @@ module SpreeCmCommissioner
63
70
  def connected_trips # rubocop:disable Metrics/MethodLength
64
71
  result = SpreeCmCommissioner::TripConnection
65
72
  .joins('
66
- INNER JOIN spree_variants variant1 ON variant1.id = cm_trip_connections.from_trip_id
67
- INNER JOIN spree_variants variant2 ON variant2.id = cm_trip_connections.to_trip_id
68
- INNER JOIN spree_products AS routes1 ON routes1.id = variant1.product_id
69
- INNER JOIN spree_products AS routes2 ON routes2.id = variant2.product_id
70
- INNER JOIN cm_trips AS trip1 ON trip1.variant_id = cm_trip_connections.from_trip_id
71
- INNER JOIN cm_trips AS trip2 ON trip2.variant_id = cm_trip_connections.to_trip_id
72
- INNER JOIN cm_trip_stops trip1_origin ON trip1_origin.trip_id = variant1.id AND trip1_origin.stop_type = 0
73
- INNER JOIN cm_trip_stops trip2_origin ON trip2_origin.trip_id = variant2.id AND trip2_origin.stop_type = 0
74
- INNER JOIN cm_trip_stops trip2_destination ON trip2_destination.trip_id = variant2.id AND trip2_destination.stop_type = 1
75
- INNER JOIN spree_vendors AS vendor1 ON vendor1.id = variant1.vendor_id
76
- INNER JOIN spree_vendors AS vendor2 ON vendor2.id = variant2.vendor_id'
73
+ INNER JOIN spree_variants variant1 ON variant1.id = cm_trip_connections.from_trip_id
74
+ INNER JOIN spree_variants variant2 ON variant2.id = cm_trip_connections.to_trip_id
75
+ INNER JOIN spree_products AS routes1 ON routes1.id = variant1.product_id
76
+ INNER JOIN spree_products AS routes2 ON routes2.id = variant2.product_id
77
+ INNER JOIN cm_trips AS trip1 ON trip1.variant_id = cm_trip_connections.from_trip_id
78
+ INNER JOIN cm_trips AS trip2 ON trip2.variant_id = cm_trip_connections.to_trip_id
79
+ INNER JOIN cm_trip_stops trip1_origin ON trip1_origin.trip_id = variant1.id AND trip1_origin.stop_type = 0
80
+ INNER JOIN cm_trip_stops trip2_origin ON trip2_origin.trip_id = variant2.id AND trip2_origin.stop_type = 0
81
+ INNER JOIN cm_trip_stops trip2_destination ON trip2_destination.trip_id = variant2.id AND trip2_destination.stop_type = 1
82
+ INNER JOIN spree_vendors AS vendor1 ON vendor1.id = variant1.vendor_id
83
+ INNER JOIN spree_vendors AS vendor2 ON vendor2.id = variant2.vendor_id'
77
84
  )
78
85
  .select('cm_trip_connections.id AS id,
79
86
  trip1.variant_id AS trip1_id,
@@ -97,8 +104,11 @@ module SpreeCmCommissioner
97
104
  trip2_origin.stop_name AS trip2_origin,
98
105
  trip2_destination.stop_name AS trip2_destination,
99
106
  vendor2.name AS trip2_vendor_name'
100
- ).where('trip1_origin.stop_id = ? AND trip2_destination.stop_id = ?', origin_id, destination_id
101
- ).uniq
107
+ ).where('trip1_origin.stop_id = ? AND trip2_destination.stop_id = ?', origin_id, destination_id)
108
+ result = result.where('vendor1.id = ? OR vendor2.id = ?', params[:vendor_id], params[:vendor_id]) if params[:vendor_id].present?
109
+ result = result.where('trip1.departure_time > ? AND trip1.departure_time <= ?', travel_date.strftime('%H:%M:%S'),
110
+ travel_date.end_of_day.strftime('%H:%M:%S')
111
+ ).uniq
102
112
 
103
113
  return [] if result.blank?
104
114
 
@@ -113,7 +123,7 @@ module SpreeCmCommissioner
113
123
  trip_id: trip[:trip1_id],
114
124
  origin_id: trip[:trip1_origin_id],
115
125
  destination_id: trip[:trip1_destination_id],
116
- departure_time: trip[:trip1_departure_time],
126
+ departure_time: parse_time(trip[:trip1_departure_time]),
117
127
  duration: trip[:trip1_duration],
118
128
  vendor_name: trip[:trip1_vendor_name],
119
129
  route_name: trip[:route1_name],
@@ -126,7 +136,7 @@ module SpreeCmCommissioner
126
136
  trip_id: trip[:trip2_id],
127
137
  origin_id: trip[:trip2_origin_id],
128
138
  destination_id: trip[:trip2_destination_id],
129
- departure_time: trip[:trip2_departure_time],
139
+ departure_time: parse_time(trip[:trip2_departure_time]),
130
140
  duration: trip[:trip2_duration],
131
141
  vendor_name: trip[:trip2_vendor_name],
132
142
  route_name: trip[:route2_name],
@@ -0,0 +1,13 @@
1
+ module Spree
2
+ module V2
3
+ module Organizer
4
+ class InviteGuestSerializer < BaseSerializer
5
+ attributes :email, :quantity, :token, :invite_type, :claimed_status, :issued_to, :expiration_date,
6
+ :claimed_at, :email_send_at, :created_at, :updated_at, :remark
7
+ belongs_to :variant, serializer: SpreeCmCommissioner::V2::Storefront::EventVariantSerializer
8
+ belongs_to :order, serializer: Spree::V2::Storefront::OrderSerializer
9
+ belongs_to :event, serializer: Spree::V2::Storefront::TaxonSerializer
10
+ end
11
+ end
12
+ end
13
+ end
@@ -6,7 +6,8 @@ module SpreeCmCommissioner
6
6
 
7
7
  attributes :first_name, :last_name, :dob, :gender, :kyc_fields, :other_occupation, :created_at, :updated_at,
8
8
  :qr_data, :social_contact_platform, :social_contact, :available_social_contact_platforms,
9
- :age, :emergency_contact, :other_organization, :expectation, :upload_later, :address, :formatted_bib_number, :phone_number
9
+ :age, :emergency_contact, :other_organization, :expectation, :upload_later, :address, :formatted_bib_number, :phone_number,
10
+ :token
10
11
 
11
12
  # temporary, once app release after cm-app v1.9.1, we no longer need this.
12
13
  attribute :seat_number do |object|
@@ -131,6 +131,8 @@
131
131
  text-decoration: none;
132
132
  font-weight: 500;
133
133
  font-size: 14px;
134
+ width: 10rem;
135
+ max-width: 90%;
134
136
  }
135
137
 
136
138
  .cta-button:hover {
data/config/routes.rb CHANGED
@@ -450,6 +450,7 @@ Spree::Core::Engine.add_routes do
450
450
  resources :images
451
451
  resource :s3_signed_urls
452
452
  resources :invites
453
+ resources :invite_guests, only: %i[show update]
453
454
  end
454
455
 
455
456
  namespace :tenant do
@@ -0,0 +1,21 @@
1
+ class CreateSpreeCmCommissionerInviteGuest < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :cm_invite_guests, if_not_exists: true do |t|
4
+ t.references :variant, foreign_key: { to_table: :spree_variants }, null: true, index: true
5
+ t.references :order, foreign_key: { to_table: :spree_orders }, null: true, index: true
6
+ t.references :event, foreign_key: { to_table: :spree_taxons }, null: true, index: true
7
+ t.string :email, index: true
8
+ t.integer :quantity, default: 1
9
+ t.string :token, null: false, unique: true, index: true
10
+ t.integer :invite_type, default: 0
11
+ t.integer :claimed_status, default: 0
12
+ t.string :issued_to
13
+ t.datetime :expiration_date
14
+ t.text :remark
15
+ t.datetime :expires_at
16
+ t.datetime :claimed_at
17
+ t.datetime :email_send_at
18
+ t.timestamps
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ class RemoveUniqueSlugIndexOnSpreeCmsPages < ActiveRecord::Migration[7.0]
2
+ def change
3
+ if index_exists?(:spree_cms_pages,
4
+ [:slug, :store_id, :deleted_at],
5
+ unique: true,
6
+ name: "index_spree_cms_pages_on_slug_and_store_id_and_deleted_at")
7
+
8
+ remove_index :spree_cms_pages,
9
+ name: "index_spree_cms_pages_on_slug_and_store_id_and_deleted_at"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ class AddUniqueIndexOnSlugTenantIdDeletedAtToSpreeCmsPages < ActiveRecord::Migration[7.0]
2
+ def change
3
+ unless index_exists?(:spree_cms_pages,
4
+ [:slug, :tenant_id, :deleted_at],
5
+ unique: true,
6
+ name: "index_spree_cms_pages_on_slug_and_tenant_id_and_deleted_at")
7
+
8
+ add_index :spree_cms_pages, [:slug, :tenant_id, :deleted_at],
9
+ unique: true,
10
+ name: "index_spree_cms_pages_on_slug_and_tenant_id_and_deleted_at"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ FactoryBot.define do
2
+ factory :cm_cms_page, class: 'Spree::CmsPage' do
3
+ sequence(:slug) { |n| "page-slug-#{n}" }
4
+ title { "Sample Page Title" }
5
+ association :store, factory: :store
6
+ locale { 'en' }
7
+ type { 'Spree::Cms::Pages::StandardPage' }
8
+ tenant { nil }
9
+ deleted_at { nil }
10
+ end
11
+ end
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- VERSION = '1.12.0'.freeze
2
+ VERSION = '1.14.0'.freeze
3
3
 
4
4
  module_function
5
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_cm_commissioner
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-26 00:00:00.000000000 Z
11
+ date: 2025-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: spree
@@ -779,6 +779,7 @@ files:
779
779
  - app/controllers/spree/api/v2/operator/taxons_controller.rb
780
780
  - app/controllers/spree/api/v2/organizer/base_controller.rb
781
781
  - app/controllers/spree/api/v2/organizer/images_controller.rb
782
+ - app/controllers/spree/api/v2/organizer/invite_guests_controller.rb
782
783
  - app/controllers/spree/api/v2/organizer/invites_controller.rb
783
784
  - app/controllers/spree/api/v2/organizer/s3_signed_urls_controller.rb
784
785
  - app/controllers/spree/api/v2/organizer/ticket_images_controller.rb
@@ -960,12 +961,14 @@ files:
960
961
  - app/errors/spree_cm_commissioner/unauthorized_event_error.rb
961
962
  - app/errors/spree_cm_commissioner/unauthorized_vendor_error.rb
962
963
  - app/exception_notifier/spree_cm_commissioner/telegram_notifier.rb
964
+ - app/factory/spree_cm_commissioner/invite_guest_claimed_telegram_message_factory.rb
963
965
  - app/factory/spree_cm_commissioner/order_telegram_message_factory.rb
964
966
  - app/factory/spree_cm_commissioner/telegram_message_factory.rb
965
967
  - app/finders/spree_cm_commissioner/line_items/find_by_variant_decorator.rb
966
968
  - app/finders/spree_cm_commissioner/orders/find.rb
967
969
  - app/finders/spree_cm_commissioner/orders/find_by_state.rb
968
970
  - app/finders/spree_cm_commissioner/payment_methods/group_by_bank.rb
971
+ - app/finders/spree_cm_commissioner/products/find_decorator.rb
969
972
  - app/helpers/spree/admin/base_helper_decorator.rb
970
973
  - app/helpers/spree/admin/google_wallets_helper.rb
971
974
  - app/helpers/spree/base_helper_decorator.rb
@@ -984,6 +987,7 @@ files:
984
987
  - app/helpers/spree_cm_commissioner/pin_code_sender_helper.rb
985
988
  - app/helpers/spree_cm_commissioner/telegram/base_helper.rb
986
989
  - app/helpers/spree_cm_commissioner/transit/service_calendars_helper.rb
990
+ - app/helpers/spree_cm_commissioner/transit/trip_helper.rb
987
991
  - app/interactors/spree_cm_commissioner/accommodation_search_detail.rb
988
992
  - app/interactors/spree_cm_commissioner/account_deletion.rb
989
993
  - app/interactors/spree_cm_commissioner/account_deletion_cron_executor.rb
@@ -1034,6 +1038,7 @@ files:
1034
1038
  - app/interactors/spree_cm_commissioner/order_accepted_state_updater.rb
1035
1039
  - app/interactors/spree_cm_commissioner/order_complete_notification_sender.rb
1036
1040
  - app/interactors/spree_cm_commissioner/order_complete_telegram_sender.rb
1041
+ - app/interactors/spree_cm_commissioner/order_importer/multi_guest.rb
1037
1042
  - app/interactors/spree_cm_commissioner/order_importer/single_guest.rb
1038
1043
  - app/interactors/spree_cm_commissioner/order_rejected_notification_sender.rb
1039
1044
  - app/interactors/spree_cm_commissioner/order_rejected_state_updater.rb
@@ -1211,6 +1216,7 @@ files:
1211
1216
  - app/models/spree_cm_commissioner/import.rb
1212
1217
  - app/models/spree_cm_commissioner/imports/import_order.rb
1213
1218
  - app/models/spree_cm_commissioner/invite.rb
1219
+ - app/models/spree_cm_commissioner/invite_guest.rb
1214
1220
  - app/models/spree_cm_commissioner/invite_user_event.rb
1215
1221
  - app/models/spree_cm_commissioner/invite_user_taxon.rb
1216
1222
  - app/models/spree_cm_commissioner/invoice.rb
@@ -1498,6 +1504,7 @@ files:
1498
1504
  - app/serializers/spree/v2/organizer/asset_serializer.rb
1499
1505
  - app/serializers/spree/v2/organizer/base_serializer.rb
1500
1506
  - app/serializers/spree/v2/organizer/image_serializer.rb
1507
+ - app/serializers/spree/v2/organizer/invite_guest_serializer.rb
1501
1508
  - app/serializers/spree/v2/organizer/invite_serializer.rb
1502
1509
  - app/serializers/spree/v2/organizer/s3_signed_url_serializer.rb
1503
1510
  - app/serializers/spree/v2/organizer/taxon_serializer.rb
@@ -2405,6 +2412,7 @@ files:
2405
2412
  - db/migrate/20250404050117_add_archived_at_to_spree_orders.rb
2406
2413
  - db/migrate/20250407092556_add_variant_id_to_cm_trips.rb
2407
2414
  - db/migrate/20250418072528_add_nested_set_columns_to_places.rb
2415
+ - db/migrate/20250422014417_create_spree_cm_commissioner_invite_guest.rb
2408
2416
  - db/migrate/20250425084929_add_description_to_spree_prototypes.rb
2409
2417
  - db/migrate/20250425085938_add_preferences_to_spree_prototypes.rb
2410
2418
  - db/migrate/20250428025645_add_slug_to_spree_prototypes.rb
@@ -2418,6 +2426,8 @@ files:
2418
2426
  - db/migrate/20250520044533_add_event_to_spree_line_items.rb
2419
2427
  - db/migrate/20250521024345_add_tenant_id_to_cm_waiting_room_sessions.rb
2420
2428
  - db/migrate/20250521095539_add_kyc_to_spree_variants.rb
2429
+ - db/migrate/20250528073748_remove_unique_slug_index_on_spree_cms_pages.rb
2430
+ - db/migrate/20250528074043_add_unique_index_on_slug_tenant_id_deleted_at_to_spree_cms_pages.rb
2421
2431
  - docker-compose.yml
2422
2432
  - docs/option_types/attr_types.md
2423
2433
  - docs/private_key.pem
@@ -2451,6 +2461,7 @@ files:
2451
2461
  - lib/spree_cm_commissioner/test_helper/factories/back_image_factory.rb
2452
2462
  - lib/spree_cm_commissioner/test_helper/factories/branch_factory.rb
2453
2463
  - lib/spree_cm_commissioner/test_helper/factories/check_in_factory.rb
2464
+ - lib/spree_cm_commissioner/test_helper/factories/cms_page_factory.rb
2454
2465
  - lib/spree_cm_commissioner/test_helper/factories/country_factory.rb
2455
2466
  - lib/spree_cm_commissioner/test_helper/factories/customer_factory.rb
2456
2467
  - lib/spree_cm_commissioner/test_helper/factories/customer_notification_factory.rb