spree_cm_commissioner 1.11.0.pre.pre4 → 1.13.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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_build_gem.yml +143 -12
  3. data/.gitignore +1 -2
  4. data/Gemfile.lock +1 -22
  5. data/Rakefile +4 -33
  6. data/app/controllers/spree/admin/cms_pages_controller_decorator.rb +32 -0
  7. data/app/controllers/spree/admin/product_places_controller.rb +2 -0
  8. data/app/controllers/spree/admin/stock_managements_controller.rb +1 -62
  9. data/app/controllers/spree/api/v2/organizer/invite_guests_controller.rb +70 -0
  10. data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +31 -14
  11. data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
  12. data/app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb +1 -0
  13. data/app/controllers/spree/api/v2/tenant/account_checker_controller.rb +1 -1
  14. data/app/controllers/spree/api/v2/tenant/account_recovers_controller.rb +2 -2
  15. data/app/controllers/spree/api/v2/tenant/cms_pages_controller.rb +41 -0
  16. data/app/controllers/spree/api/v2/tenant/reset_passwords_controller.rb +1 -1
  17. data/app/controllers/spree/api/v2/tenant/waiting_room_sessions_controller.rb +30 -0
  18. data/app/controllers/spree_cm_commissioner/admin/variants_controller_decorator.rb +17 -0
  19. data/app/controllers/spree_cm_commissioner/api/v2/storefront/cms_pages_controller_decorator.rb +18 -0
  20. data/app/interactors/spree_cm_commissioner/account_recover.rb +2 -2
  21. data/app/interactors/spree_cm_commissioner/create_event.rb +7 -26
  22. data/app/interactors/spree_cm_commissioner/create_ticket.rb +95 -0
  23. data/app/interactors/spree_cm_commissioner/event_line_items_date_syncer.rb +19 -0
  24. data/app/interactors/spree_cm_commissioner/existing_account_checker.rb +1 -1
  25. data/app/interactors/spree_cm_commissioner/order_importer/multi_guest.rb +31 -0
  26. data/app/interactors/spree_cm_commissioner/organizers_transactional_email_notifier.rb +27 -0
  27. data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +3 -3
  28. data/app/interactors/spree_cm_commissioner/product_event_id_to_children_syncer.rb +15 -0
  29. data/app/interactors/spree_cm_commissioner/sms.rb +14 -0
  30. data/app/interactors/spree_cm_commissioner/telegram_debug_pin_code_sender.rb +2 -1
  31. data/app/interactors/spree_cm_commissioner/transactional_email_sender.rb +50 -0
  32. data/app/interactors/spree_cm_commissioner/user_forgotten_password_updater.rb +2 -1
  33. data/app/interactors/spree_cm_commissioner/user_password_authenticator.rb +4 -2
  34. data/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb +7 -14
  35. data/app/interactors/spree_cm_commissioner/waiting_room_session_firebase_logger.rb +30 -0
  36. data/app/jobs/spree_cm_commissioner/ensure_event_for_product_line_item_guests_job.rb +13 -0
  37. data/app/jobs/spree_cm_commissioner/event_line_items_date_syncer_job.rb +8 -0
  38. data/app/jobs/spree_cm_commissioner/product_event_id_to_children_syncer_job.rb +8 -0
  39. data/app/jobs/spree_cm_commissioner/telegram_debug_pin_code_sender_job.rb +3 -1
  40. data/app/jobs/spree_cm_commissioner/waiting_room_session_firebase_logger_job.rb +13 -0
  41. data/app/mailers/spree_cm_commissioner/event_transactional_mailer.rb +23 -0
  42. data/app/mailers/spree_cm_commissioner/pin_code_mailer.rb +1 -0
  43. data/app/models/concerns/spree_cm_commissioner/kyc_bitwise.rb +2 -0
  44. data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +10 -6
  45. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +13 -26
  46. data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +4 -3
  47. data/app/models/concerns/spree_cm_commissioner/product_type.rb +0 -10
  48. data/app/models/concerns/spree_cm_commissioner/user_identity.rb +7 -4
  49. data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +8 -10
  50. data/app/models/spree_cm_commissioner/cms_page_decorator.rb +9 -0
  51. data/app/models/spree_cm_commissioner/event_ticket_google_wallet.rb +2 -2
  52. data/app/models/spree_cm_commissioner/guest.rb +1 -1
  53. data/app/models/spree_cm_commissioner/invite_guest.rb +23 -0
  54. data/app/models/spree_cm_commissioner/line_item_decorator.rb +10 -16
  55. data/app/models/spree_cm_commissioner/option_type_decorator.rb +6 -0
  56. data/app/models/spree_cm_commissioner/order_decorator.rb +0 -15
  57. data/app/models/spree_cm_commissioner/place.rb +1 -1
  58. data/app/models/spree_cm_commissioner/product_decorator.rb +17 -14
  59. data/app/models/spree_cm_commissioner/product_place.rb +1 -0
  60. data/app/models/spree_cm_commissioner/role_decorator.rb +3 -0
  61. data/app/models/spree_cm_commissioner/stock/availability_checker.rb +25 -27
  62. data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +1 -2
  63. data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
  64. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +0 -14
  65. data/app/models/spree_cm_commissioner/taxon_decorator.rb +26 -3
  66. data/app/models/spree_cm_commissioner/transactional_email.rb +6 -0
  67. data/app/models/spree_cm_commissioner/user_decorator.rb +6 -0
  68. data/app/models/spree_cm_commissioner/variant_decorator.rb +27 -34
  69. data/app/models/spree_cm_commissioner/vehicle.rb +0 -7
  70. data/app/models/spree_cm_commissioner/vendor_decorator.rb +9 -1
  71. data/app/overrides/spree/admin/cms_pages/_form/tenant_fields.html.erb.deface +9 -0
  72. data/app/overrides/spree/admin/cms_pages/index/cms_pages_tabs.html.erb.deface +21 -0
  73. data/app/overrides/spree/admin/taxons/_form/assets_form.html.erb.deface +2 -2
  74. data/app/overrides/spree/admin/taxons/_form/available_on.html.erb.deface +3 -1
  75. data/app/overrides/spree/admin/taxons/_form/background_color_and_foreground_color.html.erb.deface +3 -1
  76. data/app/overrides/spree/admin/taxons/_form/custom_redirect_url.html.erb.deface +3 -1
  77. data/app/overrides/spree/admin/taxons/_form/hide_video_banner.html.erb.deface +3 -1
  78. data/app/overrides/spree/admin/taxons/_form/purchasable_on_status.html.erb.deface +3 -1
  79. data/app/overrides/spree/admin/taxons/_form/show_badge_status.html.erb.deface +3 -1
  80. data/app/overrides/spree/admin/taxons/_form/to_date_form_date.html.erb.deface +3 -4
  81. data/app/overrides/spree/admin/users/_form/roles_fields.html.erb.deface +1 -1
  82. data/app/overrides/spree/admin/users/index/body.html.erb.deface +3 -0
  83. data/app/overrides/spree/admin/users/index/headers.html.erb.deface +3 -0
  84. data/app/overrides/spree/admin/variants/_form/kyc_field.html.erb.deface +40 -0
  85. data/app/overrides/spree/admin/variants/edit/variant_status.html.erb.deface +6 -3
  86. data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +45 -0
  87. data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +55 -0
  88. data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +0 -3
  89. data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
  90. data/app/serializers/spree/v2/organizer/invite_guest_serializer.rb +13 -0
  91. data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +0 -2
  92. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +2 -0
  93. data/app/serializers/spree/v2/tenant/waiting_room_session_serializer.rb +9 -0
  94. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +2 -1
  95. data/app/services/spree_cm_commissioner/user_authenticator.rb +3 -1
  96. data/app/services/spree_cm_commissioner/user_roles_assigner.rb +62 -0
  97. data/app/views/spree/admin/shared/_cms_pages_tabs.html.erb +20 -0
  98. data/app/views/spree/admin/shared/_taxon_tabs.html.erb +19 -19
  99. data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +1 -3
  100. data/app/views/spree/admin/stock_managements/index.html.erb +5 -40
  101. data/app/views/spree/shared/_base_mailer_header.html.erb +10 -2
  102. data/app/views/spree_cm_commissioner/event_transactional_mailer/_event_banner.html.erb +3 -0
  103. data/app/views/spree_cm_commissioner/event_transactional_mailer/_mailer_stylesheets.html.erb +317 -0
  104. data/app/views/spree_cm_commissioner/event_transactional_mailer/_note.html.erb +8 -0
  105. data/app/views/spree_cm_commissioner/event_transactional_mailer/_share_button.html.erb +3 -0
  106. data/app/views/spree_cm_commissioner/event_transactional_mailer/send_to_organizer.html.erb +59 -0
  107. data/app/views/spree_cm_commissioner/event_transactional_mailer/send_to_participant.html.erb +52 -0
  108. data/app/views/spree_cm_commissioner/layouts/event_transactional_mailer.html.erb +14 -0
  109. data/config/initializers/spree_permitted_attributes.rb +0 -5
  110. data/config/locales/en.yml +36 -0
  111. data/config/locales/km.yml +2 -0
  112. data/config/routes.rb +6 -11
  113. data/db/migrate/20250422014417_create_spree_cm_commissioner_invite_guest.rb +21 -0
  114. data/db/migrate/20250509033437_create_spree_cm_commissioner_transactional_emails.rb +12 -0
  115. data/db/migrate/20250509075429_add_max_order_quantity_to_spree_product.rb +5 -0
  116. data/db/migrate/20250512075319_add_tenant_id_to_spree_cms_pages.rb +6 -0
  117. data/db/migrate/20250520042602_add_event_to_spree_products.rb +7 -0
  118. data/db/migrate/20250520044533_add_event_to_spree_line_items.rb +7 -0
  119. data/db/migrate/20250521024345_add_tenant_id_to_cm_waiting_room_sessions.rb +6 -0
  120. data/db/migrate/20250521095539_add_kyc_to_spree_variants.rb +5 -0
  121. data/docker-compose.yml +1 -1
  122. data/lib/generators/spree_cm_commissioner/install/install_generator.rb +3 -11
  123. data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_dashboard/spree_cm_commissioner → spree_cm_commissioner}/utilities.js +0 -4
  124. data/lib/spree_cm_commissioner/calendar_event.rb +1 -11
  125. data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
  126. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
  127. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +5 -18
  128. data/lib/spree_cm_commissioner/test_helper/factories/role.rb +7 -0
  129. data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
  130. data/lib/spree_cm_commissioner/test_helper/factories/taxon_home_banner_factory.rb +5 -0
  131. data/lib/spree_cm_commissioner/test_helper/factories/transactional_email_factory.rb +6 -0
  132. data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +6 -41
  133. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +1 -1
  134. data/lib/spree_cm_commissioner/version.rb +1 -1
  135. data/lib/spree_cm_commissioner.rb +0 -34
  136. data/lib/tasks/ensure_event_for_product_line_item_guests.rake +7 -0
  137. data/lib/tasks/update_invalid_self_root_places.rake +9 -0
  138. data/lib/tasks/update_orphan_root_places.rake +1 -0
  139. data/spree_cm_commissioner.gemspec +0 -5
  140. metadata +54 -82
  141. data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +0 -42
  142. data/app/finders/spree_cm_commissioner/accommodations/find.rb +0 -40
  143. data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +0 -35
  144. data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +0 -40
  145. data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +0 -25
  146. data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +0 -13
  147. data/app/interactors/spree_cm_commissioner/stock/inventory_items_generator.rb +0 -15
  148. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +0 -75
  149. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +0 -24
  150. data/app/interactors/spree_cm_commissioner/user_roles_assigner.rb +0 -22
  151. data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +0 -7
  152. data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +0 -7
  153. data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +0 -11
  154. data/app/jobs/spree_cm_commissioner/stock/inventory_items_generator_job.rb +0 -11
  155. data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +0 -9
  156. data/app/models/spree_cm_commissioner/inventory.rb +0 -11
  157. data/app/models/spree_cm_commissioner/inventory_item.rb +0 -56
  158. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +0 -40
  159. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +0 -126
  160. data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +0 -36
  161. data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +0 -27
  162. data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +0 -44
  163. data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +0 -19
  164. data/app/views/spree/admin/stock_managements/_events_popover.html.erb +0 -23
  165. data/app/views/spree/admin/stock_managements/calendar.html.erb +0 -35
  166. data/db/migrate/20250304293518_create_cm_inventory_items.rb +0 -21
  167. data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +0 -5
  168. data/db/migrate/20250502025848_add_index_to_spree_products.rb +0 -5
  169. data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +0 -5
  170. data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +0 -5
  171. data/lib/spree_cm_commissioner/cached_inventory_item.rb +0 -23
  172. data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +0 -9
  173. data/lib/tasks/create_default_non_permanent_inventory_items.rake +0 -16
  174. data/lib/tasks/ensure_correct_product_type.rake +0 -7
  175. data/lib/tasks/generate_inventory_items.rake +0 -7
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class WaitingRoomSessionsController < BaseController
6
+ skip_before_action :ensure_waiting_room_session_token
7
+
8
+ def create
9
+ context = SpreeCmCommissioner::WaitingRoomSessionCreator.call(
10
+ tenant_id: MultiTenant.current_tenant_id,
11
+ remote_ip: request.remote_ip,
12
+ waiting_guest_firebase_doc_id: params[:waiting_guest_firebase_doc_id],
13
+ page_path: params[:page_path]
14
+ )
15
+
16
+ if context.success?
17
+ render_serialized_payload { serialize_resource(context.room_session) }
18
+ else
19
+ render_error_payload(context.message)
20
+ end
21
+ end
22
+
23
+ def resource_serializer
24
+ Spree::V2::Tenant::WaitingRoomSessionSerializer
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,9 +1,12 @@
1
1
  module SpreeCmCommissioner
