spree_cm_commissioner 1.14.0 → 1.16.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +12 -1
  3. data/README.md +43 -1
  4. data/app/assets/images/category_icons/art_icon.png +0 -0
  5. data/app/assets/images/category_icons/concert_icon.png +0 -0
  6. data/app/assets/images/category_icons/fundraising_icon.png +0 -0
  7. data/app/assets/images/category_icons/marathon_icon.png +0 -0
  8. data/app/assets/images/category_icons/online_icon.png +0 -0
  9. data/app/assets/images/category_icons/workshop_icon.png +0 -0
  10. data/app/controllers/spree/admin/users_controller_decorator.rb +13 -0
  11. data/app/controllers/spree/api/chatrace/guests_controller.rb +2 -1
  12. data/app/controllers/spree/api/v2/organizer/invite_guests_controller.rb +1 -0
  13. data/app/controllers/spree/api/v2/storefront/cart_payment_method_groups_controller.rb +12 -1
  14. data/app/controllers/spree/api/v2/storefront/guests_controller.rb +3 -1
  15. data/app/controllers/spree/api/v2/tenant/cart_controller.rb +11 -0
  16. data/app/controllers/spree/api/v2/tenant/checkout_controller.rb +1 -0
  17. data/app/controllers/spree/api/v2/tenant/order_histories_controller.rb +65 -0
  18. data/app/controllers/spree_cm_commissioner/admin/variants_controller_decorator.rb +1 -1
  19. data/app/controllers/spree_cm_commissioner/well_known_controller.rb +46 -0
  20. data/app/factory/spree_cm_commissioner/invite_guest_claimed_telegram_message_factory.rb +8 -8
  21. data/app/helpers/spree/admin/base_helper_decorator.rb +2 -2
  22. data/app/helpers/spree_cm_commissioner/transit/order_query_helper.rb +17 -0
  23. data/app/interactors/spree_cm_commissioner/create_event.rb +31 -6
  24. data/app/interactors/spree_cm_commissioner/create_ticket.rb +36 -1
  25. data/app/interactors/spree_cm_commissioner/host_matcher.rb +28 -0
  26. data/app/interactors/spree_cm_commissioner/place_decoder.rb +16 -0
  27. data/app/interactors/spree_cm_commissioner/place_service.rb +32 -0
  28. data/app/interactors/spree_cm_commissioner/user_pin_code_authenticator.rb +9 -0
  29. data/app/models/concerns/spree_cm_commissioner/json_preference_validator.rb +23 -0
  30. data/app/models/concerns/spree_cm_commissioner/kyc_bitwise.rb +3 -1
  31. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +2 -0
  32. data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +10 -0
  33. data/app/models/spree_cm_commissioner/guest.rb +11 -2
  34. data/app/models/spree_cm_commissioner/invite_guest.rb +8 -0
  35. data/app/models/spree_cm_commissioner/option_type_decorator.rb +2 -0
  36. data/app/models/spree_cm_commissioner/option_value_decorator.rb +1 -0
  37. data/app/models/spree_cm_commissioner/option_value_vehicle.rb +8 -0
  38. data/app/models/spree_cm_commissioner/order_decorator.rb +5 -0
  39. data/app/models/spree_cm_commissioner/payment_method_decorator.rb +8 -1
  40. data/app/models/spree_cm_commissioner/place.rb +3 -0
  41. data/app/models/spree_cm_commissioner/store_decorator.rb +2 -1
  42. data/app/models/spree_cm_commissioner/taxon_decorator.rb +4 -0
  43. data/app/models/spree_cm_commissioner/taxon_place.rb +12 -0
  44. data/app/models/spree_cm_commissioner/tenant.rb +5 -0
  45. data/app/models/spree_cm_commissioner/user_decorator.rb +14 -3
  46. data/app/models/spree_cm_commissioner/vehicle.rb +3 -0
  47. data/app/models/spree_cm_commissioner/vehicle_option_type.rb +6 -0
  48. data/app/models/spree_cm_commissioner/vendor_decorator.rb +7 -3
  49. data/app/models/spree_cm_commissioner/vendor_place.rb +11 -3
  50. data/app/overrides/spree/admin/stores/_form/store_preferences.html.erb.deface +85 -12
  51. data/app/overrides/spree/admin/variants/_form/kyc_field.html.erb.deface +3 -4
  52. data/app/queries/spree_cm_commissioner/reservation_query.rb +12 -0
  53. data/app/serializers/spree/v2/organizer/invite_guest_serializer.rb +1 -1
  54. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +1 -0
  55. data/app/serializers/spree/v2/tenant/cart_serializer.rb +8 -0
  56. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +1 -1
  57. data/app/services/spree_cm_commissioner/organizer/export_invite_guest_csv_service.rb +63 -0
  58. data/app/services/spree_cm_commissioner/payment_method_type_mapper.rb +11 -0
  59. data/app/views/spree/admin/tenants/_form.html.erb +59 -2
  60. data/config/initializers/spree_permitted_attributes.rb +3 -0
  61. data/config/routes.rb +7 -0
  62. data/db/migrate/20250127081646_add_preferences_to_cm_tenants.rb +7 -0
  63. data/db/migrate/20250527043231_create_spree_commissioner_taxon_places.rb +15 -0
  64. data/db/migrate/20250528090330_add_place_type_to_cm_vendor_place.rb +6 -0
  65. data/db/migrate/20250531074036_create_spree_cm_commissioner_option_value_vehicle.rb +10 -0
  66. data/db/migrate/20250531074258_create_spree_cm_commissioner_vehicle_option_types.rb +10 -0
  67. data/db/migrate/20250602094751_add_host_to_cm_tenants.rb +6 -0
  68. data/db/migrate/20250609015216_remove_claimed_at_and_expires_at_from_cm_invite_guest.rb +6 -0
  69. data/db/migrate/20250610080108_add_contact_to_cm_guests.rb +5 -0
  70. data/db/migrate/20250611023548_add_country_code_to_guests.rb +5 -0
  71. data/db/migrate/20250612035937_add_intel_phone_number_to_guests.rb +5 -0
  72. data/lib/spree_cm_commissioner/test_helper/factories/invite_guests_factory.rb +6 -0
  73. data/lib/spree_cm_commissioner/test_helper/factories/tenant_factory.rb +1 -0
  74. data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +17 -4
  75. data/lib/spree_cm_commissioner/version.rb +1 -1
  76. data/lib/spree_cm_commissioner.rb +1 -0
  77. data/spree_cm_commissioner.gemspec +2 -0
  78. metadata +62 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a83f128539c20da0667aaa913954b8d686df38054b90ea51ede36b2967a96c4b
