spree_cm_commissioner 1.8.10 → 1.9.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +4 -0
  3. data/Gemfile.lock +25 -1
  4. data/app/controllers/spree/admin/user_events_controller.rb +9 -7
  5. data/app/controllers/spree/api/v2/storefront/vattanac_banks_controller.rb +25 -0
  6. data/app/controllers/spree/api/v2/tenant/change_passwords_controller.rb +32 -0
  7. data/app/controllers/spree/api/v2/tenant/id_cards_controller.rb +63 -0
  8. data/app/controllers/spree/api/v2/tenant/reset_passwords_controller.rb +35 -0
  9. data/app/controllers/spree/api/v2/tenant/tickets_controller.rb +25 -0
  10. data/app/controllers/spree/api/v2/tenant/user_contacts_controller.rb +33 -0
  11. data/app/controllers/spree_cm_commissioner/admin/roles_controller_decorator.rb +13 -0
  12. data/app/interactors/spree_cm_commissioner/firebase_email_fetcher.rb +25 -0
  13. data/app/interactors/spree_cm_commissioner/firebase_id_token_provider.rb +12 -1
  14. data/app/interactors/spree_cm_commissioner/user_id_token_authenticator.rb +11 -0
  15. data/app/interactors/spree_cm_commissioner/user_registration_with_id_token.rb +3 -3
  16. data/app/interactors/spree_cm_commissioner/user_vattanac_bank_web_app_authenticator.rb +30 -0
  17. data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +116 -0
  18. data/app/models/spree_cm_commissioner/order_decorator.rb +1 -1
  19. data/app/models/spree_cm_commissioner/role_decorator.rb +9 -3
  20. data/app/models/spree_cm_commissioner/taxon_decorator.rb +0 -1
  21. data/app/models/spree_cm_commissioner/trip.rb +7 -0
  22. data/app/models/spree_cm_commissioner/trip_connection.rb +10 -5
  23. data/app/models/spree_cm_commissioner/user_identity_provider.rb +1 -1
  24. data/app/models/spree_cm_commissioner/variant_decorator.rb +19 -2
  25. data/app/models/spree_cm_commissioner/variant_options.rb +14 -0
  26. data/app/overrides/spree/admin/users/_form/roles_fields.html.erb.deface +2 -0
  27. data/app/queries/spree_cm_commissioner/tickets_searcher_query.rb +21 -0
  28. data/app/queries/spree_cm_commissioner/trip_query.rb +145 -0
  29. data/app/serializers/spree/v2/tenant/id_card_serializer.rb +12 -0
  30. data/app/serializers/spree/v2/tenant/reset_password_serializer.rb +8 -0
  31. data/app/serializers/spree/v2/tenant/ticket_serializer.rb +27 -0
  32. data/app/serializers/spree/v2/tenant/user_contact_serializer.rb +11 -0
  33. data/app/services/spree_cm_commissioner/aes_encryption_service.rb +52 -0
  34. data/app/services/spree_cm_commissioner/role_permissions_constructor.rb +42 -0
  35. data/app/services/spree_cm_commissioner/role_permissions_loader.rb +57 -0
  36. data/app/services/spree_cm_commissioner/rsa_service.rb +27 -0
  37. data/app/services/spree_cm_commissioner/user_authenticator.rb +4 -0
  38. data/app/views/spree/admin/user_events/_events.html.erb +1 -1
  39. data/app/views/spree/admin/user_events/index.html.erb +7 -3
  40. data/config/routes.rb +9 -2
  41. data/db/migrate/20250328072717_add_description_to_spree_roles.rb +1 -1
  42. data/db/migrate/20250328072841_add_vendor_id_to_spree_roles.rb +1 -1
  43. data/db/migrate/20250403025619_add_presentation_to_spree_role.rb +5 -0
  44. data/db/migrate/20250403042255_add_unique_constraint_to_name_in_spree_roles.rb +7 -0
  45. data/db/migrate/20250407092556_add_variant_id_to_cm_trips.rb +7 -0
  46. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +9 -0
  47. data/lib/spree_cm_commissioner/test_helper/factories/role_permission_factory.rb +11 -0
  48. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +6 -0
  49. data/lib/spree_cm_commissioner/test_helper/factories/user_identity_provider_factory.rb +4 -0
  50. data/lib/spree_cm_commissioner/trip_query_result.rb +14 -0
  51. data/lib/spree_cm_commissioner/user_session_jwt_token.rb +5 -0
  52. data/lib/spree_cm_commissioner/version.rb +1 -1
  53. data/lib/spree_cm_commissioner.rb +2 -0
  54. data/spree_cm_commissioner.gemspec +1 -0
  55. metadata +44 -2