2
2
  module Admin
3
3
  module VariantsControllerDecorator
4
+ include SpreeCmCommissioner::Admin::KycableHelper
5
+
4
6
  def self.prepended(base)
5
7
  base.before_action :build_option_values_form, only: %i[edit new]
6
8
  base.before_action :build_option_values, only: %i[create update]
9
+ base.before_action :build_guest_info, only: %i[create update]
7
10
  end
8
11
 
9
12
  # build option values that not exist. All option values will display to UI.
@@ -30,6 +33,20 @@ module SpreeCmCommissioner
30
33
  end
31
34
  end
32
35
 
36
+ def build_guest_info
37
+ bit_fields = SpreeCmCommissioner::KycBitwise::BIT_FIELDS.keys
38
+
39
+ if permitted_resource_params[:use_product_kyc] == '1'
40
+ permitted_resource_params[:kyc] = nil
41
+ else
42
+ @kyc_result = calculate_kyc_value(params[:variant])
43
+ permitted_resource_params[:kyc] = @kyc_result
44
+ end
45
+ # remove these fields from params to prevent unknown attribute error
46
+ bit_fields.each { |field| params.require(:variant).delete(field) }
47
+ params.require(:variant).delete(:use_product_kyc)
48
+ end
49
+
33
50
  # some option value name changed after validate.
