spree_cm_commissioner 1.8.0 → 1.8.2

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/Gemfile.lock +1 -1
  4. data/app/assets/images/mailer/bookme-plus_light.svg +1 -0
  5. data/app/controllers/spree/admin/taxons_controller_decorator.rb +5 -0
  6. data/app/controllers/spree/api/v2/organizer/invites_controller.rb +35 -0
  7. data/app/controllers/spree/api/v2/tenant/customer_notifications_controller.rb +20 -0
  8. data/app/controllers/spree/api/v2/tenant/line_items_controller.rb +56 -0
  9. data/app/controllers/spree/api/v2/tenant/notifications_controller.rb +55 -0
  10. data/app/controllers/spree/api/v2/tenant/profile_images_controller.rb +52 -0
  11. data/app/controllers/spree/api/v2/tenant/s3_signed_urls_controller.rb +29 -0
  12. data/app/interactors/spree_cm_commissioner/crew_inviter.rb +63 -0
  13. data/app/interactors/spree_cm_commissioner/customer_notification_sender.rb +10 -5
  14. data/app/interactors/spree_cm_commissioner/profile_image_destroyer.rb +16 -0
  15. data/app/mailers/spree_cm_commissioner/crew_invite_mailer.rb +13 -0
  16. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +6 -1
  17. data/app/models/spree_cm_commissioner/crew_invite.rb +7 -0
  18. data/app/models/spree_cm_commissioner/invite.rb +22 -0
  19. data/app/models/spree_cm_commissioner/invite_user_event.rb +5 -0
  20. data/app/models/spree_cm_commissioner/invite_user_taxon.rb +11 -0
  21. data/app/models/spree_cm_commissioner/line_item_decorator.rb +1 -1
  22. data/app/models/spree_cm_commissioner/order_decorator.rb +1 -1
  23. data/app/models/spree_cm_commissioner/place.rb +2 -0
  24. data/app/models/spree_cm_commissioner/product_decorator.rb +4 -0
  25. data/app/models/spree_cm_commissioner/taxon_decorator.rb +5 -0
  26. data/app/models/spree_cm_commissioner/taxon_video_banner.rb +4 -0
  27. data/app/models/spree_cm_commissioner/user_decorator.rb +4 -0
  28. data/app/models/spree_cm_commissioner/variant_options.rb +23 -0
  29. data/app/models/spree_cm_commissioner/video.rb +15 -0
  30. data/app/overrides/spree/admin/taxons/_form/assets_form.html.erb.deface +19 -0
  31. data/app/overrides/spree/admin/taxons/_form/available_on.html.erb.deface +18 -0
  32. data/app/serializers/spree/v2/organizer/asset_serializer.rb +11 -0
  33. data/app/serializers/spree/v2/organizer/invite_serializer.rb +11 -0
  34. data/app/serializers/spree/v2/organizer/taxon_serializer.rb +10 -0
  35. data/app/serializers/spree/v2/organizer/user_serializer.rb +9 -0
  36. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +2 -1
  37. data/app/serializers/spree/v2/tenant/customer_notification_serializer.rb +11 -0
  38. data/app/serializers/spree/v2/tenant/digital_link_serializer.rb +9 -0
  39. data/app/serializers/spree/v2/tenant/image_serializer.rb +11 -0
  40. data/app/serializers/spree/v2/tenant/line_item_serializer.rb +35 -0
  41. data/app/serializers/spree/v2/tenant/notification_serializer.rb +11 -0
  42. data/app/serializers/spree/v2/tenant/option_type_serializer.rb +12 -0
  43. data/app/serializers/spree/v2/tenant/option_value_serializer.rb +17 -0
  44. data/app/serializers/spree/v2/tenant/order_serializer.rb +8 -0
  45. data/app/serializers/spree/v2/tenant/s3_signed_url_serializer.rb +9 -0
  46. data/app/serializers/spree/v2/tenant/stock_item_serializer.rb +11 -0
  47. data/app/serializers/spree/v2/tenant/stock_location_serializer.rb +11 -0
  48. data/app/serializers/spree/v2/tenant/user_profile_serializer.rb +8 -0
  49. data/app/serializers/spree/v2/tenant/variant_serializer.rb +46 -0
  50. data/app/serializers/spree_cm_commissioner/v2/storefront/video_serializer.rb +9 -0
  51. data/app/views/spree/admin/customer_notifications/_form.html.erb +8 -0
  52. data/app/views/spree_cm_commissioner/crew_invite_mailer/_mailer_stylesheets.html.erb +82 -0
  53. data/app/views/spree_cm_commissioner/crew_invite_mailer/send_crew_invite_email.html.erb +33 -0
  54. data/app/views/spree_cm_commissioner/layouts/crew_invite_mailer.html.erb +15 -0
  55. data/config/initializers/spree_permitted_attributes.rb +1 -0
  56. data/config/locales/en.yml +10 -1
  57. data/config/locales/km.yml +10 -1
  58. data/config/routes.rb +15 -0
  59. data/db/migrate/20250304101907_create_spree_cm_commissioner_invite.rb +12 -0
  60. data/db/migrate/20250304101946_create_spree_cm_commissioner_invite_user_taxon.rb +11 -0
  61. data/db/migrate/20250307013422_add_parent_id_to_cm_place.rb +9 -0
  62. data/db/migrate/20250307073003_add_tenant_id_to_cm_customer_notifications.rb +6 -0
  63. data/db/migrate/20250307083809_remove_tenant_id_from_cm_notifications.rb +6 -0
  64. data/db/migrate/20250314013434_add_available_on_to_spree_taxons.rb +5 -0
  65. data/lib/spree_cm_commissioner/test_helper/factories/invite_factory.rb +7 -0
  66. data/lib/spree_cm_commissioner/test_helper/factories/invite_user_event_factory.rb +5 -0
  67. data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +30 -0
  68. data/lib/spree_cm_commissioner/test_helper/factories/user_event_factory.rb +6 -0
  69. data/lib/spree_cm_commissioner/version.rb +1 -1
  70. metadata +50 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab96626adb3039b207a661fac39be37f1dbc1701de0d79fd54d5c8a43cfaddd1