@@ -1,7 +1,5 @@
1
1
  module SpreeCmCommissioner
2
2
  class TripConnection < ApplicationRecord
3
- attr_accessor :hours, :minutes
4
-
5
3
  belongs_to :from_trip, class_name: 'Spree::Variant'
6
4
  belongs_to :to_trip, class_name: 'Spree::Variant'
7
5
 
@@ -13,11 +11,18 @@ module SpreeCmCommissioner
13
11
  def calculate_connection_time_minutes
14
12
  return if from_trip.nil? || to_trip.nil?
15
13
 
16
- connection_time_in_minutes = (to_trip.options.departure_time - from_trip.options.arrival_time) / 60
14
+ arrival_seconds = from_trip.trip.arrival_time.seconds_since_midnight
15
+ departure_seconds = to_trip.trip.departure_time.seconds_since_midnight
16
+
17
+ layover_seconds = departure_seconds - arrival_seconds
18
+ layover_seconds += 86_400 if layover_seconds.negative?
19
+
20
+ connection_time_in_minutes = layover_seconds / 60
21
+
17
22
  if connection_time_in_minutes.between?(0, 180)
18
- self.connection_time_minutes = connection_time_in_minutes
23
+ self.connection_time_minutes = connection_time_in_minutes.to_i
19
24
  else
20
- errors.add(:base, 'Connection time should be between 0 and 180 minutes')
25
+ errors.add(:base, 'Connection time must be less than 3 hours')
21
26
  end
22
27
  end
23
28
 
@@ -1,6 +1,6 @@
1
1
  module SpreeCmCommissioner
2
2
  class UserIdentityProvider < Base
3
- enum identity_type: { :google => 0, :apple => 1, :facebook => 2, :telegram => 3 }
3
+ enum identity_type: { :google => 0, :apple => 1, :facebook => 2, :telegram => 3, :vattanac_bank => 4 }
4
4
 
5
5
  belongs_to :user, class_name: Spree.user_class.to_s, optional: false
6
6
 
@@ -21,10 +21,11 @@ module SpreeCmCommissioner
21
21
  base.has_many :variant_guest_card_class, class_name: 'SpreeCmCommissioner::VariantGuestCardClass'
22
22
  base.has_many :guest_card_classes, class_name: 'SpreeCmCommissioner::GuestCardClass', through: :variant_guest_card_class
23
23
 
24
- base.has_many :trip_stops, class_name: 'SpreeCmCommissioner::TripStop', dependent: :destroy, foreign_key: :trip_id
25
24
  base.scope :subscribable, -> { active.joins(:product).where(product: { subscribable: true, status: :active }) }
26
-
25
+ base.has_one :trip,
26
+ class_name: 'SpreeCmCommissioner::Trip'
27
27
  base.accepts_nested_attributes_for :option_values
28
+ base.after_commit :sync_trip, if: -> { product.product_type == 'transit' }
28
29
  end
29
30
 
30
31
  def delivery_required?
@@ -110,6 +111,22 @@ module SpreeCmCommissioner
110
111
  end
111
112
  end
112
113
  end
114
+
115
+ def sync_trip
116
+ return unless product.product_type == 'transit'
117
+
118
+ trip = SpreeCmCommissioner::Trip.find_or_initialize_by(variant_id: id)
119
+
120
+ trip.assign_attributes(
121
+ product_id: product_id,
122
+ origin_id: options.origin,
123
+ destination_id: options.destination,
124
+ departure_time: options.departure_time,
125
+ duration: options.total_duration_in_seconds,
126
+ vehicle_id: options.vehicle
127
+ )
128
+ trip.save
129
+ end
113
130
  end
114
131
  end
115
132
 
@@ -166,5 +166,19 @@ module SpreeCmCommissioner
166
166
  Time.zone.parse(time) if time.present?
167
167
  end
168
168
  end