34
51
  def validated_option_value_name(name, option_type_id)
35
52
  return nil if name.blank?
@@ -0,0 +1,18 @@
1
+ module SpreeCmCommissioner
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module CmsPagesControllerDecorator
6
+ def scope
7
+ super.by_locale(I18n.locale).where(tenant_id: nil)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ # Prepend the decorator to the CmsPagesController
16
+ unless Spree::Api::V2::Storefront::CmsPagesController.ancestors.include?(SpreeCmCommissioner::Api::V2::Storefront::CmsPagesControllerDecorator)
17
+ Spree::Api::V2::Storefront::CmsPagesController.prepend(SpreeCmCommissioner::Api::V2::Storefront::CmsPagesControllerDecorator)
18
+ end
@@ -1,6 +1,6 @@
1
1
  module SpreeCmCommissioner
2
2
  class AccountRecover < BaseInteractor
3
- delegate :id_token, :login, :password, to: :context
3
+ delegate :id_token, :login, :password, :tenant_id, to: :context
4
4
 
5
5
  def call
6
6
  validate_user
@@ -15,7 +15,7 @@ module SpreeCmCommissioner
15
15
  def validate_user
16
16
  # get email password user
17
17
  if login.present? && password.present?
18
- context.user = Spree.user_class.find_user_by_login(login)
18
+ context.user = Spree.user_class.find_user_by_login(login, tenant_id)
19
19
  # get social user