4
- data.tar.gz: 3a2854de4ac4919d3d7db3cea152932ace963fa8217d52c2581c72ecd3e5eae6
3
+ metadata.gz: 3e00b3e7d79896c88e4956cc766ba9e3538a3c9fd0f68b8835a6c5281bdfef6f
4
+ data.tar.gz: 421e9a6a7be136d047457d06e1e51822029e05b08e5bf42b9b9410541b234105
5
5
  SHA512:
6
- metadata.gz: 822fd0f6cf7898c99d1327190518ef5bc23575018ac8482d85a0379e346f863a30bcc9dfacfed365811ad206cb712f34ae55d98a380fe03d4567780c37626a17
7
- data.tar.gz: ea9bd64db27d8f7b878448db1a9c35df245e6f6d12ea1511116ed0eb8f951c25f4fb8d03d75b0c051b35e156540d93564cc062e1a204cf2cf35fbf555736f90c
6
+ metadata.gz: 25a20ae201490fd79949c9b33a74ac4fa7597fffb80a84b8a54dd104dd02a89ead67397bfcfe276e131ad8a2dd95aed691530f8d1eb33c2d99b16982dcbbca3b
7
+ data.tar.gz: d0f39688eb2ea2f413052b02bf7cfb05d34c020c02704f41175e4f84a994934038e159c10c81f23e3b4f6ede78ea44b3cfa9cbc4092970e5de1cd49a02ab9e14
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.3.5
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (1.8.0)
37
+ spree_cm_commissioner (1.8.2)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="408" height="66" fill="none"><path fill="#010304" d="M63.27 27.02c1.4-1.91 2.3-3.95 2.69-6.05l.82-4.51c.87-4.8-.26-8.67-3.38-11.52C60.33 2.14 55.8.72 49.95.72H17.81l-4.76 26.14-2.16 11.82-3.18 17.43c-.5 2.73 0 4.98 1.45 6.69 1.46 1.7 3.6 2.56 6.38 2.56h25.69c6.22 0 11.53-1.53 15.78-4.55 4.31-3.06 6.96-7.21 7.9-12.36l.86-4.69c.54-2.99.09-5.72-1.34-8.14-1.22-2.04-2.96-3.62-5.21-4.7 1.58-1.08 2.94-2.39 4.05-3.91zM51.78 43.77l-.72 3.97c-.31 1.68-1.14 2.93-2.54 3.84-1.47.95-3.47 1.43-5.93 1.43H22.27l2.61-14.31h21.67c1.9 0 3.34.48 4.28 1.44.92.94 1.23 2.13.96 3.65zm.76-25.96-.56 3.06c-.34 1.85-1.11 3.28-2.38 4.37-1.26 1.09-2.71 1.62-4.43 1.62H27.02l2.51-13.77h17.7c1.92 0 3.37.44 4.33 1.32.92.84 1.24 1.96.98 3.4"/><path fill="#010304" d="M44.91 26.87v11.82H.74l2.18-11.82zM114.17 19.64c-2.73-1.55-6.09-2.34-9.98-2.34h-8.11c-3.89 0-7.53.79-10.83 2.34-3.32 1.56-6.12 3.8-8.33 6.65-2.21 2.84-3.66 6.15-4.33 9.81l-1.92 10.55c-.67 3.67-.43 6.96.72 9.77 1.15 2.82 3.13 5.03 5.88 6.56 2.73 1.52 6.09 2.29 9.98 2.29h8.11c3.95 0 7.62-.77 10.91-2.29 3.31-1.53 6.11-3.74 8.31-6.56 2.2-2.81 3.66-6.1 4.33-9.77l1.92-10.55c.67-3.67.42-6.97-.75-9.81-1.17-2.85-3.16-5.08-5.91-6.65M89.63 53.17c-1.89 0-3.25-.66-4.18-2.03-.95-1.4-1.21-3.19-.79-5.49l1.59-8.74c.41-2.26 1.35-4.11 2.8-5.49 1.42-1.37 3.06-2.03 5.01-2.03h7.75c1.94 0 3.34.66 4.27 2.03.94 1.38 1.21 3.23.79 5.49l-1.59 8.74c-.41 2.26-1.35 4.11-2.79 5.49-1.43 1.37-3.1 2.03-5.1 2.03h-7.76M167.9 19.64c-2.73-1.55-6.09-2.34-9.98-2.34h-8.11c-3.89 0-7.54.79-10.83 2.34-3.32 1.56-6.12 3.8-8.33 6.65-2.21 2.84-3.66 6.15-4.33 9.81l-1.92 10.55c-.67 3.67-.43 6.95.72 9.77s3.13 5.03 5.88 6.56c2.73 1.52 6.09 2.29 9.98 2.29h8.11c3.95 0 7.62-.77 10.91-2.29 3.31-1.53 6.11-3.74 8.31-6.56 2.2-2.81 3.66-6.1 4.33-9.77l1.92-10.55c.67-3.67.42-6.97-.75-9.81-1.17-2.85-3.16-5.08-5.91-6.65m-29.51 26.02 1.59-8.74c.41-2.26 1.35-4.11 2.8-5.49 1.43-1.37 3.06-2.03 5.01-2.03h7.75c1.95 0 3.34.66 4.27 2.03.94 1.38 1.21 3.23.79 5.49l-1.59 8.74c-.41 2.26-1.35 4.11-2.8 5.49-1.43 1.37-3.09 2.03-5.1 2.03h-7.75c-1.88 0-3.25-.66-4.18-2.03-.95-1.4-1.21-3.19-.79-5.49M230.08 18.75h-16.44l-19.73 16.82L200.28.63h-13.81l-11.78 64.64h13.81l2.5-13.7 4.64-3.93 10.54 17.63h15.84L206.31 38.7zM265.04 36.88 251.68.63h-12.31l-11.78 64.64h13.72l6.87-37.7 7.91 21.35c.34.92.73 1.73 1.17 2.39l.04.06c.34.52.7.95 1.08 1.28.92.78 2.08 1.18 3.45 1.18h.27c1.37 0 2.68-.4 3.88-1.18.52-.34 1.04-.77 1.52-1.25.68-.65 1.39-1.48 2.12-2.46l15.68-21.35-6.87 37.69h13.72L303.93.64h-12.32l-26.56 36.25zM347.2 22.38c-3-3.37-7.54-5.08-13.48-5.08h-7.21c-3.89 0-7.53.79-10.83 2.34-3.32 1.56-6.12 3.78-8.32 6.6-2.2 2.81-3.66 6.1-4.33 9.77l-2.12 11.63c-.63 3.44-.33 6.54.88 9.2 1.21 2.67 3.22 4.77 5.99 6.23 2.74 1.46 6.1 2.2 9.99 2.2h6.85c3.87 0 7.48-.66 10.71-1.95 3.28-1.32 6.03-3.23 8.17-5.68 1.86-2.12 3.14-4.53 3.83-7.18h-13.8c-.37.67-.94 1.24-1.71 1.74-1.3.83-2.94 1.25-4.87 1.25h-7.03c-1.98 0-3.39-.52-4.31-1.59-.92-1.06-1.21-2.44-.89-4.21l.25-1.39h33.35l1.88-10.33c1.02-5.61 0-10.16-3.01-13.55zm-10.61 13.49H316.7c.45-2.03 1.35-3.68 2.68-4.94 1.42-1.34 3.06-1.99 5.01-1.99h7.3c1.89 0 3.24.61 4.13 1.86.9 1.27 1.15 2.92.77 5.06z"/><path fill="#6444D8" d="m372.06 63.5 8.12-44.57h13.06l-8.12 44.57z"/><path fill="#6444D8" d="M405.25 47.52h-47l2.44-13.22h46.99z"/></svg>
@@ -21,6 +21,10 @@ module Spree
21
21
  remove_asset(@taxon.home_banner)
