spree_cm_commissioner 2.8.3.pre.pre11 → 2.8.3.pre.pre12
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/Gemfile.lock +1 -1
- data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +3 -1
- data/app/controllers/spree/api/v2/tenant/transit/draft_orders_controller.rb +3 -1
- data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +62 -5
- data/app/jobs/spree_cm_commissioner/telegram_gateway/pin_code_sender_job.rb +18 -0
- data/app/models/concerns/spree_cm_commissioner/line_item_open_dated_trippable.rb +50 -7
- data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +0 -1
- data/app/models/spree_cm_commissioner/pin_code.rb +62 -9
- data/app/models/spree_cm_commissioner/product_decorator.rb +6 -0
- data/app/models/spree_cm_commissioner/show.rb +0 -4
- data/app/models/spree_cm_commissioner/trip.rb +2 -12
- data/app/models/spree_cm_commissioner/voting_contestant.rb +0 -1
- data/app/queries/spree_cm_commissioner/single_leg_trips_query.rb +24 -3
- data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +3 -1
- data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +1 -1
- data/app/serializers/spree/v2/tenant/show_serializer.rb +1 -2
- data/app/serializers/spree_cm_commissioner/v2/storefront/pin_code_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/ticket_transfer_minimal_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_query_result_serializer.rb +3 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +2 -1
- data/app/services/spree_cm_commissioner/open_dated_trips/redeem.rb +80 -36
- data/app/services/spree_cm_commissioner/telegram_gateway/pin_code_sender.rb +99 -0
- data/app/services/spree_cm_commissioner/transit_order/create.rb +48 -14
- data/app/services/spree_cm_commissioner/trips/add_ons/create.rb +124 -0
- data/app/services/spree_cm_commissioner/trips/add_ons/update_price.rb +36 -0
- data/app/services/spree_cm_commissioner/trips/create_multi_leg.rb +1 -2
- data/app/services/spree_cm_commissioner/trips/variants/create.rb +1 -2
- data/app/services/spree_cm_commissioner/voting_sessions/finalize.rb +28 -29
- data/app/services/telegram_gateway_adapter/client.rb +124 -0
- data/config/locales/en.yml +5 -6
- data/config/locales/km.yml +15 -48
- data/db/migrate/20260610000001_add_delivery_channel_to_cm_pin_codes.rb +6 -0
- data/db/migrate/20260610000001_drop_is_open_dated_from_cm_trips.rb +6 -0
- data/db/migrate/20260614000001_change_code_limit_in_cm_pin_codes.rb +5 -0
- data/db/migrate/20260615000002_add_index_to_code_on_cm_pin_codes.rb +5 -0
- data/lib/spree_cm_commissioner/transit/trip_form.rb +0 -13
- data/lib/spree_cm_commissioner/trip_query_result.rb +37 -6
- data/lib/spree_cm_commissioner/trip_result.rb +6 -7
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +11 -7
- data/app/serializers/spree/v2/tenant/campaign_serializer.rb +0 -13
- data/app/services/spree_cm_commissioner/advertisements/sorted_advertisements.rb +0 -61
- data/app/services/spree_cm_commissioner/advertisements/update_campaign_sorted_ads_ids.rb +0 -33
- data/app/services/spree_cm_commissioner/trips/create_open_dated_trip.rb +0 -98
- data/app/services/spree_cm_commissioner/trips/update_open_dated_trip.rb +0 -67
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc7986faabd73e3ae1e70e3a80e14f59119301779f847a112d585d15bf52406f
|
|
4
|
+
data.tar.gz: 97c8e0f4d601a7ddb553e786bc23f0f8b9a70a1eae307d7cf79da8e05d70d5c9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a1bec82d194a2943c597dc804c2a3f69c7a3f09da8c9734d82e76c1af7c102748175307e1a530a7107a0011bf5d1556cefc1c2d9fd7929e1e52ea2979912ed2c
|
|
7
|
+
data.tar.gz: 992f9e6d0c5b58e06d726ed5967121d85530bf654410a475ce7b694f7e559dc42d6bc0e021136e550b000517b6d9a505e45db16bede3bc2ecc0b70c308c267ec
|
data/Gemfile.lock
CHANGED
|
@@ -11,6 +11,7 @@ module Spree
|
|
|
11
11
|
# - inbound_date: Date of inbound transit (optional)
|
|
12
12
|
# - outbound_legs: Array of outbound leg details (required)
|
|
13
13
|
# - inbound_legs: Array of inbound leg details (optional)
|
|
14
|
+
# - include_return: Whether to add the trip's "return included" add-on as a line item (optional)
|
|
14
15
|
def create
|
|
15
16
|
@outbound_legs = params[:outbound_legs].is_a?(Array) && params[:outbound_legs].any? ? build_legs(:outbound, params[:outbound_legs]) : []
|
|
16
17
|
@inbound_legs = params[:inbound_legs].is_a?(Array) && params[:inbound_legs].any? ? build_legs(:inbound, params[:inbound_legs]) : []
|
|
@@ -20,7 +21,8 @@ module Spree
|
|
|
20
21
|
inbound_date: params[:inbound_date]&.to_date,
|
|
21
22
|
outbound_legs: @outbound_legs,
|
|
22
23
|
inbound_legs: @inbound_legs,
|
|
23
|
-
user: spree_current_user
|
|
24
|
+
user: spree_current_user,
|
|
25
|
+
include_return: ActiveModel::Type::Boolean.new.cast(params[:include_return])
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
if result.success?
|
|
@@ -11,6 +11,7 @@ module Spree
|
|
|
11
11
|
# - inbound_date: Date of inbound transit (optional)
|
|
12
12
|
# - outbound_legs: Array of outbound leg details (required)
|
|
13
13
|
# - inbound_legs: Array of inbound leg details (optional)
|
|
14
|
+
# - include_return: Whether to add the trip's "return included" add-on as a line item (optional)
|
|
14
15
|
def create
|
|
15
16
|
@outbound_legs = params[:outbound_legs].is_a?(Array) && params[:outbound_legs].any? ? build_legs(:outbound, params[:outbound_legs]) : []
|
|
16
17
|
@inbound_legs = params[:inbound_legs].is_a?(Array) && params[:inbound_legs].any? ? build_legs(:inbound, params[:inbound_legs]) : []
|
|
@@ -20,7 +21,8 @@ module Spree
|
|
|
20
21
|
inbound_date: params[:inbound_date]&.to_date,
|
|
21
22
|
outbound_legs: @outbound_legs,
|
|
22
23
|
inbound_legs: @inbound_legs,
|
|
23
|
-
user: spree_current_user
|
|
24
|
+
user: spree_current_user,
|
|
25
|
+
include_return: ActiveModel::Type::Boolean.new.cast(params[:include_return])
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
if result.success?
|
|
@@ -6,7 +6,7 @@ module SpreeCmCommissioner
|
|
|
6
6
|
context.fail!(message: I18n.t('pincode_sender.pincode.blank')) if context.pin_code.nil?
|
|
7
7
|
|
|
8
8
|
if context.pin_code.phone_number?
|
|
9
|
-
|
|
9
|
+
send_phone_otp
|
|
10
10
|
else
|
|
11
11
|
send_email
|
|
12
12
|
end
|
|
@@ -16,18 +16,73 @@ module SpreeCmCommissioner
|
|
|
16
16
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
# Channel selection for phone-number OTPs lives here so the routing is
|
|
20
|
+
# easy to find and reason about. Telegram Gateway is primary; SMS is the fallback.
|
|
21
|
+
def send_phone_otp
|
|
22
|
+
if telegram_gateway_available?
|
|
23
|
+
send_via_telegram_gateway
|
|
24
|
+
else
|
|
25
|
+
send_via_sms
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def telegram_gateway_available?
|
|
30
|
+
return false unless telegram_gateway_enabled?
|
|
31
|
+
return false if telegram_gateway_api_key.blank?
|
|
32
|
+
|
|
33
|
+
ability = telegram_gateway_adapter.check_send_ability(context.pin_code.contact)
|
|
34
|
+
context.telegram_gateway_request_id = ability.request_id if ability.ok?
|
|
35
|
+
ability.ok?
|
|
36
|
+
rescue TelegramGatewayAdapter::Error, Faraday::Error
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def send_via_telegram_gateway
|
|
20
41
|
from_number = sms_from_number
|
|
21
42
|
return if from_number.blank?
|
|
22
43
|
|
|
23
|
-
|
|
44
|
+
# Save the chosen channel + the Telegram request_id synchronously so
|
|
45
|
+
# (a) the API response already reflects the channel for the mobile UI,
|
|
46
|
+
# (b) verification can reach Telegram even if the user submits the OTP
|
|
47
|
+
# before the async send job has run. For Telegram-delivered codes
|
|
48
|
+
# the `code` column stores Telegram's request_id (Telegram generates
|
|
49
|
+
# and holds the actual OTP).
|
|
50
|
+
context.pin_code.update!(
|
|
51
|
+
delivery_channel: :telegram_gateway,
|
|
52
|
+
code: context.telegram_gateway_request_id
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
SpreeCmCommissioner::TelegramGateway::PinCodeSenderJob.perform_later(
|
|
56
|
+
pin_code_id: context.pin_code.id,
|
|
57
|
+
from: from_number,
|
|
58
|
+
request_id: context.telegram_gateway_request_id,
|
|
59
|
+
tenant_id: context.pin_code&.application&.tenant_id
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def send_via_sms
|
|
64
|
+
from_number = sms_from_number
|
|
65
|
+
return if from_number.blank?
|
|
66
|
+
|
|
67
|
+
SpreeCmCommissioner::SmsPinCodeJob.perform_later(
|
|
68
|
+
pin_code_id: context.pin_code.id,
|
|
24
69
|
from: from_number,
|
|
25
70
|
to: context.pin_code.contact,
|
|
26
71
|
body: I18n.t('pincode_sender.sms.body', code: context.pin_code.code, readable_type: context.pin_code.readable_type),
|
|
27
72
|
tenant_id: context.pin_code&.application&.tenant_id
|
|
28
|
-
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def telegram_gateway_enabled?
|
|
77
|
+
ENV['TELEGRAM_GATEWAY_ENABLED'] == 'yes'
|
|
78
|
+
end
|
|
29
79
|
|
|
30
|
-
|
|
80
|
+
def telegram_gateway_api_key
|
|
81
|
+
@telegram_gateway_api_key ||= ENV.fetch('TELEGRAM_GATEWAY_API_KEY', nil)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def telegram_gateway_adapter
|
|
85
|
+
@telegram_gateway_adapter ||= TelegramGatewayAdapter::Client.new
|
|
31
86
|
end
|
|
32
87
|
|
|
33
88
|
def sms_from_number
|
|
@@ -38,6 +93,8 @@ module SpreeCmCommissioner
|
|
|
38
93
|
end
|
|
39
94
|
|
|
40
95
|
def send_email
|
|
96
|
+
context.pin_code.update!(delivery_channel: :email)
|
|
97
|
+
|
|
41
98
|
SpreeCmCommissioner::PinCodeMailer.send_pin_code(
|
|
42
99
|
context.pin_code.id,
|
|
43
100
|
context.pin_code.readable_type,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module TelegramGateway
|
|
3
|
+
# options = { pin_code_id:, from:, request_id:, tenant_id: }
|
|
4
|
+
class PinCodeSenderJob < SpreeCmCommissioner::SmsJob
|
|
5
|
+
def perform(options = {})
|
|
6
|
+
pin_code = SpreeCmCommissioner::PinCode.find_by(id: options[:pin_code_id])
|
|
7
|
+
return if pin_code.nil?
|
|
8
|
+
|
|
9
|
+
SpreeCmCommissioner::TelegramGateway::PinCodeSender.call(
|
|
10
|
+
pin_code: pin_code,
|
|
11
|
+
from: options[:from],
|
|
12
|
+
request_id: options[:request_id],
|
|
13
|
+
tenant_id: options[:tenant_id]
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
|
+
# Makes an open-return entitlement line item redeemable.
|
|
3
|
+
#
|
|
4
|
+
# New design: the open return is no longer a fake open-dated *trip* line item. It is a plain
|
|
5
|
+
# ecommerce add-on (a Spree::Product linked to the outbound trip's product via an
|
|
6
|
+
# `open_dated_pair` ProductRelation) sold as a regular ecommerce line item at checkout.
|
|
7
|
+
#
|
|
8
|
+
# Redemption swaps that ecommerce line item in place into a concrete transit ticket on a chosen
|
|
9
|
+
# return trip (see SpreeCmCommissioner::OpenDatedTrips::Redeem). Once swapped, the line item is a
|
|
10
|
+
# `transit` item.
|
|
2
11
|
module LineItemOpenDatedTrippable
|
|
3
12
|
extend ActiveSupport::Concern
|
|
4
13
|
|
|
5
14
|
included do
|
|
6
15
|
include SpreeCmCommissioner::StoreMetadata
|
|
7
16
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
store_private_metadata :open_dated_product_id, :integer
|
|
18
|
+
|
|
19
|
+
# The endpoints (Place ids) the *return* trip must run between — already reversed from the
|
|
20
|
+
# outbound journey at purchase time (return origin = outbound destination, and vice versa).
|
|
21
|
+
# Storing the return direction means every consumer reads it directly: redemption is a plain
|
|
22
|
+
# id match and the search needs no flip. Endpoints rather than a trip keep this
|
|
23
|
+
# leg-count-independent: a multi-leg outbound still has exactly one origin and one destination.
|
|
24
|
+
store_private_metadata :open_dated_return_origin_place_id, :integer
|
|
25
|
+
store_private_metadata :open_dated_return_destination_place_id, :integer
|
|
11
26
|
|
|
12
27
|
# Alias existing columns for better semantic meaning
|
|
13
28
|
alias_attribute :valid_until, :to_date # Expiration date
|
|
@@ -19,19 +34,47 @@ module SpreeCmCommissioner
|
|
|
19
34
|
accepter
|
|
20
35
|
end
|
|
21
36
|
|
|
22
|
-
|
|
37
|
+
def open_dated?
|
|
38
|
+
open_dated_return_origin_place_id.present? && open_dated_return_destination_place_id.present?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def open_dated_product
|
|
42
|
+
return nil if open_dated_product_id.blank?
|
|
43
|
+
|
|
44
|
+
@open_dated_product ||= Spree::Product.find_by(id: open_dated_product_id)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Origin place the return trip must depart from (= outbound destination).
|
|
48
|
+
def open_dated_return_origin_place
|
|
49
|
+
return nil if open_dated_return_origin_place_id.blank?
|
|
50
|
+
|
|
51
|
+
@open_dated_return_origin_place ||= SpreeCmCommissioner::Place.find_by(id: open_dated_return_origin_place_id)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Destination place the return trip must arrive at (= outbound origin).
|
|
55
|
+
def open_dated_return_destination_place
|
|
56
|
+
return nil if open_dated_return_destination_place_id.blank?
|
|
57
|
+
|
|
58
|
+
@open_dated_return_destination_place ||= SpreeCmCommissioner::Place.find_by(id: open_dated_return_destination_place_id)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Human-readable return journey, e.g. "Siem Reap → Phnom Penh". Nil when endpoints are missing.
|
|
62
|
+
def open_dated_route_label
|
|
63
|
+
return nil if open_dated_return_origin_place.blank? || open_dated_return_destination_place.blank?
|
|
64
|
+
|
|
65
|
+
"#{open_dated_return_origin_place.full_path_name} → #{open_dated_return_destination_place.full_path_name}"
|
|
66
|
+
end
|
|
67
|
+
|
|
23
68
|
def redeemed?
|
|
24
69
|
redeemed_at.present?
|
|
25
70
|
end
|
|
26
71
|
|
|
27
|
-
# Check if ticket is expired
|
|
28
72
|
def expired?
|
|
29
73
|
valid_until.present? && valid_until.to_date < Date.current
|
|
30
74
|
end
|
|
31
75
|
|
|
32
|
-
# Check if ticket can be redeemed
|
|
33
76
|
def can_redeem?
|
|
34
|
-
|
|
77
|
+
open_dated? && !redeemed? && !expired?
|
|
35
78
|
end
|
|
36
79
|
end
|
|
37
80
|
end
|
|
@@ -138,7 +138,6 @@ module SpreeCmCommissioner
|
|
|
138
138
|
|
|
139
139
|
trip = SpreeCmCommissioner::Trip.find_by(id: trip_id)
|
|
140
140
|
return unless trip # Skip if trip doesn't exist (will be caught by other validations)
|
|
141
|
-
return if trip.open_dated? # Skip validation for open-dated trips
|
|
142
141
|
|
|
143
142
|
# For scheduled trips, require valid trip stops
|
|
144
143
|
errors.add(:boarding_trip_stop_id, 'must be greater than 0') if boarding_trip_stop_id.present? && boarding_trip_stop_id <= 0
|
|
@@ -3,8 +3,9 @@ module SpreeCmCommissioner
|
|
|
3
3
|
has_secure_token
|
|
4
4
|
|
|
5
5
|
enum contact_type: { 'phone_number' => 0, 'email' => 1, 'telegram' => 2 }
|
|
6
|
+
enum delivery_channel: { sms: 0, telegram_gateway: 1, email: 2 }, _prefix: :delivered_via
|
|
6
7
|
|
|
7
|
-
validates :code, length: { maximum:
|
|
8
|
+
validates :code, length: { maximum: 32 }
|
|
8
9
|
validates :contact, presence: true
|
|
9
10
|
validates :contact, email: true, if: :email?
|
|
10
11
|
validates :type, presence: true
|
|
@@ -18,6 +19,15 @@ module SpreeCmCommissioner
|
|
|
18
19
|
PIN_CODE_MISMATCHED = 'not_match'.freeze
|
|
19
20
|
PIN_CODE_OK = 'ok'.freeze
|
|
20
21
|
|
|
22
|
+
# Maps Telegram Gateway verification_status values to our PIN_CODE_* results.
|
|
23
|
+
# Anything not in this map → soft fallback to local comparison.
|
|
24
|
+
TELEGRAM_VERIFICATION_RESULTS = {
|
|
25
|
+
'code_valid' => PIN_CODE_OK,
|
|
26
|
+
'code_invalid' => PIN_CODE_MISMATCHED,
|
|
27
|
+
'code_max_attempts_exceeded' => PIN_CODE_ATTEMPT_REACHED,
|
|
28
|
+
'expired' => PIN_CODE_EXPIRED
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
21
31
|
belongs_to :application, class_name: 'Spree::OauthApplication', optional: true
|
|
22
32
|
|
|
23
33
|
def check?(code)
|
|
@@ -26,14 +36,10 @@ module SpreeCmCommissioner
|
|
|
26
36
|
|
|
27
37
|
increment_attempt
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
else
|
|
34
|
-
save
|
|
35
|
-
PIN_CODE_MISMATCHED
|
|
36
|
-
end
|
|
39
|
+
result = verify_code(code)
|
|
40
|
+
set_expire if result == PIN_CODE_OK
|
|
41
|
+
save
|
|
42
|
+
result
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
def set_expire
|
|
@@ -143,5 +149,52 @@ module SpreeCmCommissioner
|
|
|
143
149
|
self.code = Random.new.bytes(8).bytes.join[0, 6]
|
|
144
150
|
self.expires_in = expires_in_seconds
|
|
145
151
|
end
|
|
152
|
+
|
|
153
|
+
# SMS codes verify internally (Plasgate has no verify API).
|
|
154
|
+
# Telegram-delivered codes verify with Telegram — no internal fallback,
|
|
155
|
+
# because `code` holds Telegram's request_id, not a comparable OTP.
|
|
156
|
+
# If Telegram is unreachable, we return PIN_CODE_MISMATCHED; the user
|
|
157
|
+
# retries from the app.
|
|
158
|
+
def verify_code(submitted_code)
|
|
159
|
+
return verify_internally(submitted_code) unless delivered_via_telegram_gateway?
|
|
160
|
+
|
|
161
|
+
verify_with_telegram(submitted_code) || PIN_CODE_MISMATCHED
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def verify_internally(submitted_code)
|
|
165
|
+
code == submitted_code ? PIN_CODE_OK : PIN_CODE_MISMATCHED
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# For Telegram-delivered PinCodes, `code` column stores Telegram's
|
|
169
|
+
# request_id (not the OTP itself — Telegram generated and delivered the OTP).
|
|
170
|
+
# Returns a PIN_CODE_* result, or nil if Telegram couldn't help us decide
|
|
171
|
+
# (network error, unmapped status).
|
|
172
|
+
def verify_with_telegram(submitted_code)
|
|
173
|
+
result = TelegramGatewayAdapter::Client.new.check_verification_status(code, submitted_code)
|
|
174
|
+
TELEGRAM_VERIFICATION_RESULTS[result.verification_status]
|
|
175
|
+
rescue TelegramGatewayAdapter::Error, Faraday::Error => e
|
|
176
|
+
log_verification_error(e)
|
|
177
|
+
nil
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def log_verification_error(error)
|
|
181
|
+
CmAppLogger.error(
|
|
182
|
+
label: 'PinCode#verify_with_telegram failed',
|
|
183
|
+
data: {
|
|
184
|
+
pin_code_id: id,
|
|
185
|
+
request_id: code,
|
|
186
|
+
error_class: error.class.name,
|
|
187
|
+
error_message: error.message
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return unless ENV['PIN_CODE_DEBUG_NOTIFIY_TELEGRAM_ENABLE'] == 'yes'
|
|
192
|
+
|
|
193
|
+
SpreeCmCommissioner::TelegramDebugPinCodeSenderJob.perform_later(
|
|
194
|
+
pin_code_id: id,
|
|
195
|
+
tenant_id: application&.tenant_id,
|
|
196
|
+
error_message: "Verification failed: #{error.class.name} — #{error.message}"
|
|
197
|
+
)
|
|
198
|
+
end
|
|
146
199
|
end
|
|
147
200
|
end
|
|
@@ -55,6 +55,12 @@ module SpreeCmCommissioner
|
|
|
55
55
|
base.has_many :product_relations, class_name: 'SpreeCmCommissioner::ProductRelation', dependent: :destroy
|
|
56
56
|
base.has_many :related_products, through: :product_relations
|
|
57
57
|
|
|
58
|
+
# Inverse side of product_relations: relations where this product is the related_product
|
|
59
|
+
# (e.g. trip add-ons point here).
|
|
60
|
+
base.has_many :inverse_product_relations, class_name: 'SpreeCmCommissioner::ProductRelation',
|
|
61
|
+
foreign_key: :related_product_id,
|
|
62
|
+
dependent: :destroy
|
|
63
|
+
|
|
58
64
|
base.belongs_to :event, class_name: 'Spree::Taxon', optional: true
|
|
59
65
|
|
|
60
66
|
base.has_many :preview_roles, class_name: 'SpreeCmCommissioner::PreviewRole', as: :previewable
|
|
@@ -65,9 +65,8 @@ module SpreeCmCommissioner
|
|
|
65
65
|
has_many :inventory_items, through: :variants
|
|
66
66
|
has_many :blocks, through: :variants
|
|
67
67
|
|
|
68
|
-
validates :departure_time, presence: true
|
|
69
|
-
validates :
|
|
70
|
-
validates :duration, numericality: { greater_than: 0 }, unless: :open_dated?
|
|
68
|
+
validates :departure_time, presence: true
|
|
69
|
+
validates :duration, numericality: { greater_than: 0 }
|
|
71
70
|
|
|
72
71
|
validate :direct_trips_cannot_have_board_to_trips, if: -> { direct? }
|
|
73
72
|
validate :multi_leg_trip_stops_must_have_board_to_trip, if: -> { multi_leg? }
|
|
@@ -120,15 +119,6 @@ module SpreeCmCommissioner
|
|
|
120
119
|
errors.add(:board_to_trips, 'direct trips cannot have board_to_trips')
|
|
121
120
|
end
|
|
122
121
|
|
|
123
|
-
def open_dated?
|
|
124
|
-
is_open_dated == true
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Find the open dated version of this trip's product
|
|
128
|
-
def open_dated_pair
|
|
129
|
-
open_dated_product
|
|
130
|
-
end
|
|
131
|
-
|
|
132
122
|
def display_name
|
|
133
123
|
product&.name
|
|
134
124
|
end
|
|
@@ -8,7 +8,6 @@ module SpreeCmCommissioner
|
|
|
8
8
|
belongs_to :advanced_to, polymorphic: true, optional: true
|
|
9
9
|
belongs_to :advanced_from, polymorphic: true, optional: true
|
|
10
10
|
has_many :votes, class_name: 'SpreeCmCommissioner::Vote', foreign_key: :contestant_id, dependent: :restrict_with_error
|
|
11
|
-
has_one :leading_stat, class_name: 'SpreeCmCommissioner::VotingSessionStat', foreign_key: :leading_contestant_id, dependent: :nullify
|
|
12
11
|
has_many :show_contestant_images, through: :show_contestant
|
|
13
12
|
has_many :show_contestant_videos, through: :show_contestant
|
|
14
13
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# results = query.call # => [TripQueryResult, ...]
|
|
15
15
|
#
|
|
16
16
|
module SpreeCmCommissioner
|
|
17
|
-
class SingleLegTripsQuery
|
|
17
|
+
class SingleLegTripsQuery # rubocop:disable Metrics/ClassLength
|
|
18
18
|
attr_reader :origin_id, :destination_id, :date,
|
|
19
19
|
:vendor_id, :tenant_id,
|
|
20
20
|
:number_of_guests, :route_type, :options
|
|
@@ -61,8 +61,7 @@ module SpreeCmCommissioner
|
|
|
61
61
|
.eager_load(
|
|
62
62
|
vendor: :logo,
|
|
63
63
|
vehicle_type: :option_values,
|
|
64
|
-
route: {}
|
|
65
|
-
open_dated_product: %i[trip variants]
|
|
64
|
+
route: {}
|
|
66
65
|
)
|
|
67
66
|
.select(selected_columns_sql)
|
|
68
67
|
.joins(
|
|
@@ -79,6 +78,7 @@ module SpreeCmCommissioner
|
|
|
79
78
|
LEFT JOIN spree_prices AS date_prices ON date_prices.inventory_item_id = date_iv.id AND date_prices.deleted_at IS NULL
|
|
80
79
|
LEFT JOIN (#{stops_subquery_sql}) all_stops ON all_stops.trip_id = cm_trips.id
|
|
81
80
|
#{return_metric_joins_sql}
|
|
81
|
+
#{open_dated_product_joins_sql}
|
|
82
82
|
SQL
|
|
83
83
|
)
|
|
84
84
|
.where(<<~SQL.squish, origin: origin_id, destination: destination_id)
|
|
@@ -111,6 +111,9 @@ module SpreeCmCommissioner
|
|
|
111
111
|
COALESCE(date_prices.compare_at_amount, prices.compare_at_amount) AS compare_at_price,
|
|
112
112
|
COALESCE(date_prices.currency, prices.currency) AS currency,
|
|
113
113
|
all_stops.stops AS stops,
|
|
114
|
+
open_dated_rel.product_id AS open_dated_product_id,
|
|
115
|
+
open_dated_prices.amount AS open_dated_price,
|
|
116
|
+
open_dated_prices.compare_at_amount AS open_dated_compare_at_price,
|
|
114
117
|
#{return_columns_sql}
|
|
115
118
|
SQL
|
|
116
119
|
end
|
|
@@ -142,6 +145,24 @@ module SpreeCmCommissioner
|
|
|
142
145
|
SQL
|
|
143
146
|
end
|
|
144
147
|
|
|
148
|
+
# Joins the "open dated" pair product (if any) for the trip's product, and its master price,
|
|
149
|
+
# so we can expose the return add-on's price/compare_at_price without loading the full
|
|
150
|
+
# Spree::Product/Variant/Price records.
|
|
151
|
+
def open_dated_product_joins_sql
|
|
152
|
+
<<~SQL.squish
|
|
153
|
+
LEFT JOIN cm_product_relations AS open_dated_rel
|
|
154
|
+
ON open_dated_rel.related_product_id = cm_trips.product_id
|
|
155
|
+
AND open_dated_rel.relation_type = #{SpreeCmCommissioner::ProductRelation.relation_types[:open_dated_pair]}
|
|
156
|
+
LEFT JOIN spree_variants AS open_dated_master
|
|
157
|
+
ON open_dated_master.product_id = open_dated_rel.product_id
|
|
158
|
+
AND open_dated_master.is_master = true
|
|
159
|
+
LEFT JOIN spree_prices AS open_dated_prices
|
|
160
|
+
ON open_dated_prices.variant_id = open_dated_master.id
|
|
161
|
+
AND open_dated_prices.inventory_item_id IS NULL
|
|
162
|
+
AND open_dated_prices.deleted_at IS NULL
|
|
163
|
+
SQL
|
|
164
|
+
end
|
|
165
|
+
|
|
145
166
|
# Subquery to aggregate all stops for each trip into a JSON array, ordered by sequence.
|
|
146
167
|
# Each stop includes place coordinates (lat/lon) joined from cm_places.
|
|
147
168
|
# Returns one row per trip_id with a `stops` JSON column consumed by selected_columns_sql.
|
|
@@ -14,7 +14,9 @@ module Spree
|
|
|
14
14
|
|
|
15
15
|
base.attributes :need_confirmation, :product_type, :kyc, :kyc_fields, :allowed_upload_later, :allow_anonymous_booking,
|
|
16
16
|
:use_video_as_default, :allow_transfer, :allow_gift_transfer
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
base.attributes :reveal_description, :discontinue_on, :public_metadata, :purchasable_on, :preview,
|
|
19
|
+
:enable_inventory_hold
|
|
18
20
|
|
|
19
21
|
# Expose only the `event_id` here instead of the full event object.
|
|
20
22
|
# This lets the client fetch event details separately (usually already cached),
|
|
@@ -2,7 +2,7 @@ module Spree
|
|
|
2
2
|
module V2
|
|
3
3
|
module Tenant
|
|
4
4
|
class ShowEpisodeSerializer < BaseSerializer
|
|
5
|
-
attributes :episode_number, :name, :
|
|
5
|
+
attributes :episode_number, :name, :scheduled_at, :ends_at
|
|
6
6
|
|
|
7
7
|
attribute :status do |episode|
|
|
8
8
|
if episode.current?
|
|
@@ -5,11 +5,10 @@ module Spree
|
|
|
5
5
|
set_type :show
|
|
6
6
|
|
|
7
7
|
attributes :slug, :description, :permalink, :from_date, :to_date, :preview, :created_at, :updated_at,
|
|
8
|
-
:free_vote_limit, :free_vote_limit_type, :show_type
|
|
8
|
+
:free_vote_limit, :free_vote_limit_type, :show_type
|
|
9
9
|
|
|
10
10
|
attribute :name, &:display_name
|
|
11
11
|
|
|
12
|
-
has_many :seasons, serializer: Spree::V2::Tenant::ShowSerializer
|
|
13
12
|
has_many :show_people, serializer: Spree::V2::Tenant::ShowPersonSerializer
|
|
14
13
|
has_many :episodes, serializer: Spree::V2::Tenant::ShowEpisodeSerializer
|
|
15
14
|
has_many :voting_sessions, serializer: Spree::V2::Tenant::VotingSessionSerializer
|
data/app/serializers/spree_cm_commissioner/v2/storefront/ticket_transfer_minimal_serializer.rb
CHANGED
|
@@ -4,7 +4,7 @@ module SpreeCmCommissioner
|
|
|
4
4
|
class TicketTransferMinimalSerializer < BaseSerializer
|
|
5
5
|
set_type :ticket_transfer
|
|
6
6
|
|
|
7
|
-
attributes :state, :price, :platform_fee, :seller_net, :total, :currency, :token,
|
|
7
|
+
attributes :number, :state, :price, :platform_fee, :seller_net, :total, :currency, :token,
|
|
8
8
|
:transfer_type, :settlement_status, :completed_at, :expires_at, :created_at, :updated_at
|
|
9
9
|
|
|
10
10
|
belongs_to :from_user, serializer: ::Spree::V2::Storefront::UserSerializer
|
|
@@ -5,9 +5,11 @@ module SpreeCmCommissioner
|
|
|
5
5
|
set_type :trip_query_result
|
|
6
6
|
|
|
7
7
|
attributes :quantity_available, :max_capacity, :price, :compare_at_price, :currency, :display_price, :display_compare_at_price,
|
|
8
|
-
:has_return_trip_globally, :has_return_trip_in_vendor
|
|
8
|
+
:has_return_trip_globally, :has_return_trip_in_vendor, :include_return_price, :include_return_compare_at_price,
|
|
9
|
+
:display_include_return_price, :display_include_return_compare_at_price
|
|
9
10
|
|
|
10
11
|
attribute :direct, &:direct?
|
|
12
|
+
attribute :include_return, &:include_return?
|
|
11
13
|
has_many :trips, serializer: ::SpreeCmCommissioner::V2::Storefront::TripResultSerializer
|
|
12
14
|
end
|
|
13
15
|
end
|
|
@@ -17,7 +17,8 @@ module SpreeCmCommissioner
|
|
|
17
17
|
|
|
18
18
|
attributes :origin_place, :destination_place, :stops, :price, :compare_at_price, :currency, :quantity_available, :max_capacity,
|
|
19
19
|
:allow_seat_selection, :departure_time, :route_type, :distance, :offset_days,
|
|
20
|
-
:featured_until, :display_price, :display_compare_at_price
|
|
20
|
+
:featured_until, :display_price, :display_compare_at_price,
|
|
21
|
+
:open_dated_product_id, :open_dated_price, :open_dated_compare_at_price
|
|
21
22
|
|
|
22
23
|
# Deprecated for backwards compatibility
|
|
23
24
|
attribute :compare_at_amount, &:compare_at_price
|