20
20
  elsif id_token.present?
21
21
  checker_context = SpreeCmCommissioner::UserIdTokenChecker.call(id_token: id_token)
@@ -10,8 +10,6 @@ module SpreeCmCommissioner
10
10
  assign_prototype
11
11
  create_child_taxon
12
12
  build_home_banner
13
- assign_option_types
14
- assign_option_values
15
13
  end
16
14
  end
17
15
 
@@ -27,9 +25,11 @@ module SpreeCmCommissioner
27
25
  taxonomy_id: context.params[:taxonomy_id]
28
26
  )
29
27
 
30
- return if @parent_taxon.save
31
-
32
- context.fail!(message: @parent_taxon.errors.full_messages.join(', '))
28
+ if @parent_taxon.save
29
+ context.slug = @parent_taxon.event_slug
30
+ else
31
+ context.fail!(message: @parent_taxon.errors.full_messages.join(', '))
32
+ end
33
33
  end
34
34
 
35
35
  def assign_vendor
@@ -44,6 +44,8 @@ module SpreeCmCommissioner
44
44
  def create_child_taxon
45
45
  child_taxon = Spree::Taxon.new(
46
46
  name: 'Ticket Type',
47
+ from_date: @parent_taxon.from_date,
48
+ to_date: @parent_taxon.to_date,
47
49
  parent_id: @parent_taxon.id,
48
50
  taxonomy_id: context.params[:taxonomy_id]
49
51
  )
@@ -63,26 +65,5 @@ module SpreeCmCommissioner
63
65
 
64
66
  context.fail!(message: 'Home banner upload failed') unless banner.persisted?
65
67
  end
66
-
67
- def assign_options(model_class, param_key, foreign_key)
68
- return unless params[param_key]
69
-
70
- params[param_key].each do |id|
71
- record = model_class.new(
72
- taxon_id: @parent_taxon.id,
73
- foreign_key => id
74
- )
75
-
76
- context.fail!(message: record.errors.full_messages.join(', ')) unless record.save
77
- end
78
- end
79
-
80
- def assign_option_types
81
- assign_options(SpreeCmCommissioner::TaxonOptionType, :option_type_id, :option_type_id)
82
- end
83
-
84
- def assign_option_values
85
- assign_options(SpreeCmCommissioner::TaxonOptionValue, :option_value_id, :option_value_id)
86
- end
87
68
  end
88
69
  end