4
- data.tar.gz: 46fa67f9aff722e83efd60ae52bb640f5ba9b2576294625a3c07093cbf0c2784
3
+ metadata.gz: 169845d2403406e562178fb3b438559b9d4cc42d93776513b5b7235399f69e2a
4
+ data.tar.gz: 5d7526b7df7150bddd97deb2989f08a014437b4c4c9efc8a318405d335e35e27
5
5
  SHA512:
6
- metadata.gz: d48ff6f2945c262955121a7f7144c5fc4ad53afa23bb2b093baaefd985346276a60b82de985b1912ec5a8fb64e6e86d37eb163638c2261446c2f85945165c3df
7
- data.tar.gz: 7e21994489d8d36559a632a0008a92ecf9902c95be865190624173b89e1c8ad6146f405744971dbd448935cf9a9557b60401f34b6d211ce833eca61880082719
6
+ metadata.gz: ba5ff297f04ed5f749794f56b8c38900d36ce76d5b78a7778c0e06007816004190745ad3664b038be3c84e30a540deb57715652ed6d8012593b57126a14dba9c
7
+ data.tar.gz: d83b272f1fe458cc17fa833e2edd1a643ffa9c25f888c8432bc4e08d10ce1b16e8a2e870b52532995448043dda00eab8473f50e8f687dbf11ee6e45eca58556e
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (1.14.0)
37
+ spree_cm_commissioner (1.16.0)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -66,6 +66,8 @@ PATH
66
66
  spree_backend (>= 4.5.0)
67
67
  spree_extension
68
68
  spree_multi_vendor (>= 2.4.1)
69
+ spree_vpago
70
+ strong_password (~> 0.0.10)
69
71
  telegram-bot
70
72
  twilio-ruby (~> 5.48.0)
71
73
 
@@ -849,6 +851,14 @@ GEM
849
851
  spree_backend (>= 4.3.0.rc1)
850
852
  spree_emails (>= 4.3.0.rc1)
851
853
  spree_extension
