spree_cm_commissioner 1.13.0 → 1.15.0.pre.beta1

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 (77) 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 +21 -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 +120 -0
  21. data/app/finders/spree_cm_commissioner/products/find_decorator.rb +16 -0
  22. data/app/helpers/spree_cm_commissioner/transit/order_query_helper.rb +17 -0
  23. data/app/helpers/spree_cm_commissioner/transit/trip_helper.rb +12 -0
  24. data/app/interactors/spree_cm_commissioner/create_event.rb +31 -6
  25. data/app/interactors/spree_cm_commissioner/create_ticket.rb +36 -1
  26. data/app/interactors/spree_cm_commissioner/host_matcher.rb +28 -0
  27. data/app/interactors/spree_cm_commissioner/place_decoder.rb +16 -0
  28. data/app/interactors/spree_cm_commissioner/place_service.rb +32 -0
  29. data/app/interactors/spree_cm_commissioner/user_pin_code_authenticator.rb +9 -0
  30. data/app/models/concerns/spree_cm_commissioner/json_preference_validator.rb +23 -0
  31. data/app/models/concerns/spree_cm_commissioner/kyc_bitwise.rb +3 -1
  32. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +2 -0
  33. data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +11 -0
  34. data/app/models/spree_cm_commissioner/cms_page_decorator.rb +16 -1
  35. data/app/models/spree_cm_commissioner/guest.rb +11 -2
  36. data/app/models/spree_cm_commissioner/invite_guest.rb +8 -0
  37. data/app/models/spree_cm_commissioner/order_decorator.rb +5 -0
  38. data/app/models/spree_cm_commissioner/payment_method_decorator.rb +8 -1
  39. data/app/models/spree_cm_commissioner/place.rb +3 -0
  40. data/app/models/spree_cm_commissioner/store_decorator.rb +2 -1
  41. data/app/models/spree_cm_commissioner/taxon_decorator.rb +4 -0
  42. data/app/models/spree_cm_commissioner/taxon_place.rb +12 -0
  43. data/app/models/spree_cm_commissioner/tenant.rb +5 -0
  44. data/app/models/spree_cm_commissioner/user_decorator.rb +14 -3
  45. data/app/models/spree_cm_commissioner/vendor_decorator.rb +7 -3
  46. data/app/models/spree_cm_commissioner/vendor_place.rb +11 -3
  47. data/app/overrides/spree/admin/stores/_form/store_preferences.html.erb.deface +85 -12
  48. data/app/overrides/spree/admin/variants/_form/kyc_field.html.erb.deface +3 -4
  49. data/app/queries/spree_cm_commissioner/reservation_query.rb +12 -0
  50. data/app/queries/spree_cm_commissioner/trip_query.rb +35 -25
  51. data/app/serializers/spree/v2/organizer/invite_guest_serializer.rb +1 -1
  52. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +1 -0
  53. data/app/serializers/spree/v2/tenant/cart_serializer.rb +8 -0
  54. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +1 -1
  55. data/app/services/spree_cm_commissioner/organizer/export_invite_guest_csv_service.rb +63 -0
  56. data/app/services/spree_cm_commissioner/payment_method_type_mapper.rb +11 -0
  57. data/app/views/spree/admin/tenants/_form.html.erb +68 -2
  58. data/config/initializers/spree_permitted_attributes.rb +3 -0
  59. data/config/routes.rb +7 -0
  60. data/db/migrate/20250127081646_add_preferences_to_cm_tenants.rb +7 -0
  61. data/db/migrate/20250527043231_create_spree_commissioner_taxon_places.rb +15 -0
  62. data/db/migrate/20250528073748_remove_unique_slug_index_on_spree_cms_pages.rb +12 -0
  63. data/db/migrate/20250528074043_add_unique_index_on_slug_tenant_id_deleted_at_to_spree_cms_pages.rb +13 -0
  64. data/db/migrate/20250528090330_add_place_type_to_cm_vendor_place.rb +6 -0
  65. data/db/migrate/20250602094751_add_host_to_cm_tenants.rb +6 -0
  66. data/db/migrate/20250609015216_remove_claimed_at_and_expires_at_from_cm_invite_guest.rb +6 -0
  67. data/db/migrate/20250610080108_add_contact_to_cm_guests.rb +5 -0
  68. data/db/migrate/20250611023548_add_country_code_to_guests.rb +5 -0
  69. data/db/migrate/20250612035937_add_intel_phone_number_to_guests.rb +5 -0
  70. data/lib/spree_cm_commissioner/test_helper/factories/cms_page_factory.rb +11 -0
  71. data/lib/spree_cm_commissioner/test_helper/factories/invite_guests_factory.rb +6 -0
  72. data/lib/spree_cm_commissioner/test_helper/factories/tenant_factory.rb +1 -0
  73. data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +17 -4
  74. data/lib/spree_cm_commissioner/version.rb +1 -1
  75. data/lib/spree_cm_commissioner.rb +1 -0
  76. data/spree_cm_commissioner.gemspec +2 -0
  77. metadata +66 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3da0bd043e4ceca1f57afecc1bab6fbc58c960ba296f7fffa42b449a2475f8f8
