spree_cm_commissioner 2.0.0.pre.pre → 2.0.1.pre.pre
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.
- checksums.yaml +4 -4
- data/.env.example +3 -0
- data/Gemfile.lock +1 -1
- data/app/assets/images/cm-hangmeas-checkout_image.svg +63 -0
- data/app/assets/images/cm-hangmeas-failed.svg +56 -0
- data/app/assets/images/cm-hangmeas-loader.svg +50 -0
- data/app/assets/images/cm-hangmeas-success.svg +51 -0
- data/app/assets/javascripts/spree_cm_commissioner/backend.js +1 -0
- data/app/assets/javascripts/spree_cm_commissioner/tenant_payment_icon_fields.js +65 -0
- data/app/assets/stylesheets/spree_cm_commissioner/backend/commissioner_admin.css.scss +1 -0
- data/app/assets/stylesheets/spree_cm_commissioner/backend/tenant_payment_icon_fields.scss +60 -0
- data/app/controllers/concerns/spree_cm_commissioner/content_cachable.rb +2 -1
- data/app/controllers/spree/admin/event_blazer_queries_controller.rb +11 -4
- data/app/controllers/spree/admin/tenants_controller.rb +8 -0
- data/app/controllers/spree/admin/trip_blazer_queries_controller.rb +35 -0
- data/app/controllers/spree/api/v2/organizer/invite_crews_controller.rb +33 -0
- data/app/controllers/spree/api/v2/storefront/dynamic_field_options_controller.rb +5 -1
- data/app/controllers/spree/api/v2/storefront/guests_controller.rb +31 -5
- data/app/controllers/spree/api/v2/tenant/checkout_controller.rb +2 -0
- data/app/controllers/spree_cm_commissioner/admin/variants_controller_decorator.rb +1 -1
- data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb +11 -0
- data/app/controllers/spree_cm_commissioner/well_known_controller.rb +31 -17
- data/app/interactors/spree_cm_commissioner/apple_app_site_association_fetcher.rb +27 -0
- data/app/interactors/spree_cm_commissioner/asset_links_fetcher.rb +27 -0
- data/app/interactors/spree_cm_commissioner/crew_invite_link_handler.rb +48 -0
- data/app/interactors/spree_cm_commissioner/telegram_chats_auto_finder.rb +144 -0
- data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +5 -1
- data/app/models/concerns/spree_cm_commissioner/event_check_in_flowable.rb +30 -0
- data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +2 -1
- data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +4 -0
- data/app/models/spree_cm_commissioner/blazer_queryable.rb +8 -0
- data/app/models/spree_cm_commissioner/crew_invite_link.rb +4 -0
- data/app/models/spree_cm_commissioner/dynamic_field.rb +17 -3
- data/app/models/spree_cm_commissioner/dynamic_field_option.rb +7 -1
- data/app/models/spree_cm_commissioner/guest.rb +1 -1
- data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +21 -3
- data/app/models/spree_cm_commissioner/invite.rb +4 -6
- data/app/models/spree_cm_commissioner/invite_team.rb +3 -1
- data/app/models/spree_cm_commissioner/pin_code.rb +3 -2
- data/app/models/spree_cm_commissioner/pin_code_telegram.rb +28 -0
- data/app/models/spree_cm_commissioner/place.rb +0 -1
- data/app/models/spree_cm_commissioner/product_decorator.rb +0 -2
- data/app/models/spree_cm_commissioner/promotion_category_decorator.rb +11 -0
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +5 -2
- data/app/models/spree_cm_commissioner/telegram_chat.rb +6 -0
- data/app/models/spree_cm_commissioner/trip.rb +9 -13
- data/app/models/spree_cm_commissioner/trip_stop.rb +5 -23
- data/app/models/spree_cm_commissioner/user_taxon.rb +1 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +9 -9
- data/app/models/spree_cm_commissioner/vendor_place.rb +8 -3
- data/app/overrides/spree/admin/taxons/_form/check_in_flows.html.erb.deface +18 -0
- data/app/overrides/spree/admin/variants/_form/kyc_field.html.erb.deface +2 -2
- data/app/queries/spree_cm_commissioner/guest_searcher_query.rb +45 -3
- data/app/queries/spree_cm_commissioner/trip_query.rb +56 -27
- data/app/serializers/spree/v2/organizer/invite_crews_serializer.rb +11 -0
- data/app/serializers/spree_cm_commissioner/v2/operator/dashboard_crew_event_serializer.rb +4 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/dynamic_field_option_serializer.rb +1 -3
- data/app/serializers/spree_cm_commissioner/v2/storefront/dynamic_field_serializer.rb +1 -3
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_dynamic_field_serializer.rb +2 -1
- data/app/services/spree_cm_commissioner/organizer/export_guest_csv_service.rb +23 -15
- data/app/views/blazer/queries/_content.html.erb +8 -0
- data/app/views/spree/admin/tenants/_form.html.erb +109 -42
- data/config/initializers/spree_permitted_attributes.rb +2 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +4 -1
- data/db/migrate/20250616084219_add_description_to_cm_vendor_place.rb +5 -0
- data/db/migrate/20250630103536_create_cm_telegram_chats.rb +13 -0
- data/db/migrate/20250701093203_add_configurations_to_cm_dynamic_field.rb +6 -0
- data/db/migrate/20250702091305_add_dynamic_field_option_to_guest_dynamic_field.rb +5 -0
- data/db/migrate/20250702091935_add_status_to_dynamic_field_option.rb +5 -0
- data/db/migrate/20250707032008_add_vendor_id_to_spree_category.rb +7 -0
- data/db/migrate/20250711092937_add_position_to_cm_dynamic_fields.rb +11 -0
- data/db/migrate/20250711093045_add_position_to_cm_dynamic_field_options.rb +11 -0
- data/db/migrate/20250714121508_rename_cm_taxon_blazer_query_to_blazer_queryables.rb +7 -0
- data/db/migrate/20250714124323_make_cm_blazer_queryables_polymorphic.rb +19 -0
- data/db/migrate/20250716022821_add_location_reference_to_cm_vendor_places.rb +5 -0
- data/db/migrate/20250716031743_drop_table_cm_vendor_stops.rb +5 -0
- data/db/migrate/20250717023824_add_vendor_reference_to_cm_trips.rb +5 -0
- data/db/migrate/20250717041414_add_location_place_reference_to_cm_trip_stops.rb +5 -0
- data/db/migrate/20250717042539_rename_cm_trip_stops_stop_id_column_to_stop_place_id.rb +7 -0
- data/db/migrate/20250717042707_rename_cm_trips_origin_and_destination_to_origin_place_and_destination_place.rb +11 -0
- data/lib/spree_cm_commissioner/test_helper/factories/block_factory.rb +9 -3
- data/lib/spree_cm_commissioner/test_helper/factories/seat_layout_factory.rb +6 -3
- data/lib/spree_cm_commissioner/test_helper/factories/seat_section_factory.rb +11 -3
- data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +3 -2
- data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +10 -0
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +5 -0
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +13 -1
- data/lib/spree_cm_commissioner/trip_query_result.rb +8 -0
- data/lib/spree_cm_commissioner/trip_result.rb +5 -3
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +39 -5
- data/app/models/spree_cm_commissioner/taxon_blazer_query.rb +0 -8
- data/app/models/spree_cm_commissioner/vendor_stop.rb +0 -9
- data/app/queries/spree_cm_commissioner/vendor_stop_place_query.rb +0 -54
@@ -3,18 +3,42 @@ module SpreeCmCommissioner
|
|
3
3
|
before_action :identify_request_source
|
4
4
|
rescue_from StandardError, with: :handle_error
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
def assetlinks_json
|
7
|
+
render_platform_link(:android)
|
8
|
+
end
|
9
|
+
|
10
|
+
def apple_app_site_association
|
11
|
+
render_platform_link(:ios)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def render_platform_link(platform)
|
17
|
+
fetcher_class = platform_link_finder(platform)
|
18
|
+
|
19
|
+
result = fetcher_class.call(
|
20
|
+
store: @store,
|
21
|
+
tenant: @tenant,
|
22
|
+
refreshed: params[:_refreshed].present?
|
23
|
+
)
|
24
|
+
|
25
|
+
if result.success? && result.contents.present?
|
26
|
+
render json: result.contents
|
12
27
|
else
|
13
28
|
render_not_found
|
14
29
|
end
|
15
30
|
end
|
16
31
|
|
17
|
-
|
32
|
+
def platform_link_finder(platform)
|
33
|
+
case platform
|
34
|
+
when :android
|
35
|
+
SpreeCmCommissioner::AssetLinksFetcher
|
36
|
+
when :ios
|
37
|
+
SpreeCmCommissioner::AppleAppSiteAssociationFetcher
|
38
|
+
else
|
39
|
+
render_not_found
|
40
|
+
end
|
41
|
+
end
|
18
42
|
|
19
43
|
def identify_request_source
|
20
44
|
host = params[:original_host].presence || request.host.downcase
|
@@ -25,16 +49,6 @@ module SpreeCmCommissioner
|
|
25
49
|
@tenant = result.tenant
|
26
50
|
end
|
27
51
|
|
28
|
-
def render_preference(preference_key)
|
29
|
-
if @store&.send(preference_key).present?
|
30
|
-
render json: @store.send(preference_key)
|
31
|
-
elsif @tenant&.send(preference_key).present?
|
32
|
-
render json: @tenant.send(preference_key)
|
33
|
-
else
|
34
|
-
render_not_found
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
52
|
def render_not_found
|
39
53
|
render json: { error: 'Not found' }, status: :not_found
|
40
54
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class AppleAppSiteAssociationFetcher < BaseInteractor
|
3
|
+
delegate :store, :tenant, :refreshed, to: :context
|
4
|
+
|
5
|
+
def call
|
6
|
+
key = cache_key
|
7
|
+
|
8
|
+
CmAppLogger.log(label: 'LOOKING_UP_CACHE', data: { cache_key: key })
|
9
|
+
|
10
|
+
Rails.cache.delete(key) if refreshed
|
11
|
+
|
12
|
+
context.contents = Rails.cache.fetch(key) do
|
13
|
+
CmAppLogger.log(label: 'CACHE_MISS', data: { cache_key: key })
|
14
|
+
resource.preferred_apple_app_site_association
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache_key
|
19
|
+
updated_at = tenant&.updated_at || store&.updated_at
|
20
|
+
"well_known:apple-app-site-association:#{resource.class.name}:#{resource.id}:#{updated_at}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def resource
|
24
|
+
tenant || store
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class AssetLinksFetcher < BaseInteractor
|
3
|
+
delegate :store, :tenant, :refreshed, to: :context
|
4
|
+
|
5
|
+
def call
|
6
|
+
key = cache_key
|
7
|
+
|
8
|
+
CmAppLogger.log(label: 'LOOKING_UP_CACHE', data: { cache_key: key })
|
9
|
+
|
10
|
+
Rails.cache.delete(key) if refreshed
|
11
|
+
|
12
|
+
context.contents = Rails.cache.fetch(key) do
|
13
|
+
CmAppLogger.log(label: 'CACHE_MISS', data: { cache_key: key })
|
14
|
+
resource.preferred_assetlinks
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache_key
|
19
|
+
updated_at = tenant&.updated_at || store&.updated_at
|
20
|
+
"well_known:assetlinks.json:#{resource.class.name}:#{resource.id}:#{updated_at}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def resource
|
24
|
+
tenant || store
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class CrewInviteLinkHandler < BaseInteractor
|
3
|
+
delegate :params, to: :context
|
4
|
+
|
5
|
+
def call
|
6
|
+
validate_data
|
7
|
+
assign_role_to_user
|
8
|
+
create_user_event
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def validate_data
|
14
|
+
if invite.nil?
|
15
|
+
context.fail!(message: I18n.t('invite.url_not_found'))
|
16
|
+
else
|
17
|
+
context.invite = invite
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def assign_role_to_user
|
22
|
+
return if user.operator?
|
23
|
+
|
24
|
+
role = Spree::Role.find_by(name: 'operator')
|
25
|
+
role_user = Spree::RoleUser.new(user_id: user.id, role_id: role.id)
|
26
|
+
role_user.save
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_user_event
|
30
|
+
@user_event = SpreeCmCommissioner::UserEvent.new(
|
31
|
+
taxon_id: invite.taxon_id,
|
32
|
+
user_id: user.id
|
33
|
+
)
|
34
|
+
|
35
|
+
return if @user_event.save
|
36
|
+
|
37
|
+
context.fail!(message: I18n.t('invite.already_invited'))
|
38
|
+
end
|
39
|
+
|
40
|
+
def invite
|
41
|
+
@invite ||= SpreeCmCommissioner::Invite.find_by(token: params[:id])
|
42
|
+
end
|
43
|
+
|
44
|
+
def user
|
45
|
+
@user ||= Spree::User.find(params[:user_id])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class TelegramChatsAutoFinder < BaseInteractor
|
3
|
+
delegate :contact, :pin_code, to: :context
|
4
|
+
|
5
|
+
def call
|
6
|
+
context.chats = []
|
7
|
+
context.verified_chat_ids = []
|
8
|
+
|
9
|
+
updates = fetch_updates
|
10
|
+
|
11
|
+
if updates['result'].any?
|
12
|
+
updates['result'].sort_by { |u| u['update_id'] }.reverse.each do |update|
|
13
|
+
handle_message_update(update) if update['message']
|
14
|
+
handle_channel_post_update(update) if update['channel_post']
|
15
|
+
handle_chat_member_update(update) if update['my_chat_member']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
context.fail!(message: 'No pin codes verified') if context.verified_chat_ids.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def handle_chat_member_update(update)
|
24
|
+
chat = update.dig('my_chat_member', 'chat')
|
25
|
+
return unless chat
|
26
|
+
|
27
|
+
chat_data = {
|
28
|
+
id: chat['id'],
|
29
|
+
title: chat['title'] || 'Unnamed Chat',
|
30
|
+
type: chat['type']
|
31
|
+
}
|
32
|
+
|
33
|
+
return if context.chats.any? { |c| c[:id] == chat_data[:id] }
|
34
|
+
|
35
|
+
db_chat = SpreeCmCommissioner::TelegramChat.find_or_initialize_by(chat_id: chat_data[:id])
|
36
|
+
is_new_chat = db_chat.new_record?
|
37
|
+
db_chat.update(chat_title: chat_data[:title], chat_type: chat_data[:type])
|
38
|
+
context.chats << chat_data
|
39
|
+
message = "👋Hello! This is Bookme+ bot.\n\nPlease enter the 6-digit PIN code displayed in your portal."
|
40
|
+
send_message(chat_data[:id], message) if is_new_chat
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_channel_post_update(update)
|
44
|
+
message = update['channel_post']
|
45
|
+
process_pin_code_message(message)
|
46
|
+
end
|
47
|
+
|
48
|
+
def handle_message_update(update)
|
49
|
+
message = update['message']
|
50
|
+
process_pin_code_message(message)
|
51
|
+
end
|
52
|
+
|
53
|
+
def process_pin_code_message(message)
|
54
|
+
chat_id = message.dig('chat', 'id')
|
55
|
+
text = message['text']&.strip
|
56
|
+
|
57
|
+
return unless valid_pin_code?(text, chat_id)
|
58
|
+
|
59
|
+
pin_code_record = find_pin_code_record
|
60
|
+
return unless pin_code_record
|
61
|
+
|
62
|
+
vendor = find_vendor
|
63
|
+
return unless vendor
|
64
|
+
|
65
|
+
chat = update_telegram_chat(message, chat_id)
|
66
|
+
return unless chat
|
67
|
+
|
68
|
+
handle_successful_verification(chat, vendor, pin_code_record)
|
69
|
+
end
|
70
|
+
|
71
|
+
def valid_pin_code?(text, chat_id)
|
72
|
+
text && chat_id && text == pin_code
|
73
|
+
end
|
74
|
+
|
75
|
+
def find_pin_code_record
|
76
|
+
pin_code_record = SpreeCmCommissioner::PinCodeTelegram.find_by(code: pin_code, contact: contact)
|
77
|
+
unless pin_code_record
|
78
|
+
context.fail!(message: "Pin code #{pin_code} not found for contact #{contact}")
|
79
|
+
return nil
|
80
|
+
end
|
81
|
+
pin_code_record
|
82
|
+
end
|
83
|
+
|
84
|
+
def find_vendor
|
85
|
+
vendor = Spree::Vendor.find_by(id: contact)
|
86
|
+
unless vendor
|
87
|
+
context.fail!(message: "Vendor not found for contact #{contact}")
|
88
|
+
return nil
|
89
|
+
end
|
90
|
+
vendor
|
91
|
+
end
|
92
|
+
|
93
|
+
def update_telegram_chat(message, chat_id)
|
94
|
+
chat = SpreeCmCommissioner::TelegramChat.find_or_initialize_by(chat_id: chat_id)
|
95
|
+
unless chat.update(
|
96
|
+
chat_title: message.dig('chat', 'title') || 'Unnamed Chat',
|
97
|
+
chat_type: message.dig('chat', 'type')
|
98
|
+
)
|
99
|
+
context.fail!(message: "Failed to update chat: #{chat.errors.full_messages.join(', ')}")
|
100
|
+
return nil
|
101
|
+
end
|
102
|
+
|
103
|
+
context.chats << { id: chat.chat_id, title: chat.chat_title, type: chat.chat_type }
|
104
|
+
chat
|
105
|
+
end
|
106
|
+
|
107
|
+
def handle_successful_verification(chat, vendor, pin_code_record)
|
108
|
+
return if chat.vendor && chat.vendor != vendor
|
109
|
+
|
110
|
+
update_vendor_preferences(vendor, chat)
|
111
|
+
chat.update(active: true, vendor: vendor)
|
112
|
+
expire_pin_code(pin_code_record)
|
113
|
+
send_success_message(chat.chat_id, vendor.name)
|
114
|
+
context.verified_chat_ids << chat.chat_id
|
115
|
+
end
|
116
|
+
|
117
|
+
def update_vendor_preferences(vendor, chat)
|
118
|
+
vendor.update(
|
119
|
+
preferred_telegram_chat_id: chat.chat_id,
|
120
|
+
preferred_telegram_chat_name: chat.chat_title,
|
121
|
+
preferred_telegram_chat_type: chat.chat_type
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
def expire_pin_code(pin_code_record)
|
126
|
+
pin_code_record.expire
|
127
|
+
pin_code_record.save
|
128
|
+
end
|
129
|
+
|
130
|
+
def send_success_message(chat_id, vendor_name)
|
131
|
+
message = "✅ PIN code verified successfully! This chat is now connected to <b>#{vendor_name}</b>.\n\n" \
|
132
|
+
'All completed orders will be sent to this chat.'
|
133
|
+
send_message(chat_id, message)
|
134
|
+
end
|
135
|
+
|
136
|
+
def fetch_updates
|
137
|
+
::Telegram.bot.get_updates
|
138
|
+
end
|
139
|
+
|
140
|
+
def send_message(chat_id, text)
|
141
|
+
::Telegram.bot.send_message(chat_id: chat_id, text: text, parse_mode: 'HTML')
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -93,6 +93,10 @@ module SpreeCmCommissioner
|
|
93
93
|
context.fail!(message: "User creation failed: #{context.user.errors.full_messages.join(', ')}")
|
94
94
|
end
|
95
95
|
|
96
|
+
def disable_services
|
97
|
+
'bus,ferry'
|
98
|
+
end
|
99
|
+
|
96
100
|
def construct_data
|
97
101
|
user = context.user
|
98
102
|
|
@@ -101,7 +105,7 @@ module SpreeCmCommissioner
|
|
101
105
|
name: user.full_name,
|
102
106
|
phone: user.phone_number,
|
103
107
|
email: user.email,
|
104
|
-
webUrl: "#{Spree::Store.default.formatted_url}/vattanac_bank_web_app?session_id=#{session_id}"
|
108
|
+
webUrl: "#{Spree::Store.default.formatted_url}/vattanac_bank_web_app?session_id=#{session_id}&ds=#{CGI.escape(disable_services)}"
|
105
109
|
}
|
106
110
|
|
107
111
|
json_data = raw_data.to_json
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
module EventCheckInFlowable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
preference :group_check_in_enabled, :boolean, default: true
|
7
|
+
preference :individual_check_in_enabled, :boolean, default: true
|
8
|
+
|
9
|
+
before_validation :validate_at_least_one_check_in_flow_presence
|
10
|
+
end
|
11
|
+
|
12
|
+
def check_in_flows
|
13
|
+
flows = []
|
14
|
+
flows << 'group' if group_check_in_enabled?
|
15
|
+
flows << 'individual' if individual_check_in_enabled?
|
16
|
+
flows
|
17
|
+
end
|
18
|
+
|
19
|
+
def group_check_in_enabled? = preferred_group_check_in_enabled
|
20
|
+
def individual_check_in_enabled? = preferred_individual_check_in_enabled
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def validate_at_least_one_check_in_flow_presence
|
25
|
+
return if check_in_flows.any?
|
26
|
+
|
27
|
+
errors.add(:check_in_flows, 'must have at least one flow selected')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -40,7 +40,8 @@ module SpreeCmCommissioner
|
|
40
40
|
'bib-zerofill' => 'integer',
|
41
41
|
'bib-display-prefix' => 'boolean',
|
42
42
|
'bib-pre-generation-on-create' => 'boolean',
|
43
|
-
'seat-number-positions' => 'array'
|
43
|
+
'seat-number-positions' => 'array',
|
44
|
+
'seat-type' => 'string'
|
44
45
|
}.freeze
|
45
46
|
|
46
47
|
included do
|
@@ -5,6 +5,10 @@ module SpreeCmCommissioner
|
|
5
5
|
included do
|
6
6
|
preference :assetlinks, :string, default: ''
|
7
7
|
preference :apple_app_site_association, :string, default: ''
|
8
|
+
preference :payment_checkout_image, :string, default: ''
|
9
|
+
preference :payment_failed_image, :string, default: ''
|
10
|
+
preference :payment_success_image, :string, default: ''
|
11
|
+
preference :payment_loader, :string, default: ''
|
8
12
|
end
|
9
13
|
end
|
10
14
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class BlazerQueryable < SpreeCmCommissioner::Base
|
3
|
+
belongs_to :queryable, polymorphic: true
|
4
|
+
belongs_to :blazer_query, class_name: 'Blazer::Query'
|
5
|
+
|
6
|
+
validates :queryable_id, uniqueness: { scope: %i[queryable_type blazer_query_id], message: I18n.t('event_blazer_queries.fail') }
|
7
|
+
end
|
8
|
+
end
|
@@ -1,20 +1,34 @@
|
|
1
1
|
module SpreeCmCommissioner
|
2
2
|
class DynamicField < SpreeCmCommissioner::Base
|
3
|
+
acts_as_list column: :position, scope: :vendor_id
|
4
|
+
|
3
5
|
belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
|
4
6
|
|
5
7
|
has_many :dynamic_field_options, class_name: 'SpreeCmCommissioner::DynamicFieldOption', dependent: :destroy
|
6
8
|
has_many :guest_dynamic_fields, class_name: 'SpreeCmCommissioner::GuestDynamicField', dependent: :destroy
|
7
9
|
has_many :product_dynamic_fields, class_name: 'SpreeCmCommissioner::ProductDynamicField', dependent: :destroy
|
8
10
|
|
9
|
-
enum data_type: {
|
11
|
+
enum data_type: { text: 0, number: 1, boolean: 2, checkbox: 3, radio: 4, selection: 5, textarea: 6 }
|
10
12
|
|
11
13
|
accepts_nested_attributes_for :dynamic_field_options, allow_destroy: true, reject_if: :all_blank
|
12
14
|
|
13
15
|
validates :label, presence: true
|
14
16
|
validates :data_type, presence: true
|
17
|
+
validates :configurations, presence: true, allow_blank: false
|
18
|
+
|
19
|
+
def requires_dynamic_field_options?
|
20
|
+
checkbox? || radio? || selection?
|
21
|
+
end
|
22
|
+
|
23
|
+
def multiple_select
|
24
|
+
return nil unless selection?
|
25
|
+
|
26
|
+
configurations['multiple_select'] == true
|
27
|
+
end
|
15
28
|
|
16
|
-
def
|
17
|
-
|
29
|
+
def multiple_select=(value)
|
30
|
+
self.configurations ||= {}
|
31
|
+
self.configurations['multiple_select'] = ActiveModel::Type::Boolean.new.cast(value)
|
18
32
|
end
|
19
33
|
end
|
20
34
|
end
|
@@ -1,8 +1,14 @@
|
|
1
1
|
module SpreeCmCommissioner
|
2
2
|
class DynamicFieldOption < SpreeCmCommissioner::Base
|
3
|
+
acts_as_list column: :position, scope: :dynamic_field_id
|
4
|
+
|
3
5
|
belongs_to :dynamic_field, class_name: 'SpreeCmCommissioner::DynamicField', optional: false
|
6
|
+
has_many :guest_dynamic_fields, class_name: 'SpreeCmCommissioner::GuestDynamicField', dependent: :restrict_with_error
|
7
|
+
|
8
|
+
enum status: { active: 0, draft: 1, archived: 2 }
|
4
9
|
|
5
10
|
validates :value, presence: true, uniqueness: { scope: :dynamic_field_id }
|
11
|
+
validates :status, presence: true
|
6
12
|
validate :parent_field_must_be_selection_type
|
7
13
|
|
8
14
|
private
|
@@ -10,7 +16,7 @@ module SpreeCmCommissioner
|
|
10
16
|
def parent_field_must_be_selection_type
|
11
17
|
return unless dynamic_field
|
12
18
|
|
13
|
-
return if dynamic_field.
|
19
|
+
return if dynamic_field.requires_dynamic_field_options?
|
14
20
|
|
15
21
|
errors.add(:dynamic_field, 'must be a selection type field')
|
16
22
|
end
|
@@ -61,7 +61,7 @@ module SpreeCmCommissioner
|
|
61
61
|
validates :seat_number, uniqueness: { scope: :event_id }, allow_nil: true, if: -> { event_id.present? }
|
62
62
|
validates :bib_index, uniqueness: true, allow_nil: true
|
63
63
|
|
64
|
-
self.whitelisted_ransackable_associations = %w[id_card event]
|
64
|
+
self.whitelisted_ransackable_associations = %w[id_card event line_item]
|
65
65
|
self.whitelisted_ransackable_attributes = %w[first_name last_name phone_number gender contact occupation_id card_type created_at check_in_status]
|
66
66
|
|
67
67
|
def self.csv_importable_columns
|
@@ -2,9 +2,11 @@ module SpreeCmCommissioner
|
|
2
2
|
class GuestDynamicField < SpreeCmCommissioner::Base
|
3
3
|
belongs_to :guest, class_name: 'SpreeCmCommissioner::Guest', optional: false
|
4
4
|
belongs_to :dynamic_field, class_name: 'SpreeCmCommissioner::DynamicField', optional: false
|
5
|
+
belongs_to :dynamic_field_option, class_name: 'SpreeCmCommissioner::DynamicFieldOption', optional: true
|
5
6
|
|
6
7
|
validates :value, presence: true
|
7
8
|
validate :validate_value_format, if: -> { value.present? && dynamic_field.present? }
|
9
|
+
validate :validate_option_reference, if: -> { dynamic_field_option.present? }
|
8
10
|
|
9
11
|
private
|
10
12
|
|
@@ -12,14 +14,30 @@ module SpreeCmCommissioner
|
|
12
14
|
return if dynamic_field.blank?
|
13
15
|
|
14
16
|
case dynamic_field.data_type.to_s
|
15
|
-
when '
|
17
|
+
when 'number'
|
16
18
|
errors.add(:value, 'must be a number') unless value.to_s.match?(/\A\d+\z/)
|
17
19
|
when 'boolean'
|
18
|
-
errors.add(:value, 'must be true or false') unless %w[true false
|
19
|
-
when 'checkbox', 'radio'
|
20
|
+
errors.add(:value, 'must be true or false') unless %w[true false].include?(value.to_s.downcase)
|
21
|
+
when 'checkbox', 'radio', 'selection'
|
22
|
+
validate_option_value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate_option_value
|
27
|
+
if dynamic_field_option.present?
|
28
|
+
self.value ||= dynamic_field_option.value
|
29
|
+
else
|
20
30
|
valid_options = dynamic_field.dynamic_field_options.pluck(:id).map(&:to_s)
|
21
31
|
errors.add(:value, 'is not a valid option') unless valid_options.include?(value)
|
22
32
|
end
|
23
33
|
end
|
34
|
+
|
35
|
+
def validate_option_reference
|
36
|
+
return if dynamic_field.blank? || dynamic_field_option.blank?
|
37
|
+
|
38
|
+
return if dynamic_field_option.dynamic_field_id == dynamic_field_id
|
39
|
+
|
40
|
+
errors.add(:dynamic_field_option, 'must belong to the same dynamic field')
|
41
|
+
end
|
24
42
|
end
|
25
43
|
end
|
@@ -5,16 +5,14 @@ module SpreeCmCommissioner
|
|
5
5
|
has_many :invite_user_events, class_name: 'SpreeCmCommissioner::InviteUserEvent', dependent: :destroy
|
6
6
|
scope :active, -> { where('expires_at > ?', Time.current) }
|
7
7
|
|
8
|
-
after_create :set_expiration
|
9
|
-
|
10
|
-
def set_expiration
|
11
|
-
update(expires_at: 3.days.from_now)
|
12
|
-
end
|
13
|
-
|
14
8
|
def invite_url
|
15
9
|
"#{Spree::Store.default.formatted_url}/invite/#{token}?utm_source=email"
|
16
10
|
end
|
17
11
|
|
12
|
+
def invite_crew_link
|
13
|
+
"#{Spree::Store.default.formatted_url}/invite_crews/#{token}"
|
14
|
+
end
|
15
|
+
|
18
16
|
def url_valid?
|
19
17
|
expires_at.present? && expires_at > Time.current
|
20
18
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module SpreeCmCommissioner
|
2
2
|
class InviteTeam < SpreeCmCommissioner::Base
|
3
|
+
validates :email, presence: true, uniqueness: { scope: :vendor_id, message: I18n.t('invite.already_invited') }
|
4
|
+
|
3
5
|
belongs_to :user, class_name: 'Spree::User', optional: true
|
4
6
|
belongs_to :inviter, class_name: 'Spree::User'
|
5
7
|
belongs_to :vendor, class_name: 'Spree::Vendor'
|
@@ -34,7 +36,7 @@ module SpreeCmCommissioner
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def invitation_link
|
37
|
-
"
|
39
|
+
"#{ENV.fetch('ORGANIZER_URL')}/invite_teams/#{token}?utm_source=email"
|
38
40
|
end
|
39
41
|
|
40
42
|
def url_valid?
|
@@ -2,7 +2,7 @@ module SpreeCmCommissioner
|
|
2
2
|
class PinCode < SpreeCmCommissioner::Base
|
3
3
|
has_secure_token
|
4
4
|
|
5
|
-
enum contact_type: { 'phone_number' => 0, 'email' => 1 }
|
5
|
+
enum contact_type: { 'phone_number' => 0, 'email' => 1, 'telegram' => 2 }
|
6
6
|
|
7
7
|
validates :code, length: { maximum: 6 }
|
8
8
|
validates :contact, presence: true
|
@@ -130,7 +130,8 @@ module SpreeCmCommissioner
|
|
130
130
|
'SpreeCmCommissioner::PinCodeMobileConfirm' => I18n.t('pincode.readable_type.confirmation_code'),
|
131
131
|
'SpreeCmCommissioner::PinCodeUpdateUserLogin' => I18n.t('pincode.readable_type.update_user_login'),
|
132
132
|
'SpreeCmCommissioner::PinCodeEmailConfirm' => I18n.t('pincode.readable_type.confirmation_code'),
|
133
|
-
'SpreeCmCommissioner::OtpCode' => I18n.t('pincode.readable_type.otp_code')
|
133
|
+
'SpreeCmCommissioner::OtpCode' => I18n.t('pincode.readable_type.otp_code'),
|
134
|
+
'SpreeCmCommissioner::PinCodeTelegram' => I18n.t('pincode.readable_type.telegram_verification')
|
134
135
|
}
|
135
136
|
end
|
136
137
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class PinCodeTelegram < PinCode
|
3
|
+
before_validation :set_telegram_attrs, on: :create
|
4
|
+
validates :contact, presence: true, numericality: { only_integer: true }, if: :telegram?
|
5
|
+
|
6
|
+
belongs_to :vendor, class_name: 'Spree::Vendor', foreign_key: 'contact', primary_key: 'id'
|
7
|
+
|
8
|
+
def telegram?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def expires_in_seconds
|
13
|
+
5 * 60
|
14
|
+
end
|
15
|
+
|
16
|
+
def expired?
|
17
|
+
expired_at < Time.zone.now
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def set_telegram_attrs
|
23
|
+
self.contact_type = :telegram
|
24
|
+
self.type = 'SpreeCmCommissioner::PinCodeTelegram'
|
25
|
+
self.expired_at = Time.zone.now + expires_in_seconds unless expired_at
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -15,7 +15,6 @@ module SpreeCmCommissioner
|
|
15
15
|
has_many :products, through: :product_places
|
16
16
|
|
17
17
|
has_many :children, -> { order(:lft) }, class_name: 'SpreeCmCommissioner::Place', foreign_key: :parent_id, dependent: :destroy
|
18
|
-
has_many :vendor_stops, class_name: 'SpreeCmCommissioner::VendorStop', dependent: :destroy, foreign_key: :stop_id
|
19
18
|
|
20
19
|
has_many :taxon_places, class_name: 'SpreeCmCommissioner::TaxonPlace', dependent: :destroy
|
21
20
|
has_many :taxons, through: :taxon_places
|
@@ -75,8 +75,6 @@ module SpreeCmCommissioner
|
|
75
75
|
|
76
76
|
base.enum purchasable_on: { both: 0, web: 1, app: 2 }
|
77
77
|
|
78
|
-
base.accepts_nested_attributes_for :trip, allow_destroy: true
|
79
|
-
|
80
78
|
base.multi_tenant :tenant, class_name: 'SpreeCmCommissioner::Tenant'
|
81
79
|
base.before_save :set_tenant
|
82
80
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
module PromotionCategoryDecorator
|
3
|
+
def self.prepended(base)
|
4
|
+
base.belongs_to :vendor, class_name: 'Spree::Vendor', optional: true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
unless Spree::PromotionCategory.included_modules.include?(SpreeCmCommissioner::PromotionCategoryDecorator)
|
10
|
+
Spree::PromotionCategory.prepend(SpreeCmCommissioner::PromotionCategoryDecorator)
|
11
|
+
end
|
@@ -3,6 +3,7 @@ module SpreeCmCommissioner
|
|
3
3
|
def self.prepended(base) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
4
4
|
base.include SpreeCmCommissioner::TaxonKind
|
5
5
|
base.include SpreeCmCommissioner::ParticipationTypeBitwise
|
6
|
+
base.include SpreeCmCommissioner::EventCheckInFlowable
|
6
7
|
|
7
8
|
base.preference :background_color, :string
|
8
9
|
base.preference :foreground_color, :string
|
@@ -55,14 +56,16 @@ module SpreeCmCommissioner
|
|
55
56
|
base.has_many :crew_invites, class_name: 'SpreeCmCommissioner::CrewInvite', dependent: :destroy
|
56
57
|
base.has_many :invite_user_events, through: :user_events, class_name: 'SpreeCmCommissioner::InviteUserEvent'
|
57
58
|
|
59
|
+
base.has_many :crew_invite_links, class_name: 'SpreeCmCommissioner::CrewInviteLink', dependent: :destroy
|
60
|
+
|
58
61
|
base.has_many :invite_guests, class_name: 'SpreeCmCommissioner::InviteGuest', foreign_key: :event_id
|
59
62
|
|
60
63
|
base.has_many :line_items, through: :products
|
61
64
|
base.has_many :event_line_items, class_name: 'Spree::LineItem', foreign_key: :event_id
|
62
65
|
base.has_many :event_products, class_name: 'Spree::Product', foreign_key: :event_id
|
63
66
|
|
64
|
-
base.has_many :event_blazer_queries, class_name: 'SpreeCmCommissioner::
|
65
|
-
base.has_many :blazer_queries, through: :event_blazer_queries, class_name: 'Blazer::Query'
|
67
|
+
base.has_many :event_blazer_queries, as: :queryable, class_name: 'SpreeCmCommissioner::BlazerQueryable'
|
68
|
+
base.has_many :blazer_queries, through: :event_blazer_queries, source: :blazer_query, class_name: 'Blazer::Query'
|
66
69
|
|
67
70
|
base.has_many :taxon_option_types, class_name: 'SpreeCmCommissioner::TaxonOptionType'
|
68
71
|
base.has_many :taxon_option_values, class_name: 'SpreeCmCommissioner::TaxonOptionValue'
|