22
22
  end
23
23
 
24
+ def remove_video_banner
25
+ remove_asset(@taxon.video_banner)
26
+ end
27
+
24
28
  private
25
29
 
26
30
  def remove_asset(asset)
@@ -38,6 +42,7 @@ module Spree
38
42
  @taxon.build_app_banner(attachment: permitted_resource_params.delete(:app_banner)) if permitted_resource_params[:app_banner]
39
43
  @taxon.build_web_banner(attachment: permitted_resource_params.delete(:web_banner)) if permitted_resource_params[:web_banner]
40
44
  @taxon.build_home_banner(attachment: permitted_resource_params.delete(:home_banner)) if permitted_resource_params[:home_banner]
45
+ @taxon.build_video_banner(attachment: permitted_resource_params.delete(:video_banner)) if permitted_resource_params[:video_banner]
41
46
  end
42
47
  end
43
48
  end
@@ -0,0 +1,35 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Organizer
5
+ class InvitesController < ::Spree::Api::V2::Organizer::BaseController
6
+ def show
7
+ if invite&.url_valid?
8
+ render_serialized_payload { serialize_resource(invite) }
9
+ else
10
+ render_error_payload(I18n.t('invite.url_not_found'))
11
+ end
12
+ end
13
+
14
+ def update
15
+ context = invite.confirm(params[:user_id])
16
+
17
+ if context.success?
18
+ render_serialized_payload { serialize_resource(context.invite) }
19
+ else
20
+ render_error_payload(context.message || I18n.t('invite.accept_fail'))
21
+ end
22
+ end
23
+
24
+ def invite
25
+ @invite ||= SpreeCmCommissioner::Invite.find_by!(token: params[:id])
26
+ end
27
+
28
+ def resource_serializer
29
+ ::Spree::V2::Organizer::InviteSerializer
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class CustomerNotificationsController < BaseController
6
+ before_action :require_spree_current_user
7
+
8
+ def show
9
+ customer_notification = SpreeCmCommissioner::CustomerNotification.find(params[:id])
10
+ render_serialized_payload { serialize_resource(customer_notification) }
11
+ end
12
+
13
+ def resource_serializer
14
+ Spree::V2::Tenant::CustomerNotificationSerializer
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,56 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class LineItemsController < BaseController
6
+ around_action :wrap_with_multitenant_without
7
+
8
+ def collection
9
+ if spree_current_user.nil?
10
+ line_items_by_order_tokens
11
+ else
12
+ spree_current_user.line_items.filter_by_event(params[:event])
13
+ end
14
+ end
15
+
16
+ def show
17
+ line_item = Spree::LineItem.find(params[:id])
18
+ render_serialized_payload { serialize_resource(line_item) }
19
+ end
20
+
21
+ private
22
+
23
+ def allowed_sort_attributes
24
+ super << :to_date
25
+ super << :from_date
26
+ end
27
+
28
+ def resource_serializer
29
+ Spree::V2::Tenant::LineItemSerializer
30
+ end
31
+
32
+ def collection_serializer
33
+ Spree::V2::Tenant::LineItemSerializer
34
+ end
35
+
36
+ def line_items_by_order_tokens
37
+ order_tokens = params[:order_tokens]
38
+ event = params[:event]
39
+
40
+ return Spree::LineItem.none if order_tokens.blank?
41
+
42
+ Spree::LineItem.joins(:order)
43
+ .where(spree_orders: { token: order_tokens, state: 'complete', user_id: nil, tenant_id: 1 })
44
+ .filter_by_event(event)
45
+ .page(params[:page])
46
+ .per(params[:per_page])
47
+ end
48
+
49
+ def wrap_with_multitenant_without(&block)
50
+ MultiTenant.without(&block)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,55 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class NotificationsController < BaseController
6
+ before_action :require_spree_current_user
7
+
8
+ def collection
9
+ spree_current_user.notifications.newest_first.user_notifications
10
+ end
11
+
12
+ def serialize_collection(collection)
13
+ options_data = collection_options(collection).merge(params: serializer_params)
14
+ options_data[:meta][:unread_count] = collection.select(&:unread?).count
15
+
16
+ collection_serializer.new(
17
+ collection,
18
+ options_data
19
+ ).serializable_hash
20
+ end
21
+
22
+ def show
23
+ notification = SpreeCmCommissioner::Notification.find(params[:id])
24
+
25
+ render_serialized_payload { serialize_resource(notification) }
26
+ end
27
+
28
+ def mark_as_read
29
+ result = SpreeCmCommissioner::NotificationReader.call(id: params[:id])
30
+
31
+ return unless result.success?
32
+
33
+ head :ok
34
+ end
35
+
36
+ def mark_all_as_read
37
+ result = SpreeCmCommissioner::NotificationReader.call(user: spree_current_user)
38
+
39
+ return unless result.success?
40
+
41
+ head :ok
42
+ end
43
+
44
+ def resource_serializer
45
+ Spree::V2::Tenant::NotificationSerializer
46
+ end
47
+
48
+ def collection_serializer
49
+ Spree::V2::Tenant::NotificationSerializer
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,52 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class ProfileImagesController < BaseController
6
+ before_action :require_spree_current_user
7
+ before_action :required_schema_validation!, except: [:destroy]
8
+
9
+ def update
10
+ context = SpreeCmCommissioner::ProfileImageUpdater.call(
11
+ user: spree_current_user,
12
+ url: schema.output.fetch(:url)
13
+ )
14
+ if context.success?
15
+ render_serialized_payload { serialize_resource(context.result) }
16
+ else
17
+ render_error_payload(context.message)
18
+ end
19
+ end
20
+
21
+ def destroy
22
+ context = SpreeCmCommissioner::ProfileImageDestroyer.call(
23
+ user: spree_current_user
24
+ )
25
+ if context.success?
26
+ render_serialized_payload(204) { serialize_resource(context.result) }
27
+ else
28
+ render_error_payload(context.message)
29
+ end
30
+ end
31
+
32
+ def required_schema_validation!
33
+ return true if required_schema.nil?
34
+
35
+ @schema = required_schema.new(request: request, locale: locale, user: spree_current_user)
36
+ return true if schema.success?
37
+
38
+ raise SchemaValidationError, schema.error_message
39
+ end
40
+
41
+ def required_schema
42
+ SpreeCmCommissioner::ProfileImageRequestSchema
43
+ end
44
+
45
+ def resource_serializer
46
+ Spree::V2::Tenant::UserProfileSerializer
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class S3SignedUrlsController < BaseController
6
+ def show
7
+ if resource.error_message.blank?
8
+ render_serialized_payload { serialize_resource(resource) }
9
+ else
10
+ render_error_payload(resource.error_message)
11
+ end
12
+ end
13
+
14
+ def resource
15
+ SpreeCmCommissioner::S3PresignedUrlBuilder.call(
16
+ bucket_name: params[:bucket_name],
17
+ object_key: params[:object_key],
18
+ file_name: params[:file_name]
19
+ )
20
+ end
21
+
22
+ def resource_serializer
23
+ Spree::V2::Tenant::S3SignedUrlSerializer
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,63 @@
1
+ module SpreeCmCommissioner
2
+ class CrewInviter < BaseInteractor
3
+ delegate :id, :invitee_id, to: :context
4
+
5
+ def call
6
+ validate_data
7
+ assign_role_to_user
8
+ create_user_event
9
+ update_invite_user_event
10
+ context.invite = crew_invite
11
+ end
12
+
13
+ private
14
+
15
+ def validate_data
16
+ if invite_user_event.nil?
17
+ context.fail!(message: I18n.t('invite.url_not_found'))
18
+ elsif !invite_user_event.invite.url_valid?
19
+ context.fail!(message: I18n.t('invite.url_expired'))
20
+ end
21
+ end
22
+
23
+ def assign_role_to_user
24
+ return if user.operator?
25
+
26
+ role = Spree::Role.find_by(name: 'operator')
27
+ role_user = Spree::RoleUser.new(user_id: user.id, role_id: role.id)
28
+ role_user.save
29
+ end
30
+
31
+ def create_user_event
32
+ @user_event = SpreeCmCommissioner::UserEvent.new(
33
+ taxon_id: crew_invite.taxon_id,
34
+ user_id: user.id
35
+ )
36
+
37
+ return if @user_event.save
38
+
39
+ context.fail!(message: I18n.t('invite.already_invited'))
40
+ end
41
+
42
+ def update_invite_user_event
43
+ invite_user_event.user_taxon_id = @user_event.id
44
+ invite_user_event.confirmed_at = Time.current
45
+
46
+ return if @invite_user_event.save
47
+
48
+ context.fail!(message: I18n.t('invite.update_fail'))
49
+ end
50
+
51
+ def crew_invite
52
+ @crew_invite ||= SpreeCmCommissioner::CrewInvite.find(id)
53
+ end
54
+
55
+ def invite_user_event
56
+ @invite_user_event ||= crew_invite.invite_user_events.find_by(email: user.email, confirmed_at: nil)
57
+ end
58
+
59
+ def user
60
+ @user ||= Spree::User.find(invitee_id)
61
+ end
62
+ end
63
+ end
@@ -55,15 +55,20 @@ module SpreeCmCommissioner
55
55
  end