4
- data.tar.gz: 974c8e19e026fa90254594447a9735be039371ae6d0bc11a24c9cdf3b86dfd8c
3
+ metadata.gz: b81b8e884fba65cb5081f039fe5c4eafbe2873a355c69f844dbbd8205b70b3fe
4
+ data.tar.gz: 6c6648d65bc80caeabb0d6fab484086647b9c80bcebb4d36e3160210be7aa3d4
5
5
  SHA512:
6
- metadata.gz: 4b1d418758d9880e50dd1481b095f4cad76cc106ca0a950b694d2a87c053bf7f48655029c52939f33bf099db0a7d84048f0482d488a20ac720f1b6f8015a687e
7
- data.tar.gz: 93b3ffb94424ba4a4ed46e5cde3303dc33c54a58417dd1a0750a5cb56d3638778d1fa5ceb75cf812617c33e12ae3c38c6402b7719ddfa53b2ce9567a8bb860e4
6
+ metadata.gz: 37e68c1ba296971013b060663e13758907ff0a567c413f1899f6b1f88d4f5eb7a17ceea8695ded4dbca9dd3be0d06669038fbf19e81ff230446b8c7e651a395d
7
+ data.tar.gz: 85f7a966e0396433617edc3375909fb92ca88bae2118fec2af8a99bd2d16d6e9b1af013ed3ad3f0ca08539f8395cde3e5312ea792e4b4f3974ccd98b62fbefcb
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (1.13.0)
37
+ spree_cm_commissioner (1.15.0.pre.beta1)
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
@@ -16,9 +16,12 @@ module Spree
16
16
  return render_error(:fully_claimed) if @invite_guest.fully_claimed?
17
17
 
18
18
  guest = @line_item.guests.new(guest_params)
19
+ guest.event_id = @invite_guest.event_id
19
20
 
20
21
  if guest.save
21
22
  @invite_guest.update(claimed_status: :claimed) if @line_item.guests.count == @invite_guest.quantity
23
+ send_guest_claimed_invitation_telegram_alert_to_vendor(guest)
24
+
22
25
  render json: SpreeCmCommissioner::V2::Storefront::GuestSerializer.new(guest.reload).serializable_hash
23
26
  else
24
27
  render_error_payload(guest.errors.full_messages.to_sentence)
@@ -27,6 +30,24 @@ module Spree
27
30
 
28
31
  private
29
32
 
33
+ def send_guest_claimed_invitation_telegram_alert_to_vendor(guest)
34
+ title = '📣 --- [NEW GUEST CLAIMED INVITATION] ---' # Style/StringLiterals
35
+ chat_id = guest.event.vendor.preferred_telegram_chat_id
36
+ return if chat_id.blank?
37
+
38
+ factory = SpreeCmCommissioner::InviteGuestClaimedTelegramMessageFactory.new(
39
+ title: title,
40
+ order: @invite_guest.order,
41
+ guest: guest,
42
+ vendor: guest.event.vendor
43
+ )
44
+ SpreeCmCommissioner::TelegramNotificationSenderJob.perform_later(
45
+ chat_id: chat_id,
46
+ message: factory.message,
47
+ parse_mode: factory.parse_mode
48
+ )
49
+ end
50
+
30
51
  def load_invite_guest_by_token
31
52
  @invite_guest = SpreeCmCommissioner::InviteGuest.find_by(token: params[:id])
32
53
  rescue ActiveRecord::RecordNotFound