@@ -0,0 +1,95 @@
1
+ module SpreeCmCommissioner
2
+ class CreateTicket
3
+ include Interactor
4
+ delegate :params, to: :context
5
+
6
+ def call
7
+ ActiveRecord::Base.transaction do
8
+ create_ticket
9
+ set_option_value
10
+ create_variant
11
+ create_stock_item
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def create_ticket
18
+ @ticket = Spree::Product.new(ticket_params)
19
+
20
+ assign_store
21
+ assign_event
22
+ return if @ticket.save
23
+
24
+ context.fail!(message: @ticket.errors.full_messages.join(', '))
25
+ end
26
+
27
+ def assign_store
28
+ @store = Spree::Store.default
29
+ @ticket.stores << @store
30
+ end
31
+
32
+ def assign_event
33
+ # Find the event taxon using the event_id from the parameters.
34
+ # It looks for a taxon with a slug like "events-{event_id}" and picks its first child.
35
+ @event = Spree::Taxon.find_by(slug: "events-#{params[:event_id]}")&.children&.first
36
+ if @event
37
+ @ticket.taxons << @event
38
+ else
39
+ context.fail!(message: 'Event not found.')
40
+ end
41
+ end
42
+
43
+ def set_option_value
44
+ @option_type = Spree::OptionType.find_or_create_by!(
45
+ name: 'ticket-type',
46
+ presentation: 'Ticket Type'
47
+ )
48
+
49
+ @option_value = Spree::OptionValue.find_or_create_by!(
50
+ name: @ticket.name,
51
+ presentation: @ticket.name,
52
+ option_type_id: @option_type.id
53
+ )
54
+ end
55
+
56
+ def create_variant
57
+ @variant = @ticket.variants.new(
58
+ price: @ticket.price,
59
+ compare_at_price: @ticket.compare_at_price,
60
+ option_value_ids: [@option_value.id]
61
+ )
62
+ return if @variant.save
63
+
64
+ context.fail!(message: @variant.errors.full_messages.join(', '))
65
+ end
66
+
67
+ def create_stock_item
68
+ @stock_item = Spree::StockItem.new(
69
+ variant_id: @variant.id,
70
+ stock_location_id: context.params[:stock_location_id],
71
+ count_on_hand: context.params[:count_on_hand]
72
+ )
73
+ return if @stock_item.save
74
+
75
+ context.fail!(message: @stock_item.errors.full_messages.join(', '))
76
+ end
77
+
78
+ def ticket_params
79
+ {
80
+ name: context.params[:name],
81
+ price: context.params[:price],
82
+ compare_at_price: context.params[:compare_at_price],
83
+ max_order_quantity: context.params[:max_order_quantity],
84
+ available_on: context.params[:available_on],
85
+ discontinue_on: context.params[:discontinue_on],
86
+ description: context.params[:description],
87
+ product_type: context.params[:product_type],
88
+ vendor_id: context.params[:vendor_id],
89
+ status: context.params[:status],
90
+ shipping_category_id: context.params[:shipping_category_id],
91
+ option_type_ids: context.params[:option_type_ids]
92
+ }
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,19 @@
1
+ module SpreeCmCommissioner
2
+ class EventLineItemsDateSyncer < BaseInteractor
3
+ delegate :event, to: :context
4
+
5
+ def call
6
+ return unless event.event?
7
+ return unless event.depth == 1
8
+
9
+ event.event_line_items.includes(:variant).find_each do |line_item|
10
+ new_from = line_item.variant.start_date_time || event.from_date
11
+ new_to = line_item.variant.end_date_time || event.to_date
12
+
13
+ # Update could be failed here if case line item does not allowed to change or no longer available.
14
+ # We can ignore this line item in this case.
15
+ line_item.update(from_date: new_from, to_date: new_to) if line_item.from_date != new_from || line_item.to_date != new_to
16
+ end
17
+ end
18
+ end
19
+ end
@@ -3,7 +3,7 @@ module SpreeCmCommissioner
3
3
  include Interactor
4
4
 
5
5
  def call
6
- user = Spree.user_class.find_user_by_login(context.login) if context.login.present?
6
+ user = Spree.user_class.find_user_by_login(context.login, context.tenant_id) if context.login.present?
7
7
 
8
8
  context.fail!(message: I18n.t('account_checker.verify.already_exist', login: context.login)) if user.present?
9
9
  end