56
56
 
57
57
  def end_users
58
+ users_scope = Spree.user_class.where(tenant_id: customer_notification.tenant_id)
59
+
58
60
  if customer_notification.notification_taxons.exists?
59
61
  taxon_ids = customer_notification.notification_taxons.pluck(:taxon_id)
60
- SpreeCmCommissioner::UsersByEventFetcherQuery
61
- .new(taxon_ids)
62
- .call
63
- .end_users_push_notificable
62
+ users_scope = SpreeCmCommissioner::UsersByEventFetcherQuery
63
+ .new(taxon_ids)
64
+ .call
65
+ .end_users_push_notificable
66
+ .where(tenant_id: customer_notification.tenant_id)
64
67
  else
65
- Spree.user_class.end_users_push_notificable
68
+ users_scope = users_scope.end_users_push_notificable
66
69
  end
70
+
71
+ users_scope
67
72
  end
68
73
  end
69
74
  end
@@ -0,0 +1,16 @@
1
+ module SpreeCmCommissioner
2
+ class ProfileImageDestroyer < BaseInteractor
3
+ delegate :user, to: :context
4
+
5
+ def call
6
+ profile_image = SpreeCmCommissioner::UserProfile.find_by(viewable: user)
7
+
8
+ if profile_image
9
+ profile_image.destroy
10
+ context.result = profile_image
11
+ else
12
+ context.fail!(message: 'No profile image to delete')
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module SpreeCmCommissioner
2
+ class CrewInviteMailer < Spree::BaseMailer
3
+ layout 'spree_cm_commissioner/layouts/crew_invite_mailer'
4
+
5
+ def send_crew_invite_email(invite_user_event_id)
6
+ @invite_user_event = SpreeCmCommissioner::InviteUserEvent.find(invite_user_event_id)
7
+
8
+ subject = Spree::Store.default.name.to_s
9
+
10
+ mail(from: from_address, to: @invite_user_event.email, subject: subject)
11
+ end
12
+ end
13
+ end
@@ -46,7 +46,12 @@ module SpreeCmCommissioner
46
46
  'bib-zerofill' => 'integer',