@@ -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
@@ -0,0 +1,120 @@
1
+ # 🎟️ --- [TICKET CLAIMED] ---
2
+ #
3
+ # Order Number:
4
+ # <code>R621016753</code>
5
+ #
6
+ # ✅ <b>Ticket Successfully Claimed!</b>
7
+ #
8
+ # [ x1 ]
9
+ # <b>5km Running Ticket</b>
10
+ # <i>👉 Distance: 5km, T-shirt: M</i>
11
+ # <i>🗓️ Nov 15, 2023</i>
12
+ # <i>🏪 Temple Run with Sai</i>
13
+ #
14
+ # [ x2 ]
15
+ # <b>5km Running Ticket</b>
16
+ # <i>👉 Distance: 5km, T-shirt: S</i>
17
+ # <i>🗓️ Nov 15, 2023 -> Nov 17, 2023</i>
18
+ # <i>🏪 Temple Run with Sai</i>
19
+ #
20
+ # <b>🙍 Claimed By</b>
21
+ # Name: Vaneath Awesome
22
+ # Tel: <code>+85570760548</code>
23
+ # Email: <code>admin_dev@cm.com</code>
24
+
25
+ module SpreeCmCommissioner
26
+ class InviteGuestClaimedTelegramMessageFactory < TelegramMessageFactory
27
+ attr_reader :order, :vendor, :guest, :show_details_link
28
+
29
+ def initialize(title:, order:, guest:, **options)
30
+ @order = order
31
+ @vendor = options[:vendor]
32
+ @guest = guest
33
+ @show_details_link = options[:show_details_link] || false
34
+
35
+ super(title: title, subtitle: options[:subtitle])
36
+ end
37
+
38
+ def selected_line_items
39
+ return order.line_items.for_vendor(vendor) if vendor.present?
40
+
41
+ order.line_items
42
+ end
43
+
44
+ # override
45
+ def body
46
+ text = []
47
+ text << "Order Number:\n<code>#{order.number}</code>\n"
48
+ text << "\n✅ <b>Ticket Successfully Claimed!</b>\n"
49
+ text << selected_line_items.map { |item| line_item_content(item) }.compact.join("\n\n")
50
+ text.compact.join("\n")
51
+ end
52
+
53
+ def line_item_content(line_item)
54
+ text = []
55
+
56
+ text << bold(line_item.product.name.to_s)
57
+ text << "Quantity: #{line_item.quantity}"
58
+ text << italic("👉 #{line_item.options_text}") if line_item.options_text.present?
59
+ text << italic(pretty_date_for(line_item)) if pretty_date_for(line_item).present?
60
+ text << italic("🏪 #{line_item.vendor.name}") if line_item.vendor&.name.present? && vendor.blank?
61
+
62
+ text.compact.join("\n")
63
+ end
64
+
65
+ def pretty_date_for(line_item)
66
+ return nil unless line_item.date_present?
67
+
68
+ from_date = pretty_date(line_item.from_date)
69
+ to_date = pretty_date(line_item.to_date)
70
+
71
+ if from_date == to_date
72
+ "🗓️ #{from_date}"
73
+ else
74
+ "🗓️ #{from_date} -> #{to_date}"
75
+ end
76
+ end
77
+
78
+ # override
79
+ def footer
80
+ text = []
81
+
82
+ text << bold('🙍 Claimed By')
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
+
86
+ if show_details_link && order.guests.any?
87
+ text << ''
88
+ text << 'View Tickets:'
89
+ text += generate_guests_links(order.guests)
90
+ end
91
+
92
+ text.compact.join("\n")
93
+ end
94
+
95
+ # Result:
96
+ # | No. A24 | No. A24 |
97
+ # | No. A24 | No. A24 |
98
+ # | No. A24 | No. A24 |
99
+ # | No. A24 | No. A24 |
100
+ # | No. A24 | No. A24 |
101
+ # | No. A24 |
102
+ def generate_guests_links(guests, guests_per_row = 2)
103
+ rows = (guests.size.to_f / guests_per_row).ceil
104
+ formatted_rows = []
105
+
106
+ rows.times do |i|
107
+ row_guests = guests.slice(i * guests_per_row, guests_per_row)
108
+ formatted_row = row_guests.map do |guest|
109
+ button_label = guest.seat_number.present? ? "No. #{guest.seat_number}" : "No. #{guest.formatted_bib_number || 'N/A'}"
110
+ link = Rails.application.routes.url_helpers.guest_cards_url(guest.token)
111
+ "| <a href='#{link}'>#{button_label}</a>"
112
+ end.join(' ')
113
+
114
+ formatted_rows << ("#{formatted_row} |")
115
+ end
116
+
117
+ formatted_rows
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,16 @@
1
+ module SpreeCmCommissioner
2
+ module Products
3
+ module FindDecorator
4
+ # override: to return only visble products
5
+ def by_taxons(products)
6
+ return products unless taxons?
7
+
8
+ products.joins(:classifications).where(::Spree::Classification.table_name => { taxon_id: taxons, visible: true })
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ unless Spree::Products::Find.included_modules.include?(SpreeCmCommissioner::Products::FindDecorator)
15
+ Spree::Products::Find.prepend(SpreeCmCommissioner::Products::FindDecorator)
16
+ end
@@ -0,0 +1,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
@@ -0,0 +1,12 @@
1
+ module SpreeCmCommissioner
2
+ module Transit
3
+ module TripHelper
4
+ def parse_time(time, timezone = nil)
5
+ return nil if time.blank?
6
+
7
+ formatted_time = Time.zone.parse(time.to_s)
8
+ timezone ? formatted_time.in_time_zone(timezone) : formatted_time
9
+ end
10
+ end
11
+ end
12
+ end
@@ -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