854
+ spree_vpago (2.0.5.pre.beta)
855
+ faraday
856
+ google-cloud-firestore
857
+ spree_api (>= 4.5)
858
+ spree_backend (>= 4.5)
859
+ spree_core (>= 4.5)
860
+ spree_extension
861
+ spree_multi_vendor (>= 2.4.1)
852
862
  sprockets (4.2.1)
853
863
  concurrent-ruby (~> 1.0)
854
864
  rack (>= 2.2.4, < 4)
@@ -866,6 +876,7 @@ GEM
866
876
  stimulus-rails (1.3.0)
867
877
  railties (>= 6.0.0)
868
878
  stringex (2.8.6)
879
+ strong_password (0.0.10)
869
880
  telegram-bot (0.15.7)
870
881
  actionpack (>= 4.0, < 7.1)
871
882
  activesupport (>= 4.0, < 7.1)
data/README.md CHANGED
@@ -123,6 +123,48 @@ EXCEPTION_TELEGRAM_BOT_TOKEN=""
123
123
  EXCEPTION_NOTIFIER_TELEGRAM_CHANNEL_ID=""
124
124
  ```
125
125
 
126
+ ## JSON Format Examples For Store and Tenant Preferences
127
+
128
+ ### Asset Links Format
129
+ For Android app integration, use the following format in your `assetlinks.json`:
130
+
131
+ ```json
132
+ [
133
+ {
134
+ "relation": ["delegate_permission/common.handle_all_urls"],
135
+ "target": {
136
+ "namespace": "android_app",
137
+ "package_name": "com.yourapp.app",
138
+ "sha256_cert_fingerprints": [
139
+ "SHA256_CERTIFICATE_FINGERPRINT_HERE"
140
+ ]
141
+ }
142
+ }
143
+ ]
144
+ ```
145
+
146
+ ### Apple App Site Association Format
147
+ For iOS app integration, use the following format in your `apple-app-site-association`:
148
+
149
+ ```json
150
+ {
151
+ "webcredentials": {
152
+ "apps": [
153
+ "TEAM_ID.com.yourapp.app"
154
+ ]
155
+ },
156
+ "applinks": {
157
+ "apps": [],
158
+ "details": [
159
+ {
160
+ "appID": "TEAM_ID.com.yourapp.app",
161
+ "paths": ["*"]
162
+ }
163
+ ]
164
+ }
165
+ }
166
+ ```
167
+
126
168
  ## Using Deface DSL (.deface files)
127
169
 
128
170
  - Make sure the path of override should match the path of view template
@@ -338,7 +380,7 @@ end
338
380
 
339
381
  ## Protected Cloudfront with signed requests
340
382
 
341
- To use signed cookies with CloudFront, you'll need to set up your CloudFront distribution to require signed cookies and then generate and distribute the signed cookies to your users. Heres how to do it using Terraform for infrastructure setup and Ruby for cookie generation.
383
+ To use signed cookies with CloudFront, you'll need to set up your CloudFront distribution to require signed cookies and then generate and distribute the signed cookies to your users. Here's how to do it using Terraform for infrastructure setup and Ruby for cookie generation.
342
384
 
343
385
  ### Generating keypair
344
386
 
@@ -5,6 +5,19 @@ module Spree
5
5
  base.before_action :build_profile, only: %i[create update]
6
6
  end
7
7
 
8
+ # override
9
+ def create
10
+ @user = Spree.user_class.new(user_params)
11
+ @user.assigned_roles = Spree::Role.where(id: params.dig(:user, :spree_role_ids)).pluck(:name) if params.dig(:user, :spree_role_ids)
12
+
13
+ if @user.save
14
+ flash[:success] = flash_message_for(@user, :successfully_created)
15
+ redirect_to spree.edit_admin_user_path(@user)
16
+ else
17
+ render :new, status: :unprocessable_entity
18
+ end
19
+ end
20
+
8
21
  private
9
22
 
10
23
  def build_profile
@@ -48,7 +48,8 @@ module Spree
48
48
  order_phone_number: order.intel_phone_number || order.customer_address&.phone,
49
49
  checked_in_at: guest.check_in&.confirmed_at,
50
50
  qr_data: guest.qr_data,
51
- order_qr_data: order.qr_data
51
+ order_qr_data: order.qr_data,
52
+ country_code: guest.country_code
52
53
  }
53
54
  end
54
55
  end
@@ -38,6 +38,7 @@ module Spree
38
38
  factory = SpreeCmCommissioner::InviteGuestClaimedTelegramMessageFactory.new(
39
39
  title: title,
40
40
  order: @invite_guest.order,
41
+ guest: guest,
41
42
  vendor: guest.event.vendor
42
43
  )
43
44
  SpreeCmCommissioner::TelegramNotificationSenderJob.perform_later(
@@ -17,7 +17,7 @@ module Spree
17
17
  # override
18
18
  def collection
19
19
  @collection ||= SpreeCmCommissioner::PaymentMethods::GroupByBank.new.execute(
20
- payment_methods: spree_current_order.available_payment_methods,
20
+ payment_methods: payment_methods,
21
21
  preferred_payment_method_id: spree_current_user&.preferred_payment_method_id
22
22
  )
23
23
  end
@@ -26,6 +26,17 @@ module Spree
26
26
  def collection_serializer
27
27
  SpreeCmCommissioner::V2::Storefront::PaymentMethodGroupSerializer
28
28
  end
29
+
30
+ private
31
+
32
+ def payment_methods
33
+ methods = spree_current_order.available_payment_methods
34
+
35
+ return methods.reject(&:mini_app?) if params[:type].blank?
36
+
37
+ type = SpreeCmCommissioner::PaymentMethodTypeMapper.call(params[:type])
38
+ methods.select { |pm| pm.mini_app? && pm.type == type }
39
+ end
29
40
  end
30
41
  end
31
42
  end
@@ -73,7 +73,9 @@ module Spree
73
73
  :address,
74
74
  :other_organization,
75
75
  :expectation,
76
- :upload_later
76
+ :upload_later,
77
+ :country_code,
78
+ :contact
77
79
  )
78
80
  end
79
81
  end
@@ -6,6 +6,7 @@ module Spree
6
6
  include Spree::Api::V2::Storefront::OrderConcern
7
7
  include Spree::Api::V2::CouponCodesHelper
8
8
  include Spree::Api::V2::Storefront::MetadataControllerConcern
9
+ include SpreeCmCommissioner::OrderConcern
9
10
 
10
11
  around_action :wrap_with_multitenant_without, except: %i[create]
11
12
 
@@ -138,6 +139,16 @@ module Spree
138
139
  end
139
140
  end
140
141
 
142
+ # Restart the checkout flow to bring the order back to the cart view
143
+ def restart_checkout_flow
144
+ spree_authorize! :update, spree_current_order, order_token
145
+
146
+ spree_current_order.restart_checkout_flow
147
+ spree_current_order.update_with_updater!
148
+
149
+ render_serialized_payload { serialized_current_order }
150
+ end
151
+
141
152
  private
142
153
 
143
154
  def wrap_with_multitenant_without(&block)
@@ -4,6 +4,7 @@ module Spree
4
4
  module Tenant
5
5
  class CheckoutController < BaseController
6
6
  include Spree::Api::V2::Storefront::OrderConcern
7
+ include SpreeCmCommissioner::OrderConcern
7
8
 
8
9
  around_action :wrap_with_multitenant_without
9
10
 
@@ -0,0 +1,65 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class OrderHistoriesController < BaseController
6
+ # GET /api/v2/tenant/order_histories
7
+ # For user - no params needed
8
+ # For guest - pass: { "order_tokens[]": [1, 2, 3] }
9
+
10
+ around_action :wrap_with_multitenant_without
11
+
12
+ def index
13
+ render_serialized_payload do
14
+ serialize_collection(paginated_collection)
15
+ end
16
+ end
17
+
18
+ # PATCH /api/v2/tenant/order_histories/:id/archive
19
+ def archive
20
+ order_token = params[:id]
21
+ order = Spree::Order.not_archived.find_by!(token: order_token)
22
+
23
+ spree_authorize! :update, order, order_token
24
+ raise CanCan::AccessDenied unless order.payment?
25
+
26
+ if order.mark_as_archive
27
+ render_serialized_payload { serialize_resource(order) }
28
+ else
29
+ render_error_payload(order.errors.full_messages.to_sentence)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def wrap_with_multitenant_without(&block)
36
+ MultiTenant.without(&block)
37
+ end
38
+
39
+ def collection
40
+ if spree_current_user.present?
41
+ spree_current_user.orders.payment.not_archived
42
+ else
43
+ order_tokens = Array(params[:order_tokens])
44
+ return Spree::Order.none if order_tokens.empty?
45
+
46
+ Spree::Order.payment.not_archived.without_user.where(token: order_tokens)
47
+ end
48
+ end
49
+
50
+ def collection_serializer
51
+ Spree::V2::Tenant::CartSerializer
52
+ end
53
+
54
+ def resource_serializer
55
+ Spree::V2::Tenant::CartSerializer
56
+ end
57
+
58
+ def model_class
59
+ Spree::Order
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -37,7 +37,7 @@ module SpreeCmCommissioner
37
37
  bit_fields = SpreeCmCommissioner::KycBitwise::BIT_FIELDS.keys
38
38
 
39
39
  if permitted_resource_params[:use_product_kyc] == '1'
40
- permitted_resource_params[:kyc] = nil
40
+ permitted_resource_params[:kyc] = @product.kyc
41
41
  else
42
42
  @kyc_result = calculate_kyc_value(params[:variant])
43
43
  permitted_resource_params[:kyc] = @kyc_result
@@ -0,0 +1,46 @@
1
+ module SpreeCmCommissioner
2
+ class WellKnownController < ApplicationController
3
+ before_action :identify_request_source
4
+ rescue_from StandardError, with: :handle_error
5
+
6
+ def show
7
+ case params[:id]
8
+ when 'assetlinks.json'
9
+ render_preference(:preferred_assetlinks)
10
+ when 'apple-app-site-association'
11
+ render_preference(:preferred_apple_app_site_association)
12
+ else
13
+ render_not_found
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def identify_request_source
20
+ host = params[:original_host].presence || request.host.downcase
21
+
22
+ result = SpreeCmCommissioner::HostMatcher.call(host: host)
23
+
24
+ @store = result.store
25
+ @tenant = result.tenant
26
+ end
27
+
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
+ def render_not_found
39
+ render json: { error: 'Not found' }, status: :not_found
40
+ end
41
+
42
+ def handle_error
43
+ render json: { error: 'Internal server error' }, status: :internal_server_error
44
+ end
45
+ end
46
+ end
@@ -24,14 +24,15 @@
24
24
 
25
25
  module SpreeCmCommissioner
26
26
  class InviteGuestClaimedTelegramMessageFactory < TelegramMessageFactory
27
- attr_reader :order, :vendor, :show_details_link
27
+ attr_reader :order, :vendor, :guest, :show_details_link
28
28
 
29
- def initialize(title:, order:, subtitle: nil, show_details_link: nil, vendor: nil)
29
+ def initialize(title:, order:, guest:, **options)
30
30
  @order = order
31
- @vendor = vendor
32
- @show_details_link = show_details_link || false
31
+ @vendor = options[:vendor]
32
+ @guest = guest
33
+ @show_details_link = options[:show_details_link] || false
33
34
 
34
- super(title: title, subtitle: subtitle)
35
+ super(title: title, subtitle: options[:subtitle])
35
36
  end
36
37
 
37
38
  def selected_line_items
@@ -79,9 +80,8 @@ module SpreeCmCommissioner
79
80
  text = []
80
81
 
81
82
  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?
83
+ text << "Name: #{guest.first_name} #{guest.last_name}" if guest.first_name.present? || guest.last_name.present?
84
+ text << "Tel: #{inline_code(guest.intel_phone_number)}" if guest.intel_phone_number.present?
85
85
 
86
86
  if show_details_link && order.guests.any?
87
87
  text << ''
@@ -1,8 +1,8 @@
1
1
  module Spree
2
2
  module Admin
3
3
  module BaseHelperDecorator
4
- def render_vector_icon(asset_path, _options = {})
5
- return inline_svg_tag asset_path, size: '24px*24px' if asset_path.end_with?('.svg')
4
+ def render_vector_icon(asset_path, options = {})
5
+ return inline_svg_tag asset_path, size: options[:size] || '24px*24px' if asset_path.end_with?('.svg')
6
6
 
7
7
  image_tag asset_path, width: '24px'
8
8
  end
@@ -0,0 +1,17 @@
1
+ module SpreeCmCommissioner
2
+ module Transit
3
+ module OrderQueryHelper
4
+ def self.transit_orders_for_vendor(vendor_id)
5
+ return Spree::Order.none if vendor_id.nil?
6
+
7
+ Spree::Order
8
+ .includes(:user, :created_by, { payments: :payment_method }, { line_items: { variant: :product } })
9
+ .joins(line_items: { variant: :product })
10
+ .where(spree_products: { product_type: :transit })
11
+ .where(spree_variants: { vendor_id: vendor_id })
12
+ .distinct
13
+ .order(created_at: :desc)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -9,7 +9,8 @@ module SpreeCmCommissioner
9
9
  assign_vendor
10
10
  assign_prototype
11
11
  create_child_taxon
12
- build_home_banner
12
+ build_asset
13
+ create_place
13
14
  end
14
15
  end
15
16
 
@@ -55,15 +56,39 @@ module SpreeCmCommissioner
55
56
  context.fail!(message: child_taxon.errors.full_messages.join(', '))
56
57
  end
57
58
 
58
- def build_home_banner
59
- return if context.params[:home_banner].nil?
59
+ def asset(banner_type, asset_param, failure_message)
60
+ return if context.params[asset_param].nil?
60
61
 
61
- banner = SpreeCmCommissioner::TaxonHomeBanner.create(
62
+ banner = banner_type.create(
62
63
  viewable: @parent_taxon,
63
- attachment: context.params[:home_banner]
64
+ attachment: context.params[asset_param]
64
65
  )
65
66
 
66
- context.fail!(message: 'Home banner upload failed') unless banner.persisted?
67
+ context.fail!(message: failure_message) unless banner.persisted?
68
+ end
69
+
70
+ def build_asset
71
+ asset(SpreeCmCommissioner::TaxonCategoryIcon, :category_icon, 'Logo upload failed') if context.params[:category_icon].present?
72
+ asset(SpreeCmCommissioner::TaxonHomeBanner, :home_banner, 'Home banner upload failed') if context.params[:home_banner].present?
73
+ context.params[:app_banner] = context.params[:home_banner] if context.params[:use_main_photo] == '1'
74
+ asset(SpreeCmCommissioner::TaxonAppBanner, :app_banner, 'App banner upload failed') if context.params[:app_banner].present?
75
+ asset(SpreeCmCommissioner::TaxonWebBanner, :web_banner, 'Web banner upload failed') if context.params[:web_banner].present?
76
+ end
77
+
78
+ def create_place
79
+ return if context.params[:base_64_content].blank?
80
+
81
+ place = SpreeCmCommissioner::PlaceDecoder.process_place(context.params[:base_64_content])
82
+
83
+ if place.save
84
+ SpreeCmCommissioner::TaxonPlace.create(
85
+ place_id: place.id,
86
+ taxon_id: @parent_taxon.id,
87
+ description: context.params[:place_description]
88
+ )
89
+ else
90
+ context.fail!(message: place.errors.full_messages.join(', '))
91
+ end
67
92
  end
68
93
  end
69
94
  end
@@ -9,6 +9,8 @@ module SpreeCmCommissioner
9
9
  set_option_value
10
10
  create_variant
11
11
  create_stock_item
12
+ upload_image
13
+ create_place
12
14
  end
13
15
  end
14
16
 
@@ -75,6 +77,37 @@ module SpreeCmCommissioner
75
77
  context.fail!(message: @stock_item.errors.full_messages.join(', '))
76
78
  end
77
79
 
80
+ def upload_image
81
+ return if context.params[:ticket_image].blank?
82
+
83
+ @image = @ticket.master.images.new(attachment: context.params[:ticket_image])
84
+ return if @image.save
85
+
86
+ context.fail!(message: @image.errors.full_messages.join(', '))
87
+ end
88
+
89
+ def create_place
90
+ if context.params[:base_64_content].present?
91
+ place = SpreeCmCommissioner::PlaceDecoder.process_place(context.params[:base_64_content])
92
+
93
+ if place.save
94
+ product_place = SpreeCmCommissioner::ProductPlace.create(
95
+ place_id: place.id,
96
+ product_id: @ticket.id
97
+ )
98
+ context.fail!(message: product_place.errors.full_messages.join(', ')) unless product_place.persisted?
99
+ else
100
+ context.fail!(message: place.errors.full_messages.join(', '))
101
+ end
102
+ elsif context.params[:place_id].present?
103
+ product_place = SpreeCmCommissioner::ProductPlace.create(
104
+ place_id: context.params[:place_id],
105
+ product_id: @ticket.id
106
+ )
107
+ context.fail!(message: product_place.errors.full_messages.join(', ')) unless product_place.persisted?
108
+ end
109
+ end
110
+
78
111
  def ticket_params
79
112
  {
80
113
  name: context.params[:name],
@@ -88,7 +121,9 @@ module SpreeCmCommissioner
88
121
  vendor_id: context.params[:vendor_id],
89
122
  status: context.params[:status],
90
123
  shipping_category_id: context.params[:shipping_category_id],
91
- option_type_ids: context.params[:option_type_ids]
124
+ option_type_ids: context.params[:option_type_ids],
125
+ allow_anonymous_booking: context.params[:allow_anonymous_booking],
126
+ allow_self_check_in: context.params[:allow_self_check_in]
92
127
  }
93
128
  end
94
129
  end
@@ -0,0 +1,28 @@
1
+ module SpreeCmCommissioner
2
+ class HostMatcher < BaseInteractor
3
+ delegate :host, to: :context
4
+
5
+ def call
6
+ normalized_host = normalize_host(host)
7
+
8
+ context.store = find_store(normalized_host)
9
+ return if context.store.present?
10
+
11
+ context.tenant = find_tenant(normalized_host)
12
+ end
13
+
14
+ private
15
+
16
+ def normalize_host(host)
17
+ host.sub(/^www\./, '')
18
+ end
19
+
20
+ def find_store(normalized_host)
21
+ Spree::Store.find_by(code: normalized_host)
22
+ end
23
+
24
+ def find_tenant(normalized_host)
25
+ SpreeCmCommissioner::Tenant.find_by(host: "https://#{normalized_host}")
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ module SpreeCmCommissioner
2
+ class PlaceDecoder
3
+ def self.process_place(base_64_content)
4
+ begin
5
+ place_data = JSON.parse(Base64.strict_decode64(base_64_content))
6
+ rescue ArgumentError, JSON::ParserError => e
7
+ Rails.logger.error("Failed to decode and parse place data: #{e.message}")
8
+ return nil
9
+ end
10
+
11
+ place = SpreeCmCommissioner::Place.where(reference: place_data['reference']).first_or_initialize
12
+ place.assign_attributes(place_data)
13
+ place
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ module SpreeCmCommissioner
2
+ class PlaceService
3
+ def initialize(model:, place_class:, params:)
4
+ @model = model # e.g., Taxon or Product
5
+ @place_class = place_class # e.g., TaxonPlace or ProductPlace
6
+ @params = params
7
+ end
8
+
9
+ def call
10
+ @base64_content = @params[:base_64_content]
11
+ @place_name = @params[:place_name]
12
+
13
+ if @base64_content.blank?
14
+ existing_place = @place_class
15
+ existing_place.delete if existing_place && @place_name.blank?
16
+ return
17
+ end
18
+
19
+ place = PlaceDecoder.process_place(@base64_content)
20
+ return if place.nil?
21
+
22
+ if place.save # rubocop:disable Style/GuardClause
23
+ existing_place = @place_class
24
+ if existing_place.present?
25
+ existing_place.update(place_id: place.id)
26
+ else
27
+ @model.places << place
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -33,6 +33,7 @@ module SpreeCmCommissioner
33
33
 
34
34
  user = Spree::User.new(create_options)
35
35
  user.confirmed_at = Time.zone.now
36
+ assign_role(user) if context.role.present?
36
37
 
37
38
  if user.save
38
39
  context.user = user
@@ -41,6 +42,14 @@ module SpreeCmCommissioner
41
42
  end
42
43
  end
43
44
 
45
+ def assign_role(user)
46
+ role = Spree::Role.find_by(name: context.role)
47
+ return unless role
48
+
49
+ user.assigned_roles = [role.name]
50
+ user.spree_roles << role
51
+ end
52
+
44
53
  def mark_pin_code_expired!
45
54
  SpreeCmCommissioner::PinCode.mark_expired!(context.pin_code_token)
46
55
  end
@@ -0,0 +1,23 @@
1
+ module SpreeCmCommissioner
2
+ module JsonPreferenceValidator
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ validate :validate_json_preferences
7
+ end
8
+
9
+ private
10
+
11
+ def validate_json_preferences
12
+ %i[preferred_assetlinks preferred_apple_app_site_association].each do |pref|
13
+ next if send(pref).blank?
14
+
15
+ begin
16
+ JSON.parse(send(pref))
17
+ rescue JSON::ParserError => e
18
+ errors.add(pref, "is not valid JSON: #{e.message}")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end