47
47
  'bib-display-prefix' => 'boolean',
48
48
  'bib-pre-generation-on-create' => 'boolean',
49
- 'seat-number-positions' => 'array'
49
+ 'seat-number-positions' => 'array',
50
+ 'origin' => 'origin',
51
+ 'destination' => 'destination',
52
+ 'departure-time' => 'time',
53
+ 'vehicle' => 'vehicle_id',
54
+ 'allow-seat-selection' => 'boolean'
50
55
  }.freeze
51
56
 
52
57
  included do
@@ -0,0 +1,7 @@
1
+ module SpreeCmCommissioner
2
+ class CrewInvite < Invite
3
+ def confirm(invitee_id)
4
+ SpreeCmCommissioner::CrewInviter.call(id: id, invitee_id: invitee_id)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ module SpreeCmCommissioner
2
+ class Invite < SpreeCmCommissioner::Base
3
+ belongs_to :taxon, class_name: 'Spree::Taxon'
4
+ belongs_to :inviter, class_name: 'Spree::User'
5
+ has_many :invite_user_events, class_name: 'SpreeCmCommissioner::InviteUserEvent', dependent: :destroy
6
+ scope :active, -> { where('expires_at > ?', Time.current) }
7
+
8
+ after_create :set_expiration
9
+
10
+ def set_expiration
11
+ update(expires_at: 3.days.from_now)
12
+ end
13
+
14
+ def invite_url
15
+ "#{Spree::Store.default.formatted_url}/invite/#{token}?utm_source=email"
16
+ end
17
+
18
+ def url_valid?
19
+ expires_at.present? && expires_at > Time.current
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module SpreeCmCommissioner
2
+ class InviteUserEvent < InviteUserTaxon
3
+ alias_attribute :user_event, :user_taxon
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ module SpreeCmCommissioner
2
+ class InviteUserTaxon < SpreeCmCommissioner::Base
3
+ belongs_to :user_taxon, class_name: 'SpreeCmCommissioner::UserTaxon'
4
+ belongs_to :invite, class_name: 'SpreeCmCommissioner::Invite'
5
+ after_create :send_crew_invite_email
6
+
7
+ def send_crew_invite_email
8
+ SpreeCmCommissioner::CrewInviteMailer.send_crew_invite_email(id).deliver_later
9
+ end
10
+ end
11
+ end
@@ -24,7 +24,7 @@ module SpreeCmCommissioner
24
24
 