@@ -0,0 +1,31 @@
1
+ module SpreeCmCommissioner
2
+ module OrderImporter
3
+ class MultiGuest < BaseInteractor
4
+ delegate :params, :quantity, to: :context
5
+
6
+ def call
7
+ return context.fail!(message: 'variant_id_is_required') if params[:variant_id].blank?
8
+ return context.fail!(message: 'email_or_phone_is_required') if params[:order_email].blank? && params[:order_phone_number].blank?
9
+
10
+ context.order = construct_order
11
+ context.fail!(message: context.order.errors.full_messages.to_sentence) unless context.order.save
12
+ end
13
+
14
+ def construct_order
15
+ Spree::Order.new(
16
+ email: params[:order_email],
17
+ phone_number: params[:order_phone_number],
18
+ completed_at: Date.current,
19
+ state: 'complete',
20
+ payment_state: 'paid',
21
+ line_items_attributes: [
22
+ {
23
+ quantity: quantity.to_i,
24
+ variant_id: params[:variant_id]
25
+ }
26
+ ]
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ module SpreeCmCommissioner
2
+ class OrganizersTransactionalEmailNotifier < BaseInteractor
3
+ delegate :event_id, to: :context
4
+
5
+ def call
6
+ return if organizers.empty?
7
+
8
+ organizers.each do |organizer|
9
+ SpreeCmCommissioner::TransactionalEmailSender.call(
10
+ event_id: event_id,
11
+ recipient_id: organizer.id,
12
+ type: 'organizer'
13
+ )
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def event
20
+ @event ||= Spree::Taxon.event.find(event_id)
21
+ end
22
+
23
+ def organizers
24
+ @organizers ||= event.vendors.first&.users || []
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,7 @@
1
1
  module SpreeCmCommissioner
2
2
  class PinCodeSender < BaseInteractor
3
+ include SpreeCmCommissioner::PinCodeSenderHelper
4
+
3
5
  def call
4
6
  context.fail!(message: I18n.t('pincode_sender.pincode.blank')) if context.pin_code.nil?
5
7
 
@@ -15,10 +17,8 @@ module SpreeCmCommissioner
15
17
  private
16
18
 
17
19
  def send_sms
18
- from = context.tenant || Spree::Store.default
19
-
20
20
  options = {
21
- from: from,
21
+ from: sender_name(context.tenant),
22
22
  to: context.pin_code.contact,
23
23
  body: I18n.t('pincode_sender.sms.body', code: context.pin_code.code, readable_type: context.pin_code.readable_type)
24
24
  }
@@ -0,0 +1,15 @@
1
+ module SpreeCmCommissioner
2
+ class ProductEventIdToChildrenSyncer < BaseInteractor
3
+ delegate :product, to: :context
4
+
5
+ # skip validation & callback like checking stock, etc.
6
+ def call
7
+ event_id = product.event_id
8
+
9
+ # rubocop:disable Rails/SkipsModelValidations
10
+ product.line_items.find_each { |line_item| line_item.update_columns(event_id: event_id) }
11
+ product.guests.find_each { |guest| guest.update_columns(event_id: event_id) }
12
+ # rubocop:enable Rails/SkipsModelValidations
13
+ end
14
+ end
15
+ end
@@ -11,6 +11,8 @@ module SpreeCmCommissioner
11
11
  context.sms_log.error = e.message
12
12
  context.sms_log.save
13
13
  context.fail!(message: e.message)
14
+
15
+ log_error_to_telegram(e.message)
14
16
  end
15
17
  end
16
18
 
@@ -57,5 +59,17 @@ module SpreeCmCommissioner
57
59
  result = SpreeCmCommissioner::InternationalMobileFormatter.call(phone_number: number)
58
60
  result.formatted_phone_number
59
61
  end
62
+
63
+ def log_error_to_telegram(message)
64
+ return unless ENV['PIN_CODE_DEBUG_NOTIFIY_TELEGRAM_ENABLE'] == 'yes'
65
+
66
+ options = {
67
+ pin_code_id: context.pin_code.id,
68
+ tenant_id: context.tenant&.id,
69
+ error_message: message
70
+ }
71
+
72
+ SpreeCmCommissioner::TelegramDebugPinCodeSenderJob.perform_later(options)
73
+ end
60
74
  end
61
75
  end
@@ -1,6 +1,6 @@
1
1
  module SpreeCmCommissioner
2
2
  class TelegramDebugPinCodeSender < BaseInteractor
3
- delegate :pin_code, :name, to: :context
3
+ delegate :pin_code, :name, :error_message, to: :context
4
4
 
5
5
  def call
6
6
  telegram_client.send_message(
@@ -16,6 +16,7 @@ module SpreeCmCommissioner
16
16
  text << "<b>From: #{name}</b>"
17
17
  text << "<b>PIN CODE sent to #{pin_code.contact}</b>"
18
18
  text << "<code>#{pin_code.code}</code> is your #{pin_code.readable_type}"
19
+ text << "⚠️ Error: <code>#{error_message}<code> ⚠️" if error_message.present?
19
20
 
20
21
  text.compact.join("\n")
21
22
  end
@@ -0,0 +1,50 @@
1
+ module SpreeCmCommissioner
2
+ class TransactionalEmailSender < BaseInteractor
3
+ delegate :event_id, :recipient_id, :type, to: :context
4
+
5
+ def call
6
+ context.fail!(message: 'Email already sent') if email_already_sent?
7
+ context.fail!(message: 'Banner Image required') unless banner_image_attached?
8
+
9
+ send_email
10
+ update_sent_at
11
+ end
12
+
13
+ private
14
+
15
+ def transactional_email
16
+ @transactional_email ||= SpreeCmCommissioner::TransactionalEmail.find_or_initialize_by(
17
+ taxon_id: event_id,
18
+ recipient_id: recipient_id,
19
+ recipient_type: 'Spree::User'
20
+ )
21
+ end
22
+
23
+ def update_sent_at
24
+ transactional_email.update!(sent_at: Time.current)
25
+ end
26
+
27
+ def email_already_sent?
28
+ transactional_email.sent_at.present?
29
+ end
30
+
31
+ def banner_image_attached?
32
+ event.home_banner&.attachment&.attached?
33
+ end
34
+
35
+ def event
36
+ @event ||= Spree::Taxon.event.find(event_id)
37
+ end
38
+
39
+ def send_email
40
+ case type.to_sym
41
+ when :organizer
42
+ SpreeCmCommissioner::EventTransactionalMailer.send_to_organizer(event_id, recipient_id).deliver_later
43
+ when :participant
44
+ SpreeCmCommissioner::EventTransactionalMailer.send_to_participant(event_id, recipient_id).deliver_later
45
+ else
46
+ context.fail!(message: "Unknown transactional email type: #{type}")
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,6 +1,7 @@
1
1
  module SpreeCmCommissioner
2
2
  class UserForgottenPasswordUpdater < BaseInteractor
3
3
  # :email, :phone_number, :country_code :pin_code, :pin_code_token, :password, :password_confirmation
4
+ delegate :tenant_id, to: :context
4
5
 
5
6
  def call
6
7
  find_user_by_login!
@@ -19,7 +20,7 @@ module SpreeCmCommissioner
19
20
  def find_user_by_login!
20
21
  login = phone_number || email
21
22
 
22
- context.user = Spree.user_class.find_user_by_login(login)
23
+ context.user = Spree.user_class.find_user_by_login(login, tenant_id)
23
24
  context.fail!(message: I18n.t('account_checker.verify.not_exist', login: login)) if context.user.blank?
24
25
  end
25
26
 
@@ -1,13 +1,15 @@
1
1
  module SpreeCmCommissioner
2
2
  class UserPasswordAuthenticator < BaseInteractor
3
- delegate :login, :password, to: :context
3
+ delegate :login, :password, :tenant_id, to: :context
4
4
 
5
5
  def call
6
- context.user = Spree.user_class.find_user_by_login(login)
6
+ context.user = Spree.user_class.find_user_by_login(login, tenant_id)
7
7
  context.fail!(message: I18n.t('authenticator.incorrect_login')) if context.user.nil?
8
+
8
9
  if spree_confirmable? && active_for_authentication? && !validate_password(user)
9
10
  context.fail!(message: I18n.t('authenticator.incorrect_password'))
10
11
  end
12
+
11
13
  context.fail!(message: I18n.t('authenticator.incorrect_password')) unless validate_password(context.user)
12
14
  context.fail!(message: 'account_temporarily_deleted') if context.user.soft_deleted?
13
15
  end
@@ -2,7 +2,7 @@ require 'google/cloud/firestore'
2
2
 
3
3
  module SpreeCmCommissioner
4
4
  class WaitingRoomSessionCreator < BaseInteractor
5
- delegate :remote_ip, :waiting_guest_firebase_doc_id, :page_path, to: :context
5
+ delegate :remote_ip, :waiting_guest_firebase_doc_id, :page_path, :tenant_id, to: :context
6
6
 
7
7
  def call
8
8
  return context.fail!(message: 'must_provide_waiting_guest_firebase_doc_id') if waiting_guest_firebase_doc_id.blank?
@@ -37,24 +37,17 @@ module SpreeCmCommissioner
37
37
  jwt_token: context.jwt_token,
38
38
  expired_at: expired_at,
39
39
  remote_ip: remote_ip,
40
- page_path: page_path
40
+ page_path: page_path,
41
+ tenant_id: tenant_id
41
42
  )
42
43
  context.room_session.save!
43
44
  end
44
45
 
45
46
  def log_to_firebase
46
- current_date = Time.zone.now.strftime('%Y-%m-%d')
47
-
48
- document = firestore.col('waiting_guests')
49
- .doc(current_date)
50
- .col('records')
51
- .doc(waiting_guest_firebase_doc_id)
52
-
53
- data = document.get.data.dup
54
- data[:entered_room_at] = Time.zone.now
55
- data[:page_path] = page_path
56
-
57
- document.update(data)
47
+ SpreeCmCommissioner::WaitingRoomSessionFirebaseLoggerJob.perform_later(
48
+ room_session_id: context.room_session.id,
49
+ waiting_guest_firebase_doc_id: waiting_guest_firebase_doc_id
50
+ )
58
51
  end
59
52
 
60
53
  def call_other_waiting_guests
@@ -0,0 +1,30 @@
1
+ require 'google/cloud/firestore'
2
+
3
+ module SpreeCmCommissioner
4
+ class WaitingRoomSessionFirebaseLogger < BaseInteractor
5
+ delegate :room_session, :waiting_guest_firebase_doc_id, to: :context
6
+
7
+ def call
8
+ current_date = room_session.created_at.strftime('%Y-%m-%d')
9
+ document = firestore.col('waiting_guests')
10
+ .doc(current_date)
11
+ .col('records')
12
+ .doc(waiting_guest_firebase_doc_id)
13
+
14
+ data = document.get.data.dup
15
+ data[:entered_room_at] = room_session.created_at
16
+ data[:page_path] = room_session.page_path
17
+ data[:tenant_id] = room_session.tenant_id
18
+
19
+ document.update(data)
20
+ end
21
+
22
+ def firestore
23
+ @firestore ||= Google::Cloud::Firestore.new(project_id: service_account[:project_id], credentials: service_account)
24
+ end
25
+
26
+ def service_account
27
+ Rails.application.credentials.cloud_firestore_service_account
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ # Job to add set initial event_id to existing product, line_item, guest that miss the event_id.
2
+ module SpreeCmCommissioner
3
+ class EnsureEventForProductLineItemGuestsJob < ApplicationJob
4
+ def perform
5
+ Spree::Taxon.event.includes(:children_products).find_each do |event|
6
+ event.children_products.where('event_id IS NULL OR event_id != ?', event.id).find_each do |product|
7
+ product.update_columns(event_id: event.id) # rubocop:disable Rails/SkipsModelValidations
8
+ ::SpreeCmCommissioner::ProductEventIdToChildrenSyncerJob.perform_later(product.id)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module SpreeCmCommissioner
2
+ class EventLineItemsDateSyncerJob < ApplicationJob
3
+ def perform(event_id)
4
+ event = Spree::Taxon.event.find(event_id)
5
+ SpreeCmCommissioner::EventLineItemsDateSyncer.call(event: event)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module SpreeCmCommissioner
2
+ class ProductEventIdToChildrenSyncerJob < ApplicationUniqueJob
3
+ def perform(product_id)
4
+ product = Spree::Product.find(product_id)
5
+ SpreeCmCommissioner::ProductEventIdToChildrenSyncer.call(product: product)
6
+ end
7
+ end
8
+ end
@@ -5,12 +5,14 @@ module SpreeCmCommissioner
5
5
  def perform(options)
6
6
  pin_code = SpreeCmCommissioner::PinCode.find(options[:pin_code_id])
7
7
  tenant = SpreeCmCommissioner::Tenant.find_by(id: options[:tenant_id])
8
+ error_message = options[:error_message]
8
9
 
9
10
  name = sender_name(tenant)
10
11
 
11
12
  SpreeCmCommissioner::TelegramDebugPinCodeSender.call(
12
13
  pin_code: pin_code,
13
- name: name
14
+ name: name,
15
+ error_message: error_message
14
16
  )
15
17
  end
16
18
  end
@@ -0,0 +1,13 @@
1
+ module SpreeCmCommissioner
2
+ class WaitingRoomSessionFirebaseLoggerJob < ApplicationJob
3
+ def perform(options)
4
+ room_session = SpreeCmCommissioner::WaitingRoomSession.find(options[:room_session_id])
5
+ waiting_guest_firebase_doc_id = options[:waiting_guest_firebase_doc_id]
6
+
7
+ SpreeCmCommissioner::WaitingRoomSessionFirebaseLogger.call(
8
+ room_session: room_session,
9
+ waiting_guest_firebase_doc_id: waiting_guest_firebase_doc_id
10
+ )
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module SpreeCmCommissioner
2
+ class EventTransactionalMailer < Spree::BaseMailer
3
+ layout 'spree_cm_commissioner/layouts/event_transactional_mailer'
4
+
5
+ def send_to_organizer(event_id, organizer_id)
6
+ @organizer = Spree::User.find(organizer_id)
7
+ @event = Spree::Taxon.find(event_id)
8
+
9
+ subject = t('mail.event_engagement_mailer.organizer.subject', event_name: @event.name)
10
+
11
+ mail(from: from_address, to: @organizer.email, subject: subject)
12
+ end
13
+
14
+ def send_to_participant(event_id, participant_id)
15
+ @participant = Spree::User.find(participant_id)
16
+ @event = Spree::Taxon.find(event_id)
17
+
18
+ subject = I18n.t('mail.event_engagement_mailer.participant.subject', event_name: @event.name)
19
+
20
+ mail(from: from_address, to: @participant.email, subject: subject)
21
+ end
22
+ end
23
+ end