169
+
170
+ def arrival_time
171
+ @arrival_time ||= departure_time + total_duration_in_minutes.minutes
172
+ end
173
+
174
+ def total_duration_in_minutes
175
+ total_duration_in_minutes = 0
176
+
177
+ total_duration_in_minutes += duration_in_hours * 60 if duration_in_hours.present?
178
+ total_duration_in_minutes += duration_in_minutes if duration_in_minutes.present?
179
+ total_duration_in_minutes += duration_in_seconds / 60 if duration_in_seconds.present?
180
+
181
+ total_duration_in_minutes
182
+ end
169
183
  end
170
184
  end
@@ -0,0 +1,2 @@
1
+ <!-- replace 'erb[loud]:contains("f.collection_check_boxes :spree_role_ids")' -->
2
+ <%= f.collection_check_boxes :spree_role_ids, Spree::Role.non_vendor, :id, :name do |role_form| %>
@@ -0,0 +1,21 @@
1
+ module SpreeCmCommissioner
2
+ class TicketsSearcherQuery
3
+ attr_reader :params, :tenant_id
4
+
5
+ def initialize(params, tenant_id)
6
+ @params = params
7
+ @tenant_id = tenant_id
8
+ end
9
+
10
+ def call
11
+ return Spree::Product.none if tenant_id.blank?
12
+
13
+ search = Spree::Product.ransack(
14
+ tenant_id_eq: tenant_id,
15
+ name_i_cont_all: params[:term]&.split
16
+ )
17
+
18
+ search.result.distinct
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,145 @@
1
+ module SpreeCmCommissioner
2
+ class TripQuery
3
+ attr_reader :origin_id, :destination_id, :travel_date
4
+
5
+ def initialize(origin_id:, destination_id:, travel_date: Time.zone.now)
6
+ @origin_id = origin_id
7
+ @destination_id = destination_id
8
+ @travel_date = travel_date
9
+ end
10
+
11
+ 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
+
17
+ trip_results += connections if connections
18
+ trip_results
19
+ end
20
+
21
+ def direct_trips
22
+ result = Spree::Variant
23
+ .select('spree_variants.id AS variant_id,
24
+ vendors.id AS vendor_id,
25
+ vendors.name AS vendor_name,
26
+ routes.name AS route_name,
27
+ routes.short_name AS short_name,
28
+ boarding.stop_id AS origin_id,
29
+ drop_off.stop_id AS destination_id,
30
+ boarding.stop_name AS origin,
31
+ drop_off.stop_name AS destination,
32
+ trips.departure_time AS departure_time,
33
+ trips.duration AS duration,
34
+ trips.vehicle_id AS vehicle_id'
35
+ )
36
+ .joins('INNER JOIN cm_trips AS trips ON trips.variant_id = spree_variants.id')
37
+ .joins('INNER JOIN cm_trip_stops AS boarding ON boarding.trip_id = trips.id AND boarding.stop_type = 0')
38
+ .joins('INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = trips.id AND drop_off.stop_type = 1')
39
+ .joins('INNER JOIN spree_vendors AS vendors ON vendors.id = spree_variants.vendor_id')
40
+ .joins('INNER JOIN spree_products AS routes ON routes.id = spree_variants.product_id')
41
+ .where('trips.origin_id = ? AND trips.destination_id = ?', origin_id, destination_id)
42
+
43
+ result.map do |trip|
44
+ trip_result_options = {
45
+ trip_id: trip[:variant_id],
46
+ vendor_id: trip[:vendor_id],
47
+ vendor_name: trip[:vendor_name],
48
+ route_name: trip[:route_name],
49
+ short_name: trip[:short_name],
50
+ detail: trip[:variant_id],
51
+ origin_id: trip[:origin_id],
52
+ origin: trip[:origin],
53
+ destination_id: trip[:destination_id],
54
+ destination: trip[:destination],
55
+ vehicle_id: trip[:vehicle_id],
56
+ departure_time: trip[:departure_time],
57
+ duration: trip[:duration]
58
+ }
59
+ SpreeCmCommissioner::TripResult.new(trip_result_options)
60
+ end
61
+ end
62
+
63
+ def connected_trips # rubocop:disable Metrics/MethodLength
64
+ result = SpreeCmCommissioner::TripConnection
65
+ .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_places trip1_origin ON trip1_origin.id = trip1.origin_id
73
+ INNER JOIN cm_places trip2_origin ON trip2_origin.id = trip2.origin_id
74
+ INNER JOIN cm_places trip2_destination ON trip2_destination.id = trip2.destination_id
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'
77
+ )
78
+ .select('cm_trip_connections.id AS id,
79
+ trip1.variant_id AS trip1_id,
80
+ trip1.origin_id AS trip1_origin_id,
81
+ trip1.destination_id AS trip1_destination_id,
82
+ trip1.departure_time AS trip1_departure_time,
83
+ trip1.duration AS trip1_duration,
84
+ routes1.short_name AS route1_short_name,
85
+ routes1.name AS route1_name,
86
+ trip1.vehicle_id AS trip1_vehicle,
87
+ vendor1.name AS trip1_vendor_name,
88
+ trip2.variant_id AS trip2_id,
89
+ trip2.origin_id AS trip2_origin_id,
90
+ trip2.destination_id AS trip2_destination_id,
91
+ trip2.departure_time AS trip2_departure_time,
92
+ trip2.duration AS trip2_duration,
93
+ trip2.vehicle_id AS trip2_vehicle,
94
+ routes2.short_name AS route2_short_name,
95
+ routes2.name AS route2_name,
96
+ trip1_origin.name AS trip1_origin,
97
+ trip2_origin.name AS trip2_origin,
98
+ trip2_destination.name AS trip2_destination,
99
+ vendor2.name AS trip2_vendor_name'
100
+ )
101
+ .where('trip1_origin.id = ? AND trip2_destination.id = ?', origin_id, destination_id)
102
+
103
+ return [] if result.blank?
104
+
105
+ build_trip_query_result(result)
106
+ end
107
+
108
+ private
109
+
110
+ def build_trip_query_result(connections)
111
+ connections.map do |trip|
112
+ from_trip = {
113
+ trip_id: trip[:trip1_id],
114
+ origin_id: trip[:trip1_origin_id],
115
+ destination_id: trip[:trip1_destination_id],
116
+ departure_time: trip[:trip1_departure_time],
117
+ duration: trip[:trip1_duration],
118
+ vendor_name: trip[:trip1_vendor_name],
119
+ route_name: trip[:route1_name],
120
+ short_name: trip[:route1_short_name],
121
+ vehicle_id: trip[:trip1_vehicle],
122
+ origin: trip[:trip1_origin],
123
+ destination: trip[:trip2_origin]
124
+ }
125
+ to_trip = {
126
+ trip_id: trip[:trip2_id],
127
+ origin_id: trip[:trip2_origin_id],
128
+ destination_id: trip[:trip2_destination_id],
129
+ departure_time: trip[:trip2_departure_time],
130
+ duration: trip[:trip2_duration],
131
+ vendor_name: trip[:trip2_vendor_name],
132
+ route_name: trip[:route2_name],
133
+ short_name: trip[:route2_short_name],
134
+ vehicle_id: trip[:trip2_vehicle],
135
+ origin: trip[:trip2_origin],
136
+ destination: trip[:trip2_destination]
137
+ }
138
+ data = [SpreeCmCommissioner::TripResult.new(from_trip),
139
+ SpreeCmCommissioner::TripResult.new(to_trip)
140
+ ]
141
+ SpreeCmCommissioner::TripQueryResult.new(data, connection_id: trip[:id])
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,12 @@
1
+ module Spree
2
+ module V2
3
+ module Tenant
4
+ class IdCardSerializer < BaseSerializer
5
+ attributes :card_type
6
+
7
+ has_one :front_image, serializer: Spree::V2::Tenant::AssetSerializer
8
+ has_one :back_image, serializer: Spree::V2::Tenant::AssetSerializer
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Spree
2
+ module V2
3
+ module Tenant
4
+ class ResetPasswordSerializer < BaseSerializer
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module V2
3
+ module Tenant
4
+ class TicketSerializer < BaseSerializer
5
+ include ::Spree::Api::V2::DisplayMoneyHelper
6
+
7
+ attributes :name
8
+
9
+ attribute :purchasable do |ticket|
10
+ value = ticket.purchasable?
11
+ [true, false].include?(value) ? value : nil
12
+ end
13
+
14
+ attribute :in_stock do |ticket|
15
+ value = ticket.in_stock?
16
+ [true, false].include?(value) ? value : nil
17
+ end
18
+
19
+ attribute :available, &:available?
20
+
21
+ attribute :display_price do |ticket, params|
22
+ display_price(ticket, params[:currency])
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module V2
3
+ module Tenant
4
+ class UserContactSerializer < BaseSerializer
5
+ set_type :user
6
+
7
+ attributes :email, :phone_number
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,52 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+
4
+ module SpreeCmCommissioner
5
+ class AesEncryptionService
6
+ ALGORITHM = 'aes-256-gcm'.freeze
7
+ KEY_LENGTH = 32
8
+ IV_LENGTH = 12
9
+ TAG_LENGTH = 16
10
+
11
+ def self.encrypt(plaintext, key)
12
+ validate_key!(key)
13
+
14
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
15
+ cipher.encrypt
16
+ cipher.key = key.b[0, KEY_LENGTH]
17
+ iv = cipher.random_iv
18
+ cipher.iv = iv
19
+
20
+ ciphertext = cipher.update(plaintext) + cipher.final
21
+ tag = cipher.auth_tag
22
+
23
+ combined = iv + ciphertext + tag
24
+ Base64.strict_encode64(combined)
25
+ end
26
+
27
+ def self.decrypt(encrypted_text, key)
28
+ validate_key!(key)
29
+
30
+ combined = Base64.decode64(encrypted_text)
31
+ iv = combined[0, IV_LENGTH]
32
+ tag = combined[-TAG_LENGTH..]
33
+ ciphertext = combined[IV_LENGTH...-TAG_LENGTH]
34
+
35
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
36
+ cipher.decrypt
37
+ cipher.key = key.b[0, KEY_LENGTH]
38
+ cipher.iv = iv
39
+ cipher.auth_tag = tag
40
+
41
+ cipher.update(ciphertext) + cipher.final
42
+ rescue OpenSSL::Cipher::CipherError => e
43
+ raise "Decryption failed: #{e.message}"
44
+ end
45
+
46
+ def self.validate_key!(key)
47
+ return if key.is_a?(String) && key.bytesize >= KEY_LENGTH
48
+
49
+ raise ArgumentError, "Key must be a string of at least #{KEY_LENGTH} bytes"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ module SpreeCmCommissioner
2
+ class RolePermissionsConstructor
3
+ def initialize(params)
4
+ @params = params[:role_permissions_attributes] || {}
5
+ end
6
+
7
+ def call
8
+ construct_role_permissions
9
+ end
10
+
11
+ private
12
+
13
+ def construct_role_permissions
14
+ role_permissions_attributes = {}
15
+
16
+ @params.each do |index, role_permission_attributes|
17
+ role_permission_attributes = role_permission_attributes.to_h.symbolize_keys
18
+ selected = role_permission_attributes.delete(:selected) == 'true'
19
+ role_permission_persisted = role_permission_attributes.key?(:id)
20
+
21
+ if role_permission_attributes[:permission_attributes]
22
+ permission_attributes = role_permission_attributes[:permission_attributes].to_h.symbolize_keys
23
+ permission_persisted = permission_attributes.key?(:id)
24
+
25
+ if permission_persisted
26
+ role_permission_attributes[:permission_id] = permission_attributes[:id]
27
+ role_permission_attributes.delete(:permission_attributes)
28
+ end
29
+ end
30
+
31
+ if selected
32
+ role_permissions_attributes[index] = role_permission_attributes
33
+ elsif role_permission_persisted
34
+ role_permission_attributes[:_destroy] = 1
35
+ role_permissions_attributes[index] = role_permission_attributes
36
+ end
37
+ end
38
+
39
+ role_permissions_attributes
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,57 @@
1
+ module SpreeCmCommissioner
2
+ class RolePermissionsLoader
3
+ attr_reader :role, :permissions_config, :role_permissions
4
+
5
+ def initialize(role, permissions_config)
6
+ @role = role
7
+ @permissions_config = permissions_config
8
+ @role_permissions = []
9
+ end
10
+
11
+ def call
12
+ load_role_permissions
13
+ role_permissions.uniq(&:slug)
14
+ end
15
+
16
+ private
17
+
18
+ def load_role_permissions
19
+ processed_entries = {}
20
+
21
+ permissions_config[:grouped].each do |entry, groups|
22
+ next if processed_entries[entry]
23
+
24
+ groups.each do |group_name, details|
25
+ next if details['actions'].empty?
26
+
27
+ permission = find_or_initialize_permission(entry, group_name)
28
+ role_permission = build_role_permission(permission)
29
+ @role_permissions << role_permission
30
+ end
31
+
32
+ processed_entries[entry] = true
33
+ end
34
+ end
35
+
36
+ def find_or_initialize_permission(entry, action)
37
+ SpreeCmCommissioner::Permission.where(
38
+ entry: entry,
39
+ action: action
40
+ ).first_or_initialize
41
+ end
42
+
43
+ def build_role_permission(permission)
44
+ if @role.persisted? && permission.persisted?
45
+ SpreeCmCommissioner::RolePermission.where(
46
+ role: @role,
47
+ permission: permission
48
+ ).first_or_initialize
49
+ else
50
+ SpreeCmCommissioner::RolePermission.new(
51
+ role: @role,
52
+ permission: permission
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+
4
+ module SpreeCmCommissioner
5
+ class RsaService
6
+ def initialize(private_key: nil, public_key: nil)
7
+ @private_key = private_key
8
+ @public_key = public_key
9
+ end
10
+
11
+ def sign(data)
12
+ raise 'Private key is required to sign data' unless @private_key
13
+
14
+ private_key_object = OpenSSL::PKey::RSA.new(@private_key)
15
+ signature = private_key_object.sign(OpenSSL::Digest.new('SHA256'), data)
16
+ "#{data}.#{Base64.strict_encode64(signature)}"
17
+ end
18
+
19
+ def verify(data, signature)
20
+ raise 'Public key is required to verify signature' unless @public_key
21
+
22
+ public_key_object = OpenSSL::PKey::RSA.new(@public_key)
23
+ signature_bytes = Base64.decode64(signature)
24
+ public_key_object.verify(OpenSSL::Digest.new('SHA256'), signature_bytes, data)
25
+ end
26
+ end
27
+ end
@@ -35,6 +35,9 @@ module SpreeCmCommissioner
35
35
  when 'telegram_web_app_auth'
36
36
  options = { telegram_init_data: params[:telegram_init_data], telegram_bot_username: params[:tg_bot] }
37
37
  SpreeCmCommissioner::UserTelegramWebAppAuthenticator.call(options)
38
+ when 'vattanac_bank_web_app_auth'
39
+ options = { session_id: params[:session_id] }
40
+ SpreeCmCommissioner::UserVattanacBankWebAppAuthenticator.call(options)
38
41
  end
39
42
  end
40
43
 
@@ -42,6 +45,7 @@ module SpreeCmCommissioner
42
45
  return 'login_auth' if params.key?(:username) && params.key?(:password)
43
46
  return 'social_auth' if params.key?(:id_token)
44
47
  return 'telegram_web_app_auth' if params.key?(:telegram_init_data) && params.key?(:tg_bot)
48
+ return 'vattanac_bank_web_app_auth' if params.key?(:session_id)
45
49
 
46
50
  raise exception(I18n.t('authenticator.invalid_or_missing_params'))
47
51
  end
@@ -1,5 +1,5 @@
1
1
  <% content_for :page_actions do %>
2
- <%= button_link_to Spree.t(:add_new_events), new_admin_user_event_path, class: "btn-success", icon: 'add.svg', id: 'admin_new_operator_link' %>
2
+ <%= button_link_to Spree.t(:add_new_events), new_admin_user_event_path(user_id: @user.id), class: "btn-success", icon: 'add.svg', id: 'admin_new_operator_link' %>
3
3
  <% end %>
4
4
 
5
5
  <%= render partial: 'spree/admin/users/tabs', locals: { current: :user_events } %>
@@ -3,7 +3,11 @@
3
3
  <% elsif @taxon.present? && @taxonomy.present? %>
4
4
  <%= render partial: 'users', locals: { user_events: @user_events, taxon: @taxon, taxonomy: @taxonomy } %>
5
5
  <% else %>
6
- <small class="form-text text-muted">
7
- <%= raw I18n.t('user_taxon.empty_info') %>
8
- </small>
6
+ <% content_for :page_actions do %>
7
+ <%= button_link_to Spree.t(:add_new_events), new_admin_user_event_path(user_id: @user.id), class: "btn-success", icon: 'add.svg', id: 'admin_new_operator_link' %>
8
+ <% end %>
9
+ <%= render partial: 'spree/admin/users/tabs', locals: { current: :user_events } %>
10
+ <small class="form-text text-muted">
11
+ <%= raw I18n.t('user_taxon.empty_info') %>
12
+ </small>
9
13
  <% end %>
data/config/routes.rb CHANGED
@@ -483,7 +483,9 @@ Spree::Core::Engine.add_routes do
483
483
  patch :associate
484
484
  end
485
485
  resource :cart_guests, only: %i[create destroy]
486
- resources :guests, only: %i[create update show]
486
+ resources :guests, only: %i[create update show] do
487
+ resources :id_cards, only: %i[create update destroy]
488
+ end
487
489
 
488
490
  resource :checkout, controller: :checkout, only: %i[update] do
489
491
  patch :next
@@ -508,11 +510,16 @@ Spree::Core::Engine.add_routes do
508
510
  resource :profile_images, only: %i[update destroy]
509
511
  resources :line_items, only: %i[index show]
510
512
  resources :guest_card_classes
513
+ resources :tickets, only: :index
514
+
515
+ resource :reset_passwords, only: [:update]
516
+ resource :change_passwords, only: [:update]
517
+ resource :user_contacts, only: [:update]
511
518
  end
512
519
 
513
520
  namespace :storefront do
514
521
  resources :waiting_room_sessions, only: :create
515
-
522
+ resources :vattanac_banks, only: %i[create]
516
523
  resource :cart, controller: :cart, only: %i[show create destroy] do
517
524
  patch :restart_checkout_flow
518
525
  end
@@ -1,5 +1,5 @@
1
1
  class AddDescriptionToSpreeRoles < ActiveRecord::Migration[7.0]
2
2
  def change
3
- add_column :spree_roles, :description, :text
3
+ add_column :spree_roles, :description, :text, if_not_exists: true
4
4
  end
5
5
  end
@@ -1,6 +1,6 @@
1
1
  class AddVendorIdToSpreeRoles < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  add_column :spree_roles, :vendor_id, :integer, if_not_exists: true
4
- add_index :spree_roles, :vendor_id
4
+ add_index :spree_roles, :vendor_id, if_not_exists: true
5
5
  end
6
6
  end
@@ -0,0 +1,5 @@
1
+ class AddPresentationToSpreeRole < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_column :spree_roles, :presentation, :string, if_not_exists: true
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class AddUniqueConstraintToNameInSpreeRoles < ActiveRecord::Migration[7.0]
2
+ def change
3
+ unless index_exists?(:spree_roles, :name, unique: true)
4
+ add_index :spree_roles, :name, unique: true
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class AddVariantIdToCmTrips < ActiveRecord::Migration[7.0]
2
+ def change
3
+ unless column_exists?(:cm_trips, :variant_id)
4
+ add_reference :cm_trips, :variant, foreign_key: { to_table: :spree_variants }
5
+ end
6
+ end
7
+ end
@@ -50,6 +50,15 @@ FactoryBot.define do
50
50
  trip.permanent_stock = 10
51
51
  trip.stock_items = [create(:stock_item, variant: trip, stock_location: route.vendor.stock_locations.first)]
52
52
  trip.stock_items.first.adjust_count_on_hand(10)
53
+
54
+ # option types
55
+ route.option_types << create(:cm_option_type, :origin)
56
+ route.option_types << create(:cm_option_type, :destination)
57
+ route.option_types << create(:cm_option_type, :departure_time)
58
+ route.option_types << create(:cm_option_type, :duration_in_hours)
59
+ route.option_types << create(:cm_option_type, :duration_in_minutes)
60
+ route.option_types << create(:cm_option_type, :allow_seat_selection)
61
+ route.option_types << create(:cm_option_type, :vehicle)
53
62
  end
54
63
  end
55
64
 
@@ -0,0 +1,11 @@
1
+ FactoryBot.define do
2
+ factory :permission, class: 'SpreeCmCommissioner::Permission' do
3
+ entry { 'organizer/events' }
4
+ action { 'view_events' }
5
+ end
6
+
7
+ factory :role_permission, class: 'SpreeCmCommissioner::RolePermission' do
8
+ association :role, factory: :role
9
+ association :permission, factory: :permission
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ FactoryBot.define do
2
+ factory :trip_connection, class: 'SpreeCmCommissioner::TripConnection' do
3
+ association :from_trip, factory: :variant
4
+ association :to_trip, factory: :variant
5
+ end
6
+ end