25
25
  base.validate :ensure_not_exceed_max_quantity_per_order, if: -> { variant&.max_quantity_per_order.present? }
26
26
 
27
- base.whitelisted_ransackable_associations |= %w[guests]
27
+ base.whitelisted_ransackable_associations |= %w[guests order]
28
28
  base.whitelisted_ransackable_attributes |= %w[number to_date from_date vendor_id]
29
29
 
30
30
  base.delegate :delivery_required?, :permanent_stock?, :high_demand, :transit?,
@@ -42,7 +42,7 @@ module SpreeCmCommissioner
42
42
  base.delegate :customer, to: :user, allow_nil: true
43
43
 
44
44
  base.whitelisted_ransackable_associations |= %w[customer taxon payments invoice]
45
- base.whitelisted_ransackable_attributes |= %w[intel_phone_number phone_number email number]
45
+ base.whitelisted_ransackable_attributes |= %w[intel_phone_number phone_number email number state]
46
46
 
47
47
  def base.search_by_qr_data!(data)
48
48
  token = data.match(/^R\d{9,}-([A-Za-z0-9_\-]+)$/)&.captures
@@ -13,6 +13,8 @@ module SpreeCmCommissioner
13
13
 
14
14
  has_many :product_places, class_name: 'SpreeCmCommissioner::ProductPlace', dependent: :destroy
15
15
  has_many :products, through: :product_places
16
+ has_many :children, class_name: 'SpreeCmCommissioner::Place', foreign_key: :parent_id, dependent: :destroy
17
+ belongs_to :parent, class_name: 'SpreeCmCommissioner::Place', optional: true
16
18
 
17
19
  def self.ransackable_attributes(auth_object = nil)
18
20
  super & %w[name code]
@@ -75,6 +75,10 @@ module SpreeCmCommissioner
75
75
  taxons.event.first&.parent
76
76
  end
77
77
 
78
+ def ticket_url
79
+ "#{Spree::Store.default.formatted_url}/tickets/#{slug}"
80
+ end
81
+
78
82
  private
79
83
 
80
84
  def set_tenant
@@ -28,6 +28,7 @@ module SpreeCmCommissioner
28
28
  base.has_one :web_banner, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::TaxonWebBanner'
29
29
  base.has_one :app_banner, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::TaxonAppBanner'
30
30
  base.has_one :home_banner, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::TaxonHomeBanner'
31
+ base.has_one :video_banner, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::TaxonVideoBanner'
31
32
 
32
33
  # Update children association to work with nested set (lft, rgt)
33
34
  base.has_many :children, -> { order(:lft) }, class_name: 'Spree::Taxon', foreign_key: :parent_id, dependent: :destroy
@@ -48,6 +49,10 @@ module SpreeCmCommissioner
48
49
  base.whitelisted_ransackable_attributes |= %w[kind]
49
50
 
50
51
  base.enum purchasable_on: { both: 0, web: 1, app: 2 }
52
+ base.has_many :crew_invites, class_name: 'SpreeCmCommissioner::CrewInvite', dependent: :destroy
53
+ base.has_many :invite_user_events, through: :user_events, class_name: 'SpreeCmCommissioner::InviteUserEvent'
54
+
55
+ base.has_many :line_items, through: :products
51
56
  end
52
57
  # rubocop:enable Metrics/MethodLength
53
58
 
@@ -0,0 +1,4 @@
1
+ module SpreeCmCommissioner
2
+ class TaxonVideoBanner < Video
3
+ end
4
+ end
@@ -67,6 +67,10 @@ module SpreeCmCommissioner
67
67
  has_spree_role?('organizer')
68
68
  end
69
69
 
70
+ def operator?
71
+ has_spree_role?('operator')
72
+ end
73
+
70
74
  def full_name
71
75
  [first_name, last_name].compact_blank.join(' ')
72
76
  end
@@ -143,5 +143,28 @@ module SpreeCmCommissioner
143
143
  def number_of_guests
144
144
  number_of_adults + number_of_kids
145
145
  end
146
+
147
+ def origin
148
+ @origin ||= option_value_name_for(option_type_name: 'origin')&.to_i
149
+ end
150
+
151
+ def destination
152
+ @destination ||= option_value_name_for(option_type_name: 'destination')&.to_i
153
+ end
154
+
155
+ def vehicle
156
+ @vehicle ||= option_value_name_for(option_type_name: 'vehicle')&.to_i
157
+ end
158
+
159
+ def allow_seat_selection
160
+ @allow_seat_selection ||= option_value_name_for(option_type_name: 'allow-seat-selection')&.to_i
161
+ end
162
+
163
+ def departure_time
164
+ @departure_time ||= begin
165
+ time = option_value_name_for(option_type_name: 'departure-time')
166
+ Time.zone.parse(time) if time.present?
167
+ end
168
+ end
146
169
  end
147
170
  end
@@ -0,0 +1,15 @@
1
+ module SpreeCmCommissioner
2
+ class Video < Spree::Asset
3
+ include ::Rails.application.routes.url_helpers
4
+
5
+ has_one_attached :attachment
6
+
7
+ validates :attachment, attached: true, content_type: %r{\Avideo/.*\z}
8
+
9
+ default_scope { includes(attachment_attachment: :blob) }
10
+
11
+ def original_url
12
+ cdn_image_url(attachment)
13
+ end
14
+ end
15
+ end
@@ -47,5 +47,24 @@
47
47
  form:f,
48
48
  classes: ['col-md-5 ml-3']
49
49
  %>
50
+
51
+ <%= f.field_container :video_banner, direct_upload: true, class: ['col-md-5 ml-3 rounded border p-3'] do %>
52
+ <% if @taxon.video_banner&.attachment.present? %>
53
+ <video class="rounded border mb-4 mw-150" style="max-height: 200px" controls>
54
+ <source src="<%= main_app.rails_blob_url(@taxon.video_banner.attachment) %>" type="<%= @taxon.video_banner.attachment.content_type %>">
55
+ Your browser does not support the video tag.
56
+ </video>
57
+ <% end %>
58
+
59
+ <div data-hook="file" class="mb-3" >
60
+ <%= f.label :video_banner, I18n.t('taxon.video_banner') %>
61
+ <%= f.label :video_banner, I18n.t('taxon.video_banner_require_size') %>
62
+ <%= f.file_field :video_banner, required: false, accept: "video/*" %>
63
+ </div>
64
+
65
+ <% if @taxon.video_banner.present? && defined?(remove_video_banner_admin_taxonomy_taxon_url) %>
66
+ <%= link_to Spree.t(:remove_video), remove_video_banner_admin_taxonomy_taxon_url, method: :delete %>
67
+ <% end %>
68
+ <% end %>
50
69
  </div>
51
70
 
@@ -0,0 +1,18 @@
1
+ <!-- insert_before "erb[loud]:contains('field_container :parent_id')" -->
2
+
3
+ <div class="form-group">
4
+ <%= label_tag nil, Spree.t(:available_on) %>
5
+ <div class="input-group datePickerFrom"
6
+ data-wrap="true"
7
+ data-alt-input="true"
8
+ data-min-date="<%= Time.now.strftime('%Y-%m-%d') %>"
9
+ data-enable-time="true">
10
+ <%= f.text_field :available_on,
11
+ value: @taxon.available_on,
12
+ placeholder: Spree.t(:select_a_date),
13
+ class: 'form-control shadow-none',
14
+ 'data-input':'' %>
15
+
16
+ <%= render partial: 'spree/admin/shared/cal_close' %>
17
+ </div>
18